Как мне изменить экспорт модели keras, чтобы принять строку b64 в RESTful API/Google cloud ML

Полный код для экспорта модели: (я уже обучил его и теперь загружаю из файла весов)

def cnn_layers(inputs):
  conv_base= keras.applications.mobilenetv2.MobileNetV2(input_shape=(224,224,3), input_tensor=inputs, include_top=False, weights='imagenet')
  for layer in conv_base.layers[:-200]:
    layer.trainable = False
  last_layer = conv_base.output
  x = GlobalAveragePooling2D()(last_layer)
  x= keras.layers.GaussianNoise(0.3)(x)
  x = Dense(1024,name='fc-1')(x)
  x = keras.layers.BatchNormalization()(x)
  x = keras.layers.advanced_activations.LeakyReLU(0.3)(x)
  x = Dropout(0.4)(x)
  x = Dense(512,name='fc-2')(x)
  x = keras.layers.BatchNormalization()(x)
  x = keras.layers.advanced_activations.LeakyReLU(0.3)(x)
  x = Dropout(0.3)(x)
  out = Dense(10, activation='softmax',name='output_layer')(x)
  return out

model_input = layers.Input(shape=(224,224,3))

model_output = cnn_layers(model_input)

test_model = keras.models.Model(inputs=model_input, outputs=model_output)

weight_path = os.path.join(tempfile.gettempdir(), 'saved_wt.h5')

test_model.load_weights(weight_path)

export_path='export'
from tensorflow.python.saved_model import builder as saved_model_builder
from tensorflow.python.saved_model import utils
from tensorflow.python.saved_model import tag_constants, signature_constants
from tensorflow.python.saved_model.signature_def_utils_impl import build_signature_def, predict_signature_def
from tensorflow.contrib.session_bundle import exporter

builder = saved_model_builder.SavedModelBuilder(export_path)

signature = predict_signature_def(inputs={'image': test_model.input},
                                  outputs={'prediction': test_model.output})

with K.get_session() as sess:
    builder.add_meta_graph_and_variables(sess=sess,
                                         tags=[tag_constants.SERVING],
                                         signature_def_map={'predict': signature})
    builder.save()

И вывод  (каталог 1 имеет saved_model.pb и models каталог) :
python /tensorflow/python/tools/saved_model_cli.py show --dir /1 --all  

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['predict']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['image'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 224, 224, 3)
        name: input_1:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['prediction'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 107)
        name: output_layer/Softmax:0
  Method name is: tensorflow/serving/predict

Чтобы принять строку b64: код был написан для массива (224, 224, 3) numpy. Итак, изменения, которые я сделал для приведенного выше кода:

  • _bytes должно быть добавлено к вводу при передаче как b64. Так,

predict_signature_def(inputs={'image':......
          изменено на
predict_signature_def(inputs={'image_bytes':.....

  • Раньше type(test_model.input) это: (224, 224, 3) и dtype: DT_FLOAT. Так,

signature = predict_signature_def(inputs={'image': test_model.input},.....           изменено на (ссылка)
temp = tf.placeholder(shape=[None], dtype=tf.string)
signature = predict_signature_def(inputs={'image_bytes': temp},.....

Изменить:
Код для отправки с использованием запросов: (как указано в комментариях)

encoded_image = None
with open('/1.jpg', "rb") as image_file:
    encoded_image = base64.b64encode(image_file.read())
object_for_api = {"signature_name": "predict",
                  "instances": [
                      {
                           "image_bytes":{"b64":encoded_image}
                           #"b64":encoded_image (or this way since "image" is not needed)
                      }]
                  }

p=requests.post(url='http://localhost:8501/v1/models/mnist:predict', json=json.dumps(object_for_api),headers=headers)
print(p)

Я получаю сообщение об ошибке <Response [400]>. Я думаю, что в том, как я отправляю, нет ошибки. Нужно что-то изменить в коде экспорта модели и конкретно в
temp = tf.placeholder(shape=[None], dtype=tf.string).


person duplex143    schedule 05.07.2018    source источник
comment
Вы когда-нибудь находили решение этой проблемы?   -  person gogasca    schedule 11.07.2019


Ответы (2)


Глядя на документы, которые вы предоставили, вам нужно взять изображение и отправить его в API. Изображения легко передаются в текстовом формате, если вы их кодируете, стандартом является base64. Итак, что мы хотим сделать, это создать объект json с изображением в формате base64 в нужном месте, а затем отправить этот объект json в REST API. python имеет библиотеку запросов, которая упрощает отправку словаря python в формате JSON.

Итак, возьмите изображение, закодируйте его, поместите в словарь и отправьте с помощью запросов:

import requests
import base64

encoded_image = None
with open("image.png", "rb") as image_file:
    encoded_image = base64.b64encode(image_file.read())

object_for_api = {"signature_name": "predict",
                  "instances": [
                      {
                          "image": {"b64": encoded_image}
                      }]
                  }

requests.post(url='http://localhost:8501/v1/models/mnist:predict', json=object_for_api)

Вы также можете закодировать свой массив numpy в JSON, но не похоже, что документы API ищут это.

person Steven Stip    schedule 05.07.2018
comment
Разве это не должно быть json=json.dumps(object_for_api) вместо json=object_for_api). Обратите внимание, что я обновил вопрос. - person duplex143; 05.07.2018
comment
Вам нужно передать строку, если вы хотите поместить ее прямо в тело, но здесь вы передаете словарь через json. поэтому вы просто добавляете словарь как json в запрос. Я вижу, вы обновили вопрос, теперь вы получаете 400, что указывает на неверный запрос. Обычно тело запроса дает вам объяснение. - person Steven Stip; 09.07.2018
comment
Итак, изменения, которые я сделал для экспорта модели, верны? - person duplex143; 09.07.2018
comment
Вам не хватает аутентификации. См. документы и пример. Использование googleapiclient.discovery упростит аутентификацию. - person rhaertel80; 16.07.2018

Два примечания:

  1. Я рекомендую вам использовать tf.saved_model.simple_save
  2. Вы можете найти model_to_estimator удобным.
  3. Хотя ваша модель кажется, что она будет работать для запросов (вывод saved_model_cli показывает, что внешнее измерение равно None как для входов, так и для выходов), довольно неэффективно отправлять массивы JSON с плавающей запятой.

Наконец, часто бывает проще изменить код для декодирования изображения на стороне сервера, чтобы вы отправляли JPG или PNG в кодировке base64 по сети вместо массива чисел с плавающей запятой. Вот один пример для Keras (я планирую обновить этот ответ более простым кодом).

person rhaertel80    schedule 16.07.2018
comment
tf.saved_model.simple_save не следует использовать из-за того, что он устарел (см. ссылку выше). - person fraank; 10.09.2019