【问题标题】:Using gcloud ml serving for large images使用 gcloud ml serving 处理大图像
【发布时间】:2018-02-23 06:14:52
【问题描述】:

我有一个训练有素的 tensorflow 网络,我希望在 gcloud ml-engine 中使用它来进行预测。

预测 gcloud ml serving 应接受大小为 320x240x3 的 numpy 数组 float32 类型图像并返回 2 个微小矩阵作为输出。

有谁知道我应该如何创建可以接受这种输入类型的输入层?

我尝试了多种方法,例如使用 base64 编码的 json 文件,但是将字符串转换为浮点类型会产生不支持的错误:

"error": "Prediction failed: Exception during model execution: LocalError(code=StatusCode.UNIMPLEMENTED, details=\"Cast string to float is not supported\n\t [[Node: ToFloat = Cast[DstT=DT_FLOAT, SrcT=DT_STRING, _output_shapes=[[-1,320,240,3]], _device=\"/job:localhost/replica:0/task:0/cpu:0\"](ParseExample/ParseExample)]]\")"

这是一个创建json文件的例子(将上面的numpy数组保存为jpeg后):

python -c 'import base64, sys, json; img = base64.b64encode(open(sys.argv[1], "rb").read()); print json.dumps({"images": {"b64": img}})' example_img.jpg &> request.json

以及尝试处理输入的 tensorflow 命令:

raw_str_input = tf.placeholder(tf.string, name='source')
feature_configs = {
                'image': tf.FixedLenFeature(
                    shape=[], dtype=tf.string),
            }
tf_example = tf.parse_example(raw_str_input, feature_configs)
input = tf.identity(tf.to_float(tf_example['image/encoded']), name='input')

以上是其中一项测试的示例,还尝试了多次尝试不同的 tensorflow 命令来处理输入,但都没有奏效......

【问题讨论】:

    标签: numpy tensorflow google-cloud-ml-engine


    【解决方案1】:

    我建议不要使用parse_example 开始。发送图像数据有多种选择,每种选择都在复杂性和有效负载大小方面进行权衡:

    1. 原始张量编码为 JSON
    2. 张量打包为字节字符串
    3. 压缩图像数据

    在每种情况下,重要的是要注意输入占位符必须具有“无”作为其形状的外部尺寸。这是“batch_size”维度(必需,即使您打算将图像一张一张地发送到服务)。

    原始张量编码为 JSON

    # Dimensions represent [batch size, height width, channels]
    input_images = tf.placeholder(dtype=tf.float32, shape=[None,320,240,3], name='source')
    output_tensor = foo(input_images)
    
    # Export the SavedModel
    inputs = {'image': input_images}
    outputs = {'output': output_tensor}
    # ....
    

    您发送到服务的 JSON 将类似于 documented(请参阅“实例 JSON 字符串”)。例如,(我建议尽可能多地删除空白;为了便于阅读,此处打印得很漂亮):

    {
      "instances": [
        {
          "image": [
            [
              [1,1,1], [1,1,1], ... 240 total ... [1,1,1]
            ],
            ... 320 total ...
            [
              [1,1,1], [1,1,1], ... 240 total ... [1,1,1]
            ]
          ]
        },
        {
          "image": [ ... repeat if you have more than one image in the request ... ]
      ]
    }
    

    请注意,gcloud 从输入文件格式构建请求正文,其中每个输入位于单独的行(并且大多数打包在一行),即:

    {"image": [[[1,1,1], [1,1,1],  <240 of these>] ... <320 of these>]}
    {"image": [[[2,2,2], [2,2,2],  <240 of these>] ... <320 of these>]}
    

    张量打包成字节串

    如果您在客户端上调整大小等,我的建议是发送一个字节字符串。 JSON 可能是通过网络发送浮点数的一种相当低效的方式。即使发送整数数据也会导致膨胀。相反,您可以在客户端对字节进行编码并在 TensorFlow 中对其进行解码。我的建议是使用uint8 数据。

    这是解码字节字符串的 TensorFlow 模型代码:

    raw_byte_strings = tf.placeholder(dtype=tf.string, shape=[None], name='source')
    
    # Decode the images. The shape of raw_byte_strings is [batch size]
    # (were batch size is determined by how many images are sent), and
    # the shape of `input_images` is [batch size, 320, 240, 3]. It's
    # important that all of the images sent have the same dimensions
    # or errors will result.
    #
    # We have to use a map_fn because decode_raw only works on a single
    # image, and we need to decode a batch of images.
    decode = lambda raw_byte_str: tf.decode_raw(raw_byte_str, tf.uint8)
    input_images = tf.map_fn(decode, raw_byte_strings, dtype=tf.uint8)
    
    output_tensor = foo(input_images)
    
    # Export the SavedModel
    inputs = {'image_bytes': input_images}
    outputs = {'output': output_tensor}
    # ....
    

    这里需要特别注意:正如 Jeremy Lewi 所指出的,此输入别名 must 的名称以 _bytes 结尾(image_bytes)。这是因为 JSON 没有区分文本和二进制数据的方法。

    请注意,同样的技巧可以应用于浮点数据,而不仅仅是 uint8 数据。

    您的客户端将负责创建 uint8s 的字节字符串。以下是在 Python 中使用 numpy 的方法。

    import base64
    import json
    import numpy as np
    
    images = []
    # In real life, this is obtained via other means, e.g. scipy.misc.imread), for now, an array of all 1s 
    images.append(np.array([[[2]*3]*240]*320], dtype=np.uint8))
    # If we want, we can send more than one image:
    images.append(np.array([[[2]*3]*240]*320], dtype=np.uint8))
    
    # Convert each image to byte strings
    bytes_strings = (i.tostring() for i in images)
    
    # Base64 encode the data
    encoded = (base64.b64encode(b) for b in bytes_strings)
    
    # Create a list of images suitable to send to the service as JSON:
    instances = [{'image_bytes': {'b64': e}} for e in encoded]
    
    # Create a JSON request
    request = json.dumps({'instances': instances})
    
    # Or if dumping a file for gcloud:
    file_data = '\n'.join(json.dumps(instances))
    

    压缩图像数据

    在 TensorFlow 中发送原始图像并进行大小调整和解码通常方便。这在this sample中有一个例子,这里不再赘述。客户端只需要发送原始 JPEG 字节。相同的note 关于_bytes 后缀适用于此。

    【讨论】:

      【解决方案2】:

      如果您使用binary data with predictions,您的输入/输出别名必须以“字节”结尾。所以我认为你需要这样做

      python -c 'import base64, sys, json; img = base64.b64encode(open(sys.argv[1], "rb").read()); print json.dumps({"images_bytes": {"b64": img}})' example_img.jpg &> request.json
      

      【讨论】:

      • 非常感谢 Jeremey 的回复,我将输入签名更改为“image_bytes”,因此它会接受 json,但它仍然给我同样的错误。这就是我改变的:tf.saved_model.signature_def_utils.build_signature_def( inputs={'image_bytes': tensor_inputs_info} 究竟是什么意思 input/output 别名?你有一个简短的例子吗?
      猜你喜欢
      • 2018-09-18
      • 1970-01-01
      • 1970-01-01
      • 2019-12-29
      • 2021-02-24
      • 1970-01-01
      • 2020-01-07
      • 2013-11-16
      相关资源
      最近更新 更多