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

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

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

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

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

"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 (после сохранения приведенного выше массива в формате 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

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

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

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

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.

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

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

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
Другие вопросы по тегам