【发布时间】: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
我想创建一个服务输入函数
将图像获取为 JSON 格式的 base64 编码字符串
将它们转换为张量并将大小减小到 (?, 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