简体   繁体   English

对 TensorFlow 服务的 RaggedTensor 请求失败

[英]RaggedTensor request to TensorFlow serving fails

I've created a TensorFlow model that uses RaggedTensors.我创建了一个使用 RaggedTensors 的 TensorFlow model。 Model works fine and when calling model.predict and I get the expected results. Model 工作正常,当调用model.predict时,我得到了预期的结果。

input = tf.ragged.constant([[[-0.9984272718429565, -0.9422321319580078, -0.27657580375671387, -3.185823678970337, -0.6360141634941101, -1.6579184532165527, -1.9000954627990723, -0.49169546365737915, -0.6758883595466614, -0.6677696704864502, -0.532067060470581], 
                                [-0.9984272718429565, -0.9421600103378296, 2.2048349380493164, -1.273996114730835, -0.6360141634941101, -1.5917999744415283, 0.6147914528846741, -0.49169546365737915, -0.6673409938812256, -0.6583622694015503, -0.5273991227149963], 
                                [-0.9984272718429565, -0.942145586013794, 2.48842453956604, -1.6836735010147095, -0.6360141634941101, -1.5785763263702393, -1.900200605392456, -0.49169546365737915, -0.6656315326690674, -0.6583622694015503, -0.5273991227149963], 
]])
model.predict(input)

>> array([[0.5138151 , 0.3277698 , 0.26122513]], dtype=float32)

I've deployed the model to a TensorFlow serving server and using the following code to invoke:我已将 model 部署到 TensorFlow 服务服务器,并使用以下代码调用:

import json
import requests
headers = {"content-type": "application/json"}
data = json.dumps({"instances":[
    [-1.3523329846758267, ... more data ],
    [-1.3523329846758267, ... more data ],
    [-1.3523329846758267, ... more data ],
    [-1.3523329846758267, ... more data ,
    [-1.3523329846758267, ... more data ],
    [-1.3523329846758267, ... more data ],
    [-1.3523329846758267, ... more data ],
    [-1.3523329846758267, ... more data })
json_response = requests.post('http://localhost:8501/v1/models/fashion_model:predict', data=data, headers=headers)
predictions = json.loads(json_response.text)

But then I get the following error:但后来我收到以下错误:

"instances is a plain list, but expecting list of objects as multiple input tensors required as per tensorinfo_map"

My model description:我的 model 描述:

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['__saved_model_init_op']:
  The given SavedModel SignatureDef contains the following input(s):
  The given SavedModel SignatureDef contains the following output(s):
    outputs['__saved_model_init_op'] tensor_info:
        dtype: DT_INVALID
        shape: unknown_rank
        name: NoOp
  Method name is: 

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['args_0'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 11)
        name: serving_default_args_0:0
    inputs['args_0_1'] tensor_info:
        dtype: DT_INT64
        shape: (-1)
        name: serving_default_args_0_1:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['dense_2'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 3)
        name: StatefulPartitionedCall:0
  Method name is: tensorflow/serving/predict
WARNING: Logging before flag parsing goes to stderr.
W0124 09:33:16.365564 140189730998144 deprecation.py:506] From /usr/local/lib/python2.7/dist-packages/tensorflow_core/python/ops/resource_variable_ops.py:1786: calling __init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.

Defined Functions:
  Function Name: '__call__'
    Option #1
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None, 11]), tf.float32, 1, tf.int64)
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None, 11]), tf.float32, 1, tf.int64)
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None

  Function Name: '_default_save_signature'
    Option #1
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None, 11]), tf.float32, 1, tf.int64)

  Function Name: 'call_and_return_all_conditional_losses'
    Option #1
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None, 11]), tf.float32, 1, tf.int64)
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None, 11]), tf.float32, 1, tf.int64)
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None

What am I missing?我错过了什么?

Update: After inspecting saved_model_cli output, I suspect I should send the request as an object like below, but I'm not sure about the inputs...更新:检查saved_model_cli output 后,我怀疑我应该像下面这样将请求作为 object 发送,但我不确定输入...

{
  "instances": [
    {
      "args_0": nested-list ?,
      "args_0_1": ???
    }
  ]
}

