【问题标题】:TensorFlow Serving for images as base64-encoded strings on Cloud ML EngineTensorFlow Serving 将图像作为 Cloud ML Engine 上的 base64 编码字符串
【发布时间】:2019-07-27 06:50:47
【问题描述】:

如何实现 TensorFlow Serving Input 功能,将图像作为 base64 编码字符串并在 Cloud ML Engine 上进行预测

我打算在本地训练后将模型部署到 Cloud Machine Learning (ML) Engine 上,但我不知道如何实现服务输入功能

此外,我试图避免使用 TensorFlow 低级 API,而只关注 TensorFlow 高级 API(TensorFlow Estimator)。下面的代码块是我正在处理的示例代码。


import numpy as np
import tensorflow as tf
import datetime
import os

# create model
from tensorflow.python.keras.applications.vgg16 import VGG16
from tensorflow.python.keras import models
from tensorflow.python.keras import layers

conv_base = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(150, 150, 3))

model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
conv_base.trainable = False
model.compile(loss='binary_crossentropy',
              optimizer=tf.keras.optimizers.RMSprop(lr=2e-5),
              metrics=['acc'])

dt = datetime.datetime.now()
datetime_now = dt.strftime("%y%m%d_%H%M%S")
model_dir = 'models/imageclassifier_'+datetime_now
model_dir = os.path.join(os.getcwd(), model_dir)
if not os.path.exists(model_dir):
    os.makedirs(model_dir)
print ("model_dir: ",model_dir)

est_imageclassifier = tf.keras.estimator.model_to_estimator(keras_model=model, model_dir=model_dir)

# input layer name
input_name = model.input_names[0]
input_name

此部分用于图像输入功能。

def imgs_input_fn(filenames, labels=None, perform_shuffle=False, repeat_count=1, batch_size=1):
    def _parse_function(filename, label):
        image_string = tf.read_file(filename)
        image = tf.image.decode_image(image_string, channels=3)
        image.set_shape([None, None, None])
        image = tf.image.resize_images(image, [150, 150])
        image = tf.subtract(image, 116.779) # Zero-center by mean pixel
        image.set_shape([150, 150, 3])
        image = tf.reverse(image, axis=[2]) # 'RGB'->'BGR'
        d = dict(zip([input_name], [image])), label
        return d
    if labels is None:
        labels = [0]*len(filenames)
    labels=np.array(labels)
    # Expand the shape of "labels" if necessary
    if len(labels.shape) == 1:
        labels = np.expand_dims(labels, axis=1)
    filenames = tf.constant(filenames)
    labels = tf.constant(labels)
    labels = tf.cast(labels, tf.float32)
    dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
    dataset = dataset.map(_parse_function)
    if perform_shuffle:
        # Randomizes input using a window of 256 elements (read into memory)
        dataset = dataset.shuffle(buffer_size=256)
    dataset = dataset.repeat(repeat_count)  # Repeats dataset this # times
    dataset = dataset.batch(batch_size)  # Batch size to use
    iterator = dataset.make_one_shot_iterator()
    batch_features, batch_labels = iterator.get_next()
    return batch_features, batch_labels

我想创建一个服务输入函数

  1. 将图像获取为 JSON 格式的 base64 编码字符串

  2. 将它们转换为张量并将大小减小到 (?, 150, 150, 3) 以进行预测

如下图,

def serving_input_receiver_fn():

''' CODE HERE!'''

return tf.estimator.export.ServingInputReceiver(feature_placeholders, feature_placeholders)

为了训练和评估模型,

train_spec = tf.estimator.TrainSpec(input_fn=lambda: imgs_input_fn(train_files,
                                                                   labels=train_labels,
                                                                   perform_shuffle=True,
                                                                   repeat_count=1,
                                                                   batch_size=20), 
                                    max_steps=500)

exporter = tf.estimator.LatestExporter('Servo', serving_input_receiver_fn)

eval_spec = tf.estimator.EvalSpec(input_fn=lambda: imgs_input_fn(val_files,
                                                                 labels=val_labels,
                                                                 perform_shuffle=False,
                                                                 batch_size=1),
                                 exporters=exporter)

tf.estimator.train_and_evaluate(est_imageclassifier, train_spec, eval_spec)

如果我理解正确,在 Cloud ML Engine 上获取预测的输入文件示例应该类似于

request.json

