При использовании развернутой модели в cloudml обнаружен недопустимый символ в base64 - PullRequest
0 голосов
/ 30 мая 2018

Для лучшего контекста я загрузил предварительно обученную модель в облачную мл.Это модель inceptionV3, преобразованная из keras в приемлемый формат с помощью tenorflow.

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 отлично работает, когда я использую его локально.Но когда я развертываю его в облаке 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 голосов
/ 30 мая 2018

Похоже, вы пытаетесь выполнить декодирование base64 в TensorFlow , а использовать формат {"b64": ...} JSON.Вам нужно сделать одно или другое;как правило, мы рекомендуем последнее.

В качестве примечания, ваш входной заполнитель должен иметь внешний размер None.Это может усложнить некоторые вещи, например, вам придется либо изменить размеры до размера 1 (что помешает вам использовать службу пакетного прогнозирования в ее текущем состоянии), либо вам придется tf.map_fn применитьодинаковый набор преобразований для каждого элемента входного «пакета».Вы можете найти пример этой техники в этом примере .

Наконец, я рекомендую использовать tf.saved_model.simple_save.

В целом, вот некоторый модифицированный код.Обратите внимание, что я встраиваю вашу функцию ввода (в отличие от ее сериализации в графическое определение и повторного импорта):

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

Примечание: Я не уверен на 100%, что фигурыintermediate_layer_model и images совместимы.Форма images будет [Нет, высота, ширина, num_channels].

Также обратите внимание, что ваш локальный код предсказания немного изменится.Вы не кодируете изображения base64, и вам нужно отправить «пакет» / список изображений, а не отдельные изображения.Что-то вроде:

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]})

Вы не указали, используете ли вы пакетное прогнозирование или онлайн-прогнозирование, которые имеют похожие, но немного отличающиеся «форматы» для входных данных.В любом случае ваша модель не экспортирует поле «ключ» (вы имели в виду? Возможно, это полезно для пакетного прогнозирования, но не для онлайн).

Для пакетного прогнозирования формат файла - строки JSON;каждая строка содержит один пример.Каждая строка может быть сгенерирована следующим образом из Python:

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

(обратите внимание на пропуск "ключа" на данный момент).Поскольку у вас есть только один вход, есть сокращение:

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

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

На самом деле, мы настоятельно рекомендуем использовать gcloud ml-engine local predict --json-instances=FILE --model-dir=... перед развертыванием в облаке, чтобы помочь отладке.

Если вы собираетесь использовать какой-то другой клиент помимо gcloud, напримерв веб-приложении, мобильном приложении, веб-сервере и т. д. вы не будете отправлять файл, и вам нужно будет самостоятельно создать полный запрос.Это очень похоже на формат файла выше.В основном, возьмите каждую строку файла строк JSON и поместите их в массив, называемый «instances», то есть

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

Вы можете использовать тот же синтаксический сахар, если хотите:

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

Надеюсь, это поможет!

...