keras 如何让Tensorflow Serving保持命名输出不变?

s4chpxco  于 2023-02-16  发布在  其他
关注(0)|答案(1)|浏览(164)

我想创建一个返回命名Tensor的模型签名(使用Keras),这就是我的意思。当模型部署到TF-Serving时,我希望它返回如下JSON:

{
    "predictions": [
        {
            "t3": 19,
            "t1": 76.975174,
            "t2": "cat3"
        },
        {
            "t3": 17,
            "t1": 77.7983246,
            "t2": "cat3"
        }
    ]
}

最重要的是t1t2t3,这是我命名的,如果我不命名,返回的JSON是这样的:

{
    "predictions": [
        {
            "output_0": 77.5714188,
            "output_1": "cat3",
            "output_2": 17
        },
        {
            "output_0": 80.7243729,
            "output_1": "cat4",
            "output_2": 17
        }
    ]
}

output_0output_1output_2是由某个组件自动生成的(不确定是哪一个,但我猜是TF或Keras)。
这是我目前掌握的情况:

from tensorflow.keras import layers

class OutputWithNames(layers.Layer):
    def __init__(self):
        super(OutputWithNames, self).__init__()

    def call(self, x):
        return {"t1": x[0], "t2": x[1], "t3": x[2]}

添加这个自定义层作为模型签名中的最后一个层,我得到了我所提到的内容:一个JSON对象,具有所需的属性名t1t2t3
使用saved_model_cli工具检查保存的模型,当生成的模型按预期工作时,我得到的输出签名如下:

The given SavedModel SignatureDef contains the following output(s):
  outputs['t1'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1)
      name: StatefulPartitionedCall_1:0
  outputs['t2'] tensor_info:
      dtype: DT_STRING
      shape: (-1)
      name: StatefulPartitionedCall_1:1
  outputs['t3'] tensor_info:
      dtype: DT_INT32
      shape: (-1)
      name: StatefulPartitionedCall_1:2

再说一次,到目前为止一切都很好。但是当我只有一个输出时:

from tensorflow.keras import layers

class OutputWithNames(layers.Layer):
    def __init__(self):
        super(OutputWithNames, self).__init__()

    def call(self, x):
        return {"t1": x}

注:在第一个代码块中,x是一个Tensor列表,因为我有多个输出。这就是为什么我可以/必须使用x[0]。但在第二个代码块中,x只是一个Tensor,因为只有一个输出。这就是为什么在x前面没有[]
这一次,TF-Serving将为我生成这个JSON:

{
    "predictions": [
        67.2723083,
        68.9468231
    ]
}

虽然这是我期待看到的:

{
    "predictions": [
        {
            "t1": 67.2723083
        },
        {
            "t1": 68.9468231
        }
    ]
}

下面是saved_model_cli工具返回的结果:

The given SavedModel SignatureDef contains the following output(s):
  outputs['t1'] tensor_info:
      dtype: DT_INT64
      shape: (-1)
      name: StatefulPartitionedCall:0

基本上,保存的模型已经为输出正确设置了名称,但是由于某种原因,TF-Serving将其剥离,并且它只返回值,而不是返回对象数组。
我的问题是,当只有一个输出时,如何强制TF-Serving返回一个对象列表?

tjrkku2a

tjrkku2a1#

答案是,如果不改变TF Serving的实现方式,就无法实现。TF Serving文档中指出:
...如果模型的输出只包含一个命名的Tensor,我们忽略名称和predictions键Map到标量或列表值的列表。如果模型输出多个命名的Tensor,我们输出对象的列表,类似于上面提到的行格式的请求。
如果您事先不知道要使用什么模型(以及预期输出),我的建议是:
1.解析返回的JSON predictions对象。
1.检查JSON数组是否包含值或对象。
1.如果它返回值,就知道所用模型的输出只包含一个命名Tensor,因此可以为"t1"单独制定任何想要使用的逻辑。
1.如果它返回对象,那么您就知道所用模型的输出包含多个命名Tensor,每个Tensor都具有您已经设置的名称(例如"t3""t1""t2")。
或者,如果您可以向输出添加一个额外的(任意的)Tensor,例如,通过定义一个特定的Predict SignatureDef,这可能会允许您强制TF Serving返回一个对象列表,但显然其中一个对象将是任意的,您可以忽略其名称。
预测SignatureDefs还允许您向输出添加可选的附加Tensor,您可以显式查询这些Tensor。假设除了下面的scores输出键之外,您还希望获取池层以用于调试或其他目的。在这种情况下,您只需添加一个具有类似pool的键和适当值的附加Tensor。

signature_def: {
  key  : "my_prediction_signature"
  value: {
    inputs: {
      key  : "images"
      value: {
        name: "x:0"
        dtype: ...
        tensor_shape: ...
      }
    }
    outputs: {
      key  : "scores"
      value: {
        name: "y:0"
        dtype: ...
        tensor_shape: ...
      }
    }
    method_name: "tensorflow/serving/predict"
  }
}

我对定义或实现这样的签名并不熟悉,但如果前面提到的基本解析方法在您的情况下不可行,希望这能提供另一个方向。

相关问题