Update2 A Colab to test this scenario, a link to download the model is included in the Colab. 更新 2用于测试此场景的Colab ,Colab 中包含下载 model 的链接。

Update 3:更新 3:

As suggested by @Niteya Shah I've called the API with:正如@Niteya Shah 所建议的那样,我将 API 称为:

data = json.dumps({
 "inputs": {
   "args_0": [[-0.9984272718429565, -0.9422321319580078, -0.27657580375671387, -3.185823678970337, -0.6360141634941101, -1.6579184532165527, -1.9000954627990723, -0.49169546365737915, -0.6758883595466614, -0.6677696704864502, -0.532067060470581], 
              [-0.9984272718429565, -0.9421600103378296, 2.2048349380493164, -1.273996114730835, -0.6360141634941101, -1.5917999744415283, 0.6147914528846741, -0.49169546365737915, -0.6673409938812256, -0.6583622694015503, -0.5273991227149963]],
   "args_0_1": [1, 2]  #Please Check what inputs come here ?
  }
})

And got the results (Finally:):并得到了结果(最后:):

{'outputs': [[0.466771603, 0.455221593, 0.581544757]]}

Then called the model with the same data like so:然后使用相同的数据调用 model,如下所示:

import numpy as np
input = tf.ragged.constant([[
                            [-0.9984272718429565, -0.9422321319580078, -0.27657580375671387, -3.185823678970337, -0.6360141634941101, -1.6579184532165527, -1.9000954627990723, -0.49169546365737915, -0.6758883595466614, -0.6677696704864502, -0.532067060470581], 
                            [-0.9984272718429565, -0.9421600103378296, 2.2048349380493164, -1.273996114730835, -0.6360141634941101, -1.5917999744415283, 0.6147914528846741, -0.49169546365737915, -0.6673409938812256, -0.6583622694015503, -0.5273991227149963]
]])
model.predict(input)

And got different results:并得到了不同的结果:

array([[0.4817084 , 0.3649785 , 0.01603118]], dtype=float32)

I guess I'm still not there.我想我还是不在。

https://www.tensorflow.org/tfx/serving/api_rest#predict_api https://www.tensorflow.org/tfx/serving/api_rest#predict_api

I think that you need to use a columnar format as recommended in the REST API instead of the row format because the dimensions of your 0th input do not match.我认为您需要使用 REST API 中推荐的列格式而不是行格式,因为您的第 0 个输入的尺寸不匹配。 This means that instead of instances you will have to use inputs.这意味着您将不得不使用输入而不是实例。 Since you also have multiple inputs, you will have to also mention that as a named input.由于您还有多个输入,因此您还必须将其作为命名输入提及。

A sample data request could look like this示例数据请求可能如下所示

data = json.dumps({
 "inputs": {
   "args_0": [[-0.9984272718429565, -0.9422321319580078, -0.27657580375671387, -3.185823678970337, -0.6360141634941101, -1.6579184532165527, -1.9000954627990723, -0.49169546365737915, -0.6758883595466614, -0.6677696704864502, -0.532067060470581], 
              [-0.9984272718429565, -0.9421600103378296, 2.2048349380493164, -1.273996114730835, -0.6360141634941101, -1.5917999744415283, 0.6147914528846741, -0.49169546365737915, -0.6673409938812256, -0.6583622694015503, -0.5273991227149963]],
   "args_0_1": [10, 11]  #Substitute this with the correct row partition values. 
  }
})

Edit:编辑:

I read about Ragged tensors from here and it seems that the second input may be the row partitions.我从这里读到了 Ragged 张量,似乎第二个输入可能是行分区。 I couldn't find it in the documentation about what row partition style is normally used so I am using the row lengths method.我在文档中找不到关于通常使用哪种行分区样式的信息,所以我使用的是行长度方法。 Luckily TensorFlow ragged provides methods that do this for us.幸运的是 TensorFlow ragged 提供了为我们执行此操作的方法 Use the values and row_splits properties to access them.使用valuesrow_splits属性来访问它们。 That should work.那应该行得通。

