在cloudml上使用已部署的模型时,在base64中找到无效字符

时间:2018-05-30 06:04:37

标签: python tensorflow keras google-cloud-platform google-cloud-ml

为了更好的背景,我已经在云端上传了一个预先训练过的模型。这是一个初始V3模型,从keras转换为tensorflow中可接受的格式。

from keras.applications.inception_v3 import InceptionV3
model = InceptionV3(weights='imagenet') 
from keras.models import Model
intermediate_layer_model = Model(inputs=model.input,outputs=model.layers[311].output) 
with tf.Graph().as_default() as g_input:
    input_b64 = tf.placeholder(shape=(1,),
                               dtype=tf.string,
                               name='input')
    input_bytes = tf.decode_base64(input_b64[0])
    image = tf.image.decode_image(input_bytes)
    image_f = tf.image.convert_image_dtype(image, dtype=tf.float32)
    input_image = tf.expand_dims(image_f, 0)
    output = tf.identity(input_image, name='input_image') 
g_input_def = g_input.as_graph_def()
K.set_learning_phase(0)
sess = K.get_session()
from tensorflow.python.framework import graph_util
g_trans = sess.graph
g_trans_def = graph_util.convert_variables_to_constants(sess,
    g_trans.as_graph_def(),
    [intermediate_layer_model.output.name.replace(':0','')])
with tf.Graph().as_default() as g_combined:
    x = tf.placeholder(tf.string, name="input_b64")

    im, = tf.import_graph_def(g_input_def,
        input_map={'input:0': x},
        return_elements=["input_image:0"])

    pred, = tf.import_graph_def(g_trans_def,
             input_map={intermediate_layer_model.input.name: im,
             'batch_normalization_1/keras_learning_phase:0': False},
             return_elements=[intermediate_layer_model.output.name])

    with tf.Session() as sess2:
        inputs = {"inputs": tf.saved_model.utils.build_tensor_info(x)}
        outputs = {"outputs":tf.saved_model.utils.build_tensor_info(pred)}
        signature =tf.saved_model.signature_def_utils.build_signature_def(
                inputs=inputs,
                outputs=outputs,
        method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME
            )

      # save as SavedModel
        b = tf.saved_model.builder.SavedModelBuilder('inceptionv4/')
        b.add_meta_graph_and_variables(sess2,
                      [tf.saved_model.tag_constants.SERVING],
                      signature_def_map={'serving_default': signature})
        b.save()

生成的pb文件在本地使用时工作正常。但是当我在cloud ml上部署它时,我得到以下错误。

