如何将图形导出到Tensorflow Serving以使输入为b64?

3
我有一个Keras图,其中包含一个形状为(?,224,224,3)的float32张量,我想将其导出到Tensorflow Serving,以便使用RESTful进行预测。问题是我不能输入张量,但可以输入编码的b64字符串,因为这是REST API的限制。这意味着在导出图形时,输入需要是一个字符串,需要解码。
如何“注入”新输入以转换为旧张量,而不重新训练图形本身?我已尝试过几个例子[1][2]
我目前有以下代码用于导出:
image = tf.placeholder(dtype=tf.string, shape=[None], name='source')


signature = predict_signature_def(inputs={'image_bytes': image},
                                 outputs={'output': model.output})

我需要找到一种将图像转换为模型输入的方法,或者一种将模型输出与图像连接的方法。
非常感谢您的帮助!
2个回答

2
您可以使用 tf.decode_base64
image = tf.placeholder(dtype=tf.string, shape=[None], name='source')
image_b64decoded = tf.decode_base64(image)
signature = predict_signature_def(inputs={'image_bytes': image_b64decoded},
                                 outputs={'output': model.output})

编辑:

如果您需要使用tf.image.decode_image,您可以使用tf.map_fn使其能够处理多个输入:

image = tf.placeholder(dtype=tf.string, shape=[None], name='source')
image_b64decoded = tf.decode_base64(image)
image_decoded = tf.map_fn(tf.image.decode_image, image_b64decoded, dtype=tf.uint8)

只要图像都具有相同的尺寸,这就可以工作。但是,结果是一个完全未知形状的张量,因为tf.image.decode_image可以根据图像类型输出不同数量的维度。您可以对其进行重塑或使用另一个tf.image.decode_*调用,以便至少在张量中具有已知数量的维度。

我不确定你的模型具体是怎样的,所以我假设解码后的base64数据是你需要的输入。如果你需要图像解码,你也可以使用 tf.image.decode_image 或相关函数,或者如果你需要重塑形状,只需使用 tf.reshape 即可。 - jdehesa
tf.image.decode_image 是我正在寻找的内容,但不幸的是,它只能在秩为0的形状上操作... - user3337758
缺少的一步是将这个结果连接到模型的初始float32张量上。我该怎么做呢? - user3337758
@user3337758 所以你的意思是你已经有了一个图表,现在想要在它之前进行一些预处理?不幸的是,这并不容易,最好一开始就有额外的第一步,并在训练的某个时候提供值(您可以在图表中的任何位置提供值,而不仅仅是占位符)。如果您想编辑图表中的连接,可以使用Graph Editor模块,但这并不是非常直观的。 - jdehesa
感谢澄清。我会重新训练修改后的图形。 - user3337758
显示剩余3条评论

-1

创建一个export_model可能是一种更简单的方法。 tensorflow.org中的一个例子

  1. 使用float32、形状为(?, 224, 224, 3)的张量定义Keras图

model = ...

  1. 定义一个预处理b64图像的函数
def preprocess_input(base64_input_bytes):
    def decode_bytes(img_bytes):
        img = tf.image.decode_jpeg(img_bytes, channels=3)
        img = tf.image.resize(img, (224, 224))
        img = tf.image.convert_image_dtype(img, tf.float32)
        return img

    base64_input_bytes = tf.reshape(base64_input_bytes, (-1,))
    return tf.map_fn(lambda img_bytes:
                     decode_bytes(img_bytes),
                     elems=base64_input_bytes,                     
                     fn_output_signature=tf.float32)
  • 导出一个服务模型
serving_inputs = tf.keras.layers.Input(shape=(), dtype=tf.string, name='b64_input_bytes')
serving_x = tf.keras.layers.Lambda(preprocess_input, name='decode_image_bytes')(serving_inputs)
serving_x = model(serving_x)
serving_model = tf.keras.Model(serving_inputs, serving_x)
tf.saved_model.save(serving_model, serving_model_path)
  1. 服务
import requests
data = json.dumps({"signature_name": "serving_default", "instances": [{"b64_input_bytes": {"b64": b64str_1}}, {"b64_input_bytes": {"b64": b64str_2}}]})
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/{model_name}:predict', data=data, headers=headers)
predictions = json.loads(json_response.text)['predictions']

由于您有一个Lambda层,如果没有提及自定义函数“preprocess_input”,则无法调用导出的模型。如果您有其他选项可以保存传递给Lambda层的自定义函数的模型,请在此处提供。 - Ahmed
@Dr.Xavier 感谢您的评论。自定义函数(preprocess_input)在serving_x中提到,我们只需创建“新模型”,其中包括自定义函数和训练模型。(例如,训练模型->导出模型(处理,训练模型)) - reedcolab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接