Использование gcloud ml для больших изображений

У меня есть обученная сеть в тензорном потоке, которую я хочу использовать в gcloud ml-engine для прогнозирования.

Прогнозирование обслуживания gcloud ml должно принимать изображения типа float32 массива numpy размером 320x240x3 и возвращать 2 крошечные матрицы в качестве вывода.

Кто-нибудь знает, как мне создать входные слои, которые будут принимать этот тип ввода?

Я пробовал несколько способов, например, используя файлы json с кодировкой base64, но приведение строки к типу float приводит к ошибке, в которой она не поддерживается:

"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

И команды тензорного потока, пытающиеся обработать ввод:

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')

выше приведен пример одного из выполненных тестов, также были предприняты несколько попыток различных команд тензорного потока для обработки ввода, но ни одна из них не сработала...


person A. Zan    schedule 14.09.2017    source источник


Ответы (2)


Я бы рекомендовал не использовать 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, который вы отправляете в службу, будет выглядеть как задокументированный (см. «Экземпляры строки 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}
# ....

Одно особое примечание: как указал Джереми Леви, имя этого входного псевдонима должен заканчиваться на _bytes (image_bytes). Это связано с тем, что JSON не имеет способа отличить текст от двоичных данных.

Обратите внимание, что тот же трюк можно применить к данным с плавающей запятой, а не только к данным uint8.

Ваш клиент будет отвечать за создание строки байтов uint8. Вот как вы могли бы сделать это в 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. Это показано в этом примере, который я здесь не повторится. Клиенту просто нужно отправить необработанные байты JPEG. Здесь применяется то же примечание о суффиксе _bytes.

person rhaertel80    schedule 14.09.2017

Если вы используете бинарные данные с прогнозами, псевдонимы ввода/вывода должны заканчиваться на «байты». Поэтому я думаю, что вам нужно сделать

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
person Jeremy Lewi    schedule 14.09.2017
comment
Большое спасибо Джереми за ваш ответ, я изменил входную подпись на image_bytes, чтобы он принимал json, но все равно выдает ту же ошибку. вот что я изменил: tf.saved_model.signature_def_utils.build_signature_def( inputs={'image_bytes': tensor_inputs_info} что именно означают input/output псевдонимы? у вас есть короткий пример? - person A. Zan; 14.09.2017