{"b64": "9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHJC...”}
{"b64": "9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHJC...”}

gcloud ml-engine predict --model MODEL_NAME  \
                --version MODEL_VERSION \
                --json-instances request.json

如果您一直读到这里并且有一些想法,请您建议我如何针对这种特殊情况实现服务输入功能。

非常感谢,


2nd Post - 更新我到目前为止所做的事情。

根据 sdcbr 的评论,下面是我的 serving_input_receiver_fn()。

对于_img_string_to_tensor()函数或者(prepare_image函数),我想我应该按照我训练模型的方式来做图像准备,你可以看到

imgs_input_fn() => _parse_function().

def serving_input_receiver_fn():
    def _img_string_to_tensor(image_string):
        image = tf.image.decode_image(image_string, channels=3)
        image.set_shape([None, None, None])
        image = tf.image.resize_images(image, [150, 150])
        image = tf.subtract(image, 116.779) # Zero-center by mean pixel
        image.set_shape([150, 150, 3])
        image = tf.reverse(image, axis=[2]) # 'RGB'->'BGR'
        return image

    input_ph = tf.placeholder(tf.string, shape=[None])

    images_tensor = tf.map_fn(_img_string_to_tensor, input_ph, back_prop=False, dtype=tf.float32)

    return tf.estimator.export.ServingInputReceiver({model.input_names[0]: images_tensor}, {'image_bytes': input_ph})

在我训练模型并将保存的模型部署到 Cloud ML Engine 之后。我的输入图像被准备成如下所示的格式。

{"image_bytes": {"b64": "YQ=="}}

但是我通过 gcloud 得到预测后发现了错误。

gcloud ml-engine predict --model model_1  \
               --version v1 \
               --json-instances request.json

{ "error": "预测失败:模型执行期间出错: AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"断言 失败:[无法将字节解码为 JPEG、PNG、GIF 或 BMP]\n\t [[{{节点 map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert}} = 断言[T=[DT_STRING],总结=3, _device=\"/job:localhost/replica:0/task:0/device:CPU:0\"](map/while/decode_image/cond_jpeg/cond_png/cond_gif/is_bmp, map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert/data_0)]]\")" }

我在 _img_string_to_tensor 函数中做错了吗?

您能否详细说明一下这个 tf.placeholder?

input_ph = tf.placeholder(tf.string, shape=[None])

对于您上面的代码,您使用 shape=[1],但我认为它应该是 shape=[None]。

【问题讨论】:

    标签: tensorflow tensorflow-serving google-cloud-ml tensorflow-estimator


    【解决方案1】:

    按照这些思路应该可以工作:

    def serving_input_receiver_fn():
        def prepare_image(image_str_tensor):
            image = tf.image.decode_image(image_str_tensor,
                                         channels=3)
            image = tf.image.resize_images(image, [150, 150])
            return image
    
        # Ensure model is batchable
        # https://stackoverflow.com/questions/52303403/
        input_ph = tf.placeholder(tf.string, shape=[None])
        images_tensor = tf.map_fn(
            prepare_image, input_ph, back_prop=False, dtype=tf.float32)
        return tf.estimator.export.ServingInputReceiver(
            {model.input_names[0]: images_tensor},
            {'image_bytes': input_ph})
    

    您可以在prepare_image 函数中添加额外的预处理。请注意,images_tensor 应该映射到应该接收输入的tf.kerasmodel 中的层名称。

    另请参阅thisthis 相关问题。

    【讨论】:

    • 非常感谢您的回复和建议。这对我来说意义重大。但是,我仍然陷入这个问题。我已经在“第二篇文章 - 更新我到目前为止所做的事情”下面发布了。
    • 我刚刚解决了这个问题!再次感谢您的回答。那是对的!我输入的 Json 错误。
    【解决方案2】:

    回答!

    根据 sdcbr 的评论,这是我正在寻找的正确答案,但我刚刚发现它为什么不起作用的问题。

    根据错误,

    { "error": "预测失败:模型执行期间出错: AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"断言 失败:[无法将字节解码为 JPEG、PNG、GIF 或 BMP]\n\t [[{{节点 map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert}} = 断言[T=[DT_STRING],总结=3, _device=\"/job:localhost/replica:0/task:0/device:CPU:0\"](map/while/decode_image/cond_jpeg/cond_png/cond_gif/is_bmp, map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert/data_0)]]\")" }

    这是因为 request.json 类似于

    {\"image_bytes\": {\"b64\": \"YQ==\"}}
    {\"image_bytes\": {\"b64\": \"YQ==\"}}
    .
    .
    

    应该是这样的

    {"image_bytes": {"b64": "YQ=="}}
    {"image_bytes": {"b64": "YQ=="}}
    .
    .
    

    在我清理并删除所有反斜杠后,它起作用了!

    附注这是您需要仔细检查的内容。如果你在 IPython notebook 上打印出来,它不会显示反斜杠。我必须在编辑器上打开它,然后我才能找到真正的问题。

    【讨论】:

    • 是什么导致了 \ 斜线,我们如何避免它们而不是纠正输出?
    猜你喜欢
    • 1970-01-01
    • 2017-11-03
    • 2015-08-20
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多