TensorFlow Serving 将图像作为 Cloud ML Engine 上的 base64 编码字符串
TensorFlow Serving for images as base64-encoded strings on Cloud ML Engine
如何将图像作为 base64 编码字符串实现 TensorFlow 服务输入功能并在 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 编码字符串
将它们转换为Tensors,并将尺寸缩小到(?, 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
如果您一直读到这里并且有一些想法,能否请您建议我如何针对这种特殊情况实现服务输入功能。
非常感谢,
第 2 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": "Prediction failed: Error during model execution:
AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"assertion
failed: [Unable to decode bytes as JPEG, PNG, GIF, or BMP]\n\t
[[{{node
map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert}} =
Assert[T=[DT_STRING], summarize=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].
按照这些思路应该可行:
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
#
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.keras
模型中应该接收输入的层的名称。
另见 this and 相关问题。
回答!
从sdcbr的评论来看,这是我要找的正确答案,但我刚刚发现为什么它不起作用的问题。
根据错误,
{ "error": "Prediction failed: Error during model execution:
AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"assertion
failed: [Unable to decode bytes as JPEG, PNG, GIF, or BMP]\n\t
[[{{node
map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert}} =
Assert[T=[DT_STRING], summarize=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=="}}
.
.
在我清理并删除所有反斜杠后,有效!
P.S。这是你需要仔细检查的。如果您在 IPython 笔记本上打印出来,它不会显示反斜杠。我必须在编辑器上打开它,然后才能找到真正的问题。
如何将图像作为 base64 编码字符串实现 TensorFlow 服务输入功能并在 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 编码字符串
将它们转换为Tensors,并将尺寸缩小到(?, 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
如果您一直读到这里并且有一些想法,能否请您建议我如何针对这种特殊情况实现服务输入功能。
非常感谢,
第 2 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": "Prediction failed: Error during model execution: AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"assertion failed: [Unable to decode bytes as JPEG, PNG, GIF, or BMP]\n\t [[{{node map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert}} = Assert[T=[DT_STRING], summarize=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].
按照这些思路应该可行:
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
#
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.keras
模型中应该接收输入的层的名称。
另见 this and
回答!
从sdcbr的评论来看,这是我要找的正确答案,但我刚刚发现为什么它不起作用的问题。
根据错误,
{ "error": "Prediction failed: Error during model execution: AbortionError(code=StatusCode.INVALID_ARGUMENT, details=\"assertion failed: [Unable to decode bytes as JPEG, PNG, GIF, or BMP]\n\t [[{{node map/while/decode_image/cond_jpeg/cond_png/cond_gif/Assert_1/Assert}} = Assert[T=[DT_STRING], summarize=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=="}}
.
.
在我清理并删除所有反斜杠后,有效!
P.S。这是你需要仔细检查的。如果您在 IPython 笔记本上打印出来,它不会显示反斜杠。我必须在编辑器上打开它,然后才能找到真正的问题。