Мы создаем автоматизированный конвейер TFX на основе Airflow и основали нашу модель на Учебнике Keras . Мы сохраняем модель keras следующим образом:
model.save(fn_args.serving_model_dir, save_format='tf',
signatures=signatures,
)
Этот signatures
dict:
signatures = {
'serving_default':
_get_serve_tf_examples_fn(model, tf_transform_output) \
.get_concrete_function(
tf.TensorSpec(
shape=[None],
dtype=tf.string,
name='examples'
)
),
'prediction':
get_request_url_fn(model, tf_transform_output) \
.get_concrete_function(
tf.TensorSpec(
shape=[None],
dtype=tf.string,
name='prediction_examples'
)
),
}
_get_serve_tf_examples_fn служит цели обеспечения компонента оценщика TFX дополнительными тензорами, тензоры не используются в модели для целей оценки модели. Это как в руководстве Keras TFX выше:
def _get_serve_tf_examples_fn(model, tf_transform_output):
model.tft_layer = tf_transform_output.transform_features_layer()
@tf.function
def serve_tf_examples_fn(serialized_tf_examples):
feature_spec = tf_transform_output.raw_feature_spec()
feature_spec.pop(_LABEL_KEY)
parsed_features = tf.io.parse_example(serialized_tf_examples, feature_spec)
transformed_features = model.tft_layer(parsed_features)
transformed_features.pop(_transformed_name(_LABEL_KEY))
return model(transformed_features)
return serve_tf_examples_fn
Вышеупомянутая модель 'interface' принимает TF.Examples, как это необходимо для компонента TFX Evaluator (TFMA).
Однако для обслуживания TF мы хотим иметь возможность отправлять 1 необработанную строку - просто URL - в REST API предсказателя обслуживания TF и получать для нее прогнозируемую оценку. В настоящее время get_request_url_fn
это:
def get_request_url_fn(model, tf_transform_output):
model.tft_layer = tf_transform_output.transform_features_layer()
@tf.function
def serve_request_url_fn(request_url):
feature_spec = tf_transform_output.raw_feature_spec()
# Model requires just one of the features made available to other TFX components
# Throw away the rest and leave just 'request_url'
feature_spec = {'request_url': feature_spec['request_url']}
parsed_features = tf.io.parse_example(request_url, feature_spec)
transformed_features = model.tft_layer(parsed_features)
transformed_features.pop(_transformed_name(_LABEL_KEY))
return model(transformed_features)
return serve_request_url_fn
Однако этот подход по-прежнему требует ввода в форме TF.Example
. Это требует значительных накладных расходов со стороны клиента. А именно import tensorflow
. Однако этот код работает:
url = f'http://{server}:8501/v1/models/wrcv3:predict'
headers = {"content-type": "application/json"}
url_request = b'index'
example = tf.train.Example(
features=tf.train.Features(
feature={"request_url":
tf.train.Feature(bytes_list=tf.train.BytesList(value=[url_request]))
}
)
)
print(example)
data = {
"signature_name":"prediction",
"instances":[
{
"prediction_examples":{"b64": base64.b64encode(example.SerializeToString()).decode('utf-8')}
}
]
}
data = json.dumps(data)
print(data)
json_response = requests.post(url, data=data, headers=headers)
print(json_response.content)
print(json_response.json)
Возвращается в результате:
features {
feature {
key: "request_url"
value {
bytes_list {
value: "index"
}
}
}
}
{"signature_name": "prediction", "instances": [{"prediction_examples": {"b64": "ChoKGAoLcmVxdWVzdF91cmwSCQoHCgVpbmRleA=="}}]}
b'{\n "predictions": [[0.897708654]\n ]\n}'
<bound method Response.json of <Response [200]>>
Когда мы отправляем строку в кодировке base64 вместо TF.Example
, она явно терпит неудачу:
url = f'http://{server}:8501/v1/models/wrcv3:predict'
headers = {"content-type": "application/json"}
url_request = b'index.html'
data = {
"signature_name":"prediction",
"instances":[
{
"prediction_examples":{"b64": base64.b64encode(url_request).decode('UTF-8')}
}
]
}
data = json.dumps(data)
print(data)
json_response = requests.post(url, data=data, headers=headers)
print(json_response.content)
print(json_response.json)
возвращается:
{"signature_name": "prediction", "instances": [{"prediction_examples": {"b64": "aW5kZXguaHRtbA=="}}]}
b'{ "error": "Could not parse example input, value: \\\'index.html\\\'\\n\\t [[{{node ParseExample/ParseExampleV2}}]]" }'
<bound method Response.json of <Response [400]>>
Вопрос: как должна выглядеть подпись / подпись, чтобы принимать необработанные строки? Если не нравится get_request_url_fn
. Конечно, клиенту не нужно загружать TF только для того, чтобы сделать запрос ?
Сам веб-сайт TFX подробно описывает 3 protobufs для классификации / прогнозирования / регрессии здесь , но Это не интуитивно (для меня), как использовать эти 3 protobufs для создания нужного нам сопоставления.
Заранее глубокая благодарность.