Обслуживание Tensorflow на предварительно обученной модели Keras ResNet50, всегда возвращающее одни и те же прогнозы

Я использую следующий код для экспорта предварительно обученной модели Keras ResNet50 в тензорный поток для обслуживания тензорного потока:

import tensorflow as tf
sess = tf.Session()
from keras import backend as K
K.set_session(sess)
K.set_learning_phase(0)

# Modelo resnet con pesos entrenados en imagenet
from keras.applications.resnet50 import ResNet50
model = ResNet50(weights='imagenet')

# exportar en tensorflow
import os
version_number = max([ int(x) for x in os.listdir('./resnet-classifier') ]) + 1
export_path = './resnet-classifier/{}'.format(version_number)
with tf.keras.backend.get_session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    tf.saved_model.simple_save(sess, export_path,
            inputs=dict(input_image=model.input),
            outputs={t.name:t for t in model.outputs}
    )

Я пробовал несколько вариантов вышеперечисленного, все они с одинаковыми результатами (тот же прогноз при обслуживании с помощью тензорного потока).

Затем я запускаю тензорный поток, например:

docker run -p 8501:8501 \
  -v ./resnet-classifier:/models/resnet-classifier \
  -e MODEL_NAME=resnet-classifier -e MODEL_BASE_PATH=/models \
  -t tensorflow/serving

Наконец, я использую следующую функцию, чтобы делать прогнозы относительно обслуживания тензорного потока:

def imagepath_to_tfserving_payload(img_path):
    import numpy as np
    from keras.preprocessing import image
    from keras.applications.resnet50 import preprocess_input
    img = image.img_to_array(image.load_img(img_path, target_size=(224, 224)))
    X = np.expand_dims(img, axis=0).astype('float32')
    X = preprocess_input(X)
    payload = dict(instances=X.tolist())
    payload = json.dumps(payload)
    return payload

def tfserving_predict(image_payload, url=None):
    import requests
    if url is None:
        url = 'http://localhost:8501/v1/models/resnet-classifier:predict'
    r = requests.post(url, data=image_payload)
    pred_json = json.loads(r.content.decode('utf-8'))
    from keras.applications.resnet50 import decode_predictions
    predictions = decode_predictions(np.asarray(pred_json['predictions']), top=3)[0]
    return predictions

Затем я использую обе указанные выше функции из оболочки ipython, чтобы выбрать случайные изображения из набора val imagenet, который я сохранил локально. Проблема в том, что обслуживание тензорного потока всегда возвращает один и тот же прогноз для всех отправляемых мной изображений.

Каждый раз, когда я экспортирую модель с помощью первого скрипта, приведенного выше, я получаю немного разные классы с достоверностью «1» для первого класса и «0» для других, например:

# Serialization 1, in ./resnet-classifier/1 always returning:
[
  [
    "n07745940",
    "strawberry",
    1.0
  ],
  [
    "n02104029",
    "kuvasz",
    1.4013e-36
  ],
  [
    "n15075141",
    "toilet_tissue",
    0.0
  ]
]

# Serialization 2, in ./resnet-classifier/2 always returning:
[
  [
    "n01530575",
    "brambling",
    1.0
  ],
  [
    "n15075141",
    "toilet_tissue",
    0.0
  ],
  [
    "n02319095",
    "sea_urchin",
    0.0
  ]
]

Это может быть связано с Tensorflow: обслуживающая модель всегда возвращает один и тот же прогноз, но я не знаю, как ответы там (не принятые) могут помочь.

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


person roirodriguez    schedule 25.11.2018    source источник


Ответы (2)


Я обнаружил, что вызов sessions.run (tf.global_variables_initializer ()) отменяет предварительно обученные веса, подсказка находится на http://zachmoshe.com/2017/11/11/use-keras-models-with-tf.html.

Решение для меня было действительно простым, просто измените первый блок кода в исходном вопросе следующим образом, который вызывает tf.global_variables_initializer () до создания экземпляра модели / загрузки веса:

import tensorflow as tf
sess = tf.Session()
sess.run(tf.global_variables_initializer())

from keras import backend as K
K.set_session(sess)
K.set_learning_phase(0)

# Modelo resnet con pesos entrenados en imagenet
from keras.applications.resnet50 import ResNet50
model = ResNet50(weights='imagenet')

# exportar en tensorflow
import os
versions = [ int(x) for x in os.listdir('./resnet-classifier') ]
version_number = max(versions) + 1 if versions else 1
export_path = './resnet-classifier/{}'.format(version_number)

tf.saved_model.simple_save(sess, export_path,
        inputs=dict(input_image=model.input),
        outputs={t.name:t for t in model.outputs}
)
person roirodriguez    schedule 11.12.2018

У меня иногда возникали такие проблемы, когда я забывал нормализовать изображение. Я думаю, что resnet принимает изображения в формате чисел с плавающей запятой от 0 до 1. (или, может быть, от -1 до 1.). Я не знаю, что делает функция preprocess_input, но вы можете проверить, возвращает ли она массив в ожидаемом формате.

person Andrey Kite Gorin    schedule 25.11.2018
comment
Я тоже думал об этом, но я работаю правильно, когда использую preprocess_input непосредственно против этой модели keras. Я безуспешно пытался разделить на 255 после, до и вместо preprocess_input :-( - person roirodriguez; 25.11.2018
comment
resnet50 использует предварительную обработку в стиле caffe, то есть не масштабирует пиксели, чтобы они принадлежали [0,1]. Функция preprocess_input по умолчанию должна делать то же самое. Нет необходимости изменять масштаб пикселей, когда вы используете resnet50 для обучения передачи или для вывода с предварительно обученной моделью. - person Srihari Humbarwadi; 26.11.2018