TensorFlow Служит для изображений в виде строк в кодировке base64 на Cloud ML Engine - PullRequest
1 голос
/ 05 марта 2019

Как реализовать функцию входного обслуживания TensorFlow для изображений в виде строк в кодировке base64 и получить прогноз на Cloud ML Engine

Я планирую развернуть модель на движке Cloud Machine Learning (ML) после предварительного обучения, но не знаю, как реализовать функцию обслуживания ввода .

Кроме того, я старался избегать низкоуровневых API TensorFlow и сосредоточился только на высокоуровневых API TensorFlow ( 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

Я хотел бы создать функцию ввода обслуживания , что

  1. Получение изображений в виде строк в кодировке base64 в формате JSON

  2. Преобразуйте их в тензоры и уменьшите размер до (?, 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

Если вы читаете до здесь и у вас есть идея, не могли бы вы подсказать, как реализовать функцию входного ввода для этого конкретного случая.

Заранее большое спасибо,


2nd Post - Чтобы обновить то, что я сделал до сих пор.

Согласно комментарию sdcbr, ниже приведен мой serve_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": "Ошибка прогноза: ошибка во время выполнения модели: Ошибка AbortionError (code = StatusCode.INVALID_ARGUMENT, details = \ "утверждение не удалось: [невозможно декодировать байты в формате JPEG, PNG, GIF или BMP] \ n \ t [[{{узел map / while / decode_image / cond_jpeg / cond_png / cond_gif / Assert_1 / Assert}} = Утвердите [T = [DT_STRING], суммируйте = 3, _device = \ "/ работа: локальный / реплика: 0 / задача: 0 / устройства: CPU: 0 \"] (отображение /, а / decode_image / cond_jpeg / cond_png / cond_gif / is_bmp, карта / во время / decode_image / cond_jpeg / cond_png / cond_gif / Assert_1 / Утверждай / data_0)]] \ ")" }

Я что-то не так сделал в функции _img_string_to_tensor?

и не могли бы вы разъяснить мне больше об этом tf.placeholder?

input_ph = tf.placeholder(tf.string, shape=[None])

В приведенном выше коде вы используете shape = [1], но я думаю, что оно должно быть shape = [None].

Ответы [ 2 ]

2 голосов
/ 05 марта 2019

Что-то вроде этого должно работать:

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
    # https://stackoverflow.com/questions/52303403/
    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, которая должна получить входные данные.

См. Также этот и этот связанный вопрос.

1 голос
/ 11 марта 2019

Ответ!

Из комментария sdcbr, правильный ответ , который я ищу, но проблема, которую я только что выяснил, почему она не работает.

На основании ошибки

{"error": "Ошибка прогноза: ошибка при выполнении модели: ошибка AbortionError (code = StatusCode.INVALID_ARGUMENT, details = \": [Невозможно декодировать байтыв формате JPEG, PNG, GIF или BMP] \ n \ t [[{{карта узла / while / decode_image / cond_jpeg / cond_png / cond_gif / Assert_1 / Assert}} = Assert [T = [DT_STRING], sumrize = 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=="}}
.
.

После того, как я очистил и удалил все обратные слеши, это работает!

PS это то, что вам нужно тщательно проверить.Если вы распечатываете его на ноутбуке IPython, он не показывает обратную косую черту.Я должен открыть его в редакторе, а затем я нахожу реальную проблему.

...