RuntimeError: Prediction failed: Error during model execution: AbortionError(code=StatusCode.INVALID_ARGUMENT, details="Invalid character found in base64.
     [[Node: import/DecodeBase64 = DecodeBase64[_output_shapes=[<unknown>], _device="/job:localhost/replica:0/task:0/device:CPU:0"](import/strided_slice)]]")

以下是我用于获取本地预测的代码。

import base64
import json

with open('MEL_BE_0.jpg', 'rb') as image_file:
    encoded_string = str(base64.urlsafe_b64encode(image_file.read()),'ascii')

import tensorflow as tf

with tf.Session(graph=tf.Graph()) as sess:
    MetaGraphDef=tf.saved_model.loader.load(
       sess,
       [tf.saved_model.tag_constants.SERVING],
       'inceptionv4')
    input_tensor = tf.get_default_graph().get_tensor_by_name('input_b64:0')
    print(input_tensor)
    avg_tensor = tf.get_default_graph().get_tensor_by_name('import_1/avg_pool/Mean:0')
    print(avg_tensor)
    predictions = sess.run(avg_tensor, {input_tensor: [encoded_string]})

最后,下面是我用于将编码字符串包装在发送到cloud-ml引擎的请求中的代码片段。

request_body= json.dumps({"key":"0", "image_bytes": {"b64": [encoded_string]}})

1 个答案:

答案 0 :(得分:2)

看起来您正在尝试在TensorFlow 中执行base64解码,而使用{"b64": ...} JSON格式。你需要做一个或另一个;我们通常推荐后者。

作为旁注,您的输入占位符的外部尺寸必须为None。这可能会使一些事情变得棘手,例如,您必须将尺寸重塑为尺寸1(这将阻止您在当前状态下使用批量预测服务)或者您必须向我们{ {1}}将同一组转换应用于输入&#34;批次&#34;的每个元素。您可以在this example中找到该技术的示例。

最后,我建议使用tf.map_fn

完全放在一起,这里有一些修改过的代码。请注意,我在内联您的输入函数(而不是将其序列化为图形def并重新导入):

tf.saved_model.simple_save

注意:我并非100%确定HEIGHT = 299 WIDTH = 299 # Get Keras Model from keras.applications.inception_v3 import InceptionV3 model = InceptionV3(weights='imagenet') from keras.models import Model intermediate_layer_model = Model(inputs=model.input,outputs=model.layers[311].output) K.set_learning_phase(0) sess = K.get_session() from tensorflow.python.framework import graph_util g_trans = sess.graph g_trans_def = graph_util.convert_variables_to_constants(sess, g_trans.as_graph_def(), [intermediate_layer_model.output.name.replace(':0','')]) # Create inputs to model and export with tf.Graph().as_default() as g_combined: def decode_and_resize(image_bytes): image = tf.image.decode_image(image_bytes) # Note resize expects a batch_size, but tf_map supresses that index, # thus we have to expand then squeeze. Resize returns float32 in the # range [0, uint8_max] image = tf.expand_dims(image, 0) image = tf.image.resize_bilinear( image, [HEIGHT, WIDTH], align_corners=False) image = tf.squeeze(image, squeeze_dims=[0]) image = tf.cast(image, dtype=tf.uint8) return image input_byes = tf.placeholder(shape=(None,), dtype=tf.string, name='input') images = tf.map_fn( decode_and_resize, input_bytes, back_prop=False, dtype=tf.uint8) images = tf.image.convert_image_dtype(images, dtype=tf.float32) pred, = tf.import_graph_def(g_trans_def, input_map={intermediate_layer_model.input.name: images, 'batch_normalization_1/keras_learning_phase:0': False}, return_elements=[intermediate_layer_model.output.name]) with tf.Session() as sess2: tf.saved_model.simple_save( sess2, model_dir='inceptionv4/' inputs={"inputs": input_bytes}, outputs={"outputs": pred}) intermediate_layer_model的形状是兼容的。 images的形状将为[None,height,width,num_channels]。

另请注意,您的本地预测代码会有所改变。您不需要对图像进行base64编码,您需要发送&#34;批量&#34; /图像列表而不是单个图像。类似的东西:

images

您没有说明您是否正在进行批量预测或在线预测,它们具有相似但略有不同的格式&#34;对于输入。在任何一种情况下,您的模型都不会导出&#34;键&#34; field(你的意思是?它可能对批量预测有帮助,但不适用于在线)。

对于批量预测,文件格式为JSON行;每行包含一个例子。可以从Python生成每一行:

with open('MEL_BE_0.jpg', 'rb') as image_file:
  encoded_string = image_file.read()

input_tensor = tf.get_default_graph().get_tensor_by_name('input:0')
print(input_tensor)
avg_tensor = tf.get_default_graph().get_tensor_by_name('import_1/avg_pool/Mean:0')
print(avg_tensor)
predictions = sess.run(avg_tensor, {input_tensor: [encoded_string]})

(注意省略&#34;键&#34;暂时)。由于您只有一个输入,因此有一个简写:

example = json.dumps({"image_bytes": {"b64": ENCODED_STRING}})

如果您想进行在线预测,请注意,如果您使用example = json.dumps({"b64": ENCODED_STRING}) 发送请求,则实际使用的文件格式与批量预测相同。

事实上,我们强烈建议您在部署到云之前使用gcloud来帮助调试。

如果您打算使用除gcloud之外的其他客户端,例如,在Web应用程序,移动应用程序,前端服务器等中,那么您将无法发送文件,您需要自己构建完整的请求。它与上面的文件格式非常相似。基本上,取JSON行文件的每一行并将它们放入一个数组calle&#34;实例&#34;,即,

gcloud ml-engine local predict --json-instances=FILE --model-dir=...

如果您愿意,可以使用相同的语法糖

request_body= json.dumps({"instances": [{"image_bytes": {"b64": [encoded_string]}}]})

我希望这有帮助!