TensorFlow在Cloud ML Engine上为图像提供base64编码的字符串

问题描述 投票:1回答:2

如何将图像的TensorFlow服务输入函数实现为base64编码的字符串,并在Cloud ML Engine上进行预测

我计划在云端机器学习(ML)引擎上进行部署后对其进行部署,但我不知道如何实现服务输入功能。

此外,我试图避免使用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

如果你在这里阅读并有一些想法,请你建议我如何为这个特殊情况实现服务输入功能。

提前谢谢了,


第二篇文章 - 更新到目前为止我所做的工作。

根据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(代码= StatusCode.INVALID_ARGUMENT,details = \”断言失败:[无法将字节解码为JPEG,PNG,GIF或BMP] \ n \ t [[ {{node 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
2个回答
2
投票

沿着这些方向的东西应该有效:

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相关问题。


1
投票

回答!

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

根据错误,

{“error”:“预测失败:模型执行期间出错:AbortionError(代码= StatusCode.INVALID_ARGUMENT,details = \”断言失败:[无法将字节解码为JPEG,PNG,GIF或BMP] \ n \ t [[ {{node 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笔记本上打印出来,它就不会显示反斜杠。我必须在编辑器上打开它然后才能找到真正的问题。

© www.soinside.com 2019 - 2024. All rights reserved.