Others may benefit from this, as it took me a while to stitch together:其他人可能会从中受益,因为我花了一段时间才缝合在一起:

  1. Training a toy LSTM model on ragged tensors.在不规则张量上训练玩具 LSTM model。
  2. Loading it into TensorFlow Serving.将其加载到 TensorFlow 服务中。
  3. Making a prediction request with a serielized ragged tensor.使用序列化的不规则张量发出预测请求。

If anyone knows how to rename "args_0" and "args_0_1", please add.如果有人知道如何重命名“args_0”和“args_0_1”,请添加。 Relevant Git Issue: https://github.com/tensorflow/tensorflow/issues/37226相关 Git 问题: https://github.com/tensorflow/tensorflow/issues/37226

Build & Save Model构建和保存 Model

TensorFlow version: 2.9.1 Python version: 3.8.12 TensorFlow 版本:2.9.1 Python 版本:3.8.12

# Task: predict whether each sentence is a question or not.
sentences = tf.constant(
    ['What makes you think she is a witch?',
     'She turned me into a newt.',
     'A newt?',
     'Well, I got better.'])
is_question = tf.constant([True, False, True, False])

# Preprocess the input strings.
hash_buckets = 1000
words = tf.strings.split(sentences, ' ')
hashed_words = tf.strings.to_hash_bucket_fast(words, hash_buckets)


# Build the Keras model.
keras_model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=[None], dtype=tf.int64, ragged=True),
    tf.keras.layers.Embedding(hash_buckets, 16),
    tf.keras.layers.LSTM(32, use_bias=False),
    tf.keras.layers.Dense(32),
    tf.keras.layers.Activation(tf.nn.relu),
    tf.keras.layers.Dense(1)
])

keras_model.compile(loss='binary_crossentropy', optimizer='rmsprop')
keras_model.fit(hashed_words, is_question, epochs=5)

print(keras_model.predict(hashed_words))

keras_module_path = "/home/ec2-user/SageMaker/keras-toy-lstm/1"
tf.keras.Model.save(keras_model, keras_module_path)

Load & Infer from Model从 Model 加载和推断

Load model into TensorFlow serving container将 model 装入 TensorFlow 服务容器

docker run -t --rm -p 8501:8501 -v "/home/ec2-user/SageMaker/keras-toy-lstm/:/models/keras-model" -e MODEL_NAME=keras-model  tensorflow/serving 
import requests
import json 

payload = {"args_0": [940, 203, 668, 638], 
            "args_0_1": [0, 4]}
headers = {"content-type": "application/json"}
data = json.dumps({"inputs":payload})

r = requests.post('http://localhost:8501/v1/models/keras-model:predict', data=data, headers=headers)
r.text

SavedModelCLI Output已保存模型CLI Output

(tensorflow2_p38) sh-4.2$ saved_model_cli show --dir /tmp/tmpgp0loz1v/ --all

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['__saved_model_init_op']:
  The given SavedModel SignatureDef contains the following input(s):
  The given SavedModel SignatureDef contains the following output(s):
    outputs['__saved_model_init_op'] tensor_info:
        dtype: DT_INVALID
        shape: unknown_rank
        name: NoOp
  Method name is: 

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['args_0'] tensor_info:
        dtype: DT_INT64
        shape: (-1)
        name: serving_default_args_0:0
    inputs['args_0_1'] tensor_info:
        dtype: DT_INT64
        shape: (-1)
        name: serving_default_args_0_1:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['dense_1'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: StatefulPartitionedCall:0
  Method name is: tensorflow/serving/predict

Concrete Functions:
  Function Name: '__call__'
    Option #1
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None]), tf.int64, 1, tf.int64)
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None]), tf.int64, 1, tf.int64)
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None

  Function Name: '_default_save_signature'
    Option #1
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None]), tf.int64, 1, tf.int64)

  Function Name: 'call_and_return_all_conditional_losses'
    Option #1
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None]), tf.int64, 1, tf.int64)
        Argument #2
          DType: bool
          Value: True
        Argument #3
          DType: NoneType
          Value: None
    Option #2
      Callable with:
        Argument #1
          DType: RaggedTensorSpec
          Value: RaggedTensorSpec(TensorShape([None, None]), tf.int64, 1, tf.int64)
        Argument #2
          DType: bool
          Value: False
        Argument #3
          DType: NoneType
          Value: None

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM