我已将Keras模型转换为Tensorflow估计器,将Tensorflow Transform添加到图形中,然后导出该模型以进行服务。
当我检查模型签名时,我可以看到以下信息:
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['examples'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: input_example_tensor:0
The given SavedModel SignatureDef contains the following output(s):
outputs['specialities'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 154)
name: specialities/Softmax:0
Method name is: tensorflow/serving/predict
我使用tf.estimator.export.build_parsing_serving_input_receiver_fn
转换了功能规范,因此签名中输入节点的名称为example
。我的模型中输入节点的名称为procedures
。
然后我使用saved_model_cli
手动测试导出的模型,一切看起来都很好(我得到了概率列表)
!saved_model_cli run --dir=/model_dir/1533849825
--tag_set serve
--signature_def serving_default
--input_examples 'examples=[{"procedures": ["99214,17000,17000,13121,99203"]}]'
现在,我将此模型加载到TF服务中,模型服务器可以正常启动。
当我请求使用下面的json有效负载(应用程序/ json)进行模型预测时,出现以下错误:
{
"signature_name":"serving_default",
"instances":[
{
"examples":["99214,17000,17000,13121,99203"]
}
]
}
错误:
"error": "Expected serialized to be a vector, got shape: [1,1]
不同的有效负载结构会导致此错误
{
"signature_name":"serving_default",
"examples":[
{
"procedure":["99214,17000,17000,13121,99203"]
}
]
}
错误:
"error": "JSON Value: {\n \"signature_name\": \"serving_default\",\n
\"examples\": [\n {\n \"procedures\":
["99214,17000,17000,13121,99203"]]\n }\n ]\n} not formatted
correctly. Expecting object with \'instances\' key and a list/array as the value."
在这种预测情况下,TensorFlow服务请求的正确有效载荷格式是什么?
有效载荷是否需要在tf.Example结构中格式化?
答案 0 :(得分:1)
您可以尝试使用build_raw_serving_input_receiver_fn()导出模型吗?并在JSON预测请求中将原始张量(procedure
)传递为:
{
"signature_name": "serving_default",
"instances": [
{
"procedure": ["99214,17000,17000,13121,99203"]
}
]
}
假设您使用默认的服务签名名称和单个命名输入,则可以将请求缩短为:
{
"instances": ["99214,17000,17000,13121,99203"]
}
关于您现有的代码,使用build_parsing_serving_input_receiver_fn()
导出模型需要将序列化的tf.Example
字符串Blob作为输入。 saved_model_cli
工具(使用python并可以使用protos)可以为您完成序列化,因此效果很好。在使用Web / REST API时进行原型序列化会很麻烦(需要protobuf库),并且对您的使用而言是一个过大的杀伤力。
答案 1 :(得分:0)
我在这里用Estimator
api举例,希望它可以帮助遇到类似问题的人。
要使用SavedModel
导出Estimator
,则需要input_receiver_fn
才能在投放时接受输入。我的应用程序中的input_receiver_fn
如下:
def _serving_input_receiver_fn():
serialized_tf_sample = tf.placeholder(dtype=tf.string,
shape=None, name='input_example_tensor')
receiver_tensors = {'example': serialized_tf_sample}
# example_proto: {'feature_name': tf.VarLenFeature(tf.int64),}
features = tf.parse_example(serialized_tf_sample, example_proto)
return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)
您可以将以下代码放在train_and_evaluate
下,以导出SavedModel
estimator.export_savedmodel(model_export_path, _serving_input_receiver_fn)
要提供模型服务,您可以提取tensorflow/serving
码头工人图像,可以参考https://www.tensorflow.org/serving/docker以获得帮助。 (我建议您使用devel标签提取图像,因为它最好进行调试)
只需运行以下命令即可开始投放
/usr/local/bin/tensorflow_model_server --port=8500 --rest_api_port=8501 --model_name=my_model --model_base_path my_model_path
客户端代码很简单,但是应该注意。因为序列化的示例应使用base64编码并添加一个b64
键。
import requests
resp = requests.post('http://host:8501/v1/models/my_model:predict', json={
'instances': [
{'example': {'b64':base64.b64encode(single_example.SerializeToString())}}
]})
resp.json()
如有任何疑问,请在下面发表评论。
答案 2 :(得分:0)
正如上面的netfs所述,将build_parsing_serving_input_receiver_fn()
用于REST API很麻烦。要详细说明它的麻烦之处,以下是您原始问题的答案,POST正文应为:
{
"signature_name":"serving_default",
"instances":[
{
"examples":{"b64": base64.b64encode(example.SerializeToString()).decode('utf-8')}
}
]
}
example
是一个tf.Example原型对象。例如:
example = tf.train.Example(
features=tf.train.Features(
feature={"procedures":
tf.train.Feature(int64_list=tf.train.Int64List(value=[99214,17000,17000,13121,99203]))
)
)