【问题标题】:How do I export a graph to Tensorflow Serving so that the input is b64?如何将图形导出到 Tensorflow Serving 以使输入为 b64?
【发布时间】:2018-08-07 13:53:56
【问题描述】:

我有一个 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})

不知何故,我需要找到一种将图像转换为 model.input 的方法,或者一种让模型输出连接到图像的方法。

任何帮助将不胜感激!

【问题讨论】:

    标签: rest tensorflow keras base64 serving


    【解决方案1】:

    你可以使用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
    • tf.image.decode_image 将是我正在寻找的,但不幸的是,它只能对 0 级的形状进行操作...
    • 问题是我需要以 b64 字符串的形式输入,但图形架构被训练为具有浮点张量输入形状。这意味着输入也需要采用字符串的形式......
    • @user3337758 很抱歉,我不太了解这个问题。您可以在 TensorFlow 中获取 Base64 字符串列表,对其进行解码,将字节数组解码为图像,根据需要重新调整它们的形状,并在必要时更改图像数据类型(例如使用 tf.image.convert_image_dtype。我不确定是什么缺少的步骤。
    • 缺少的步骤是将这个结果连接到模型的初始 float32 张量。我该怎么做呢?
    【解决方案2】:

    创建 export_model 可能是一种更简单的方法。 One example in 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)
    
    1. 导出服务模型
    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 层的自定义函数保存模型有任何其他选择,请在此处提供。
    • @Dr.Xavier 感谢您的评论。 serving_x 中提到了自定义函数(preprocess_input),我们只是创建了一个“新模型”,其中包括自定义函数和训练模型。 (例如,训练模型 -> 导出模型(处理,训练模型))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-03
    • 2017-07-15
    • 2020-03-25
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多