TensorFlow服务REST API的正确负载

时间:2018-08-09 22:18:51

标签: rest tensorflow keras tensorflow-serving tensorflow-estimator

我已将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结构中格式化?

3 个答案:

答案 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]))
                )
            )