Керас несовместимое время предсказания

Я попытался оценить время предсказания моей модели keras и понял кое-что странное. Помимо того, что в нормальных условиях она довольно быстра, время от времени модели требуется довольно много времени, чтобы сделать прогноз. И не только это, эти времена также тем больше, чем дольше работает модель. Я добавил минимальный рабочий пример для воспроизведения ошибки.

import time
import numpy as np
from sklearn.datasets import make_classification
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten

# Make a dummy classification problem
X, y = make_classification()

# Make a dummy model
model = Sequential()
model.add(Dense(10, activation='relu',name='input',input_shape=(X.shape[1],)))
model.add(Dense(2, activation='softmax',name='predictions'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

model.fit(X, y, verbose=0, batch_size=20, epochs=100)

for i in range(1000):
    # Pick a random sample
    sample = np.expand_dims(X[np.random.randint(99), :], axis=0)
    # Record the prediction time 10x and then take the average
    start = time.time()
    for j in range(10):
        y_pred = model.predict_classes(sample)
    end = time.time()
    print('%d, %0.7f' % (i, (end-start)/10))

Время не зависит от выборки (выбирается случайным образом). Если тест повторяется, индексы в цикле for, прогнозирование которого занимает больше времени, снова будут (почти) такими же.

Я использую:

tensorflow 2.0.0
python 3.7.4

Для моей заявки мне нужно гарантировать выполнение в определенное время. Однако это невозможно, учитывая такое поведение. Что не так? Это ошибка в Keras или ошибка в бэкэнде тензорного потока?

РЕДАКТИРОВАТЬ: predict_on_batch показывает то же поведение, но более разреженно:

y_pred = model(sample, training=False).numpy() показывает также некоторые сильные выбросы, однако они не увеличиваются.

РЕДАКТИРОВАТЬ 2: я перешел на последнюю версию tensorflow 1 (1.15). Проблема не только в том, что проблема больше не существует, но и значительно улучшилось "нормальное" время предсказания! Я не считаю эти два всплеска проблемными, поскольку они не появились, когда я повторил тест (по крайней мере, не с теми же индексами и линейно возрастающими), и их процентное соотношение не так велико, как на первом графике.

Таким образом, мы можем сделать вывод, что это, похоже, проблема, присущая тензорному потоку 2.0, который демонстрирует аналогичное поведение в других ситуациях, о которых упоминает @OverLordGoldDragon.

2 ответа

Решение

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

В двух словах: model(x) действует через свои __call__ метод (который наследуется от base_layer.Layer), в то время как predict(), predict_classes()и т. д. включают выделенную функцию цикла через _select_training_loop(); в каждом из них используются разные методы предварительной и последующей обработки данных, подходящие для разных сценариев использования, иmodel(x) в 2.1 был разработан специально для обеспечения максимальной производительности при работе с небольшими моделями / небольшими партиями (и, возможно, любого размера) (и все еще самым быстрым в версии 2.0).

Цитата разработчика TensorFlow из связанных обсуждений:

Вы можете предсказать результат, используя вызов модели, а не прогноз модели, т. Е. Вызов model(x) сделает это намного быстрее, потому что нет части "преобразование в набор данных", а также он напрямую вызывает кешированный tf.function.

Примечание: это должно быть меньше проблем в 2.1 и особенно 2.2, но все равно тестируйте каждый метод. Также я понимаю, что это не дает прямого ответа на ваш вопрос о временных пиках; Я подозреваю, что это связано с механизмами нетерпеливого кэширования, но самый надежный способ определить это через TF Profiler, что нарушено в 2.1.


Обновление: что касается нарастающих пиков, возможно троттлинг GPU; вы сделали ~1000 итеров, попробуйте вместо этого 10000 - в конце концов рост должен прекратиться. Как вы отметили в своих комментариях, этого не происходит сmodel(x); имеет смысл, поскольку задействуется на один шаг GPU меньше ("преобразование в набор данных").

Update2: вы могли бы ошибка в УБС здесь об этом, если вы сталкиваетесь этот вопрос; это в основном я пою там

Хотя я не могу объяснить несоответствия во времени выполнения, я могу порекомендовать вам попытаться преобразовать вашу модель в TensorFlow Lite, чтобы ускорить прогнозирование отдельных записей данных или небольших пакетов.

Я провел тест на этой модели:

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(384, activation='elu', input_shape=(256,)),
    tf.keras.layers.Dense(384, activation='elu'),
    tf.keras.layers.Dense(256, activation='elu'),
    tf.keras.layers.Dense(128, activation='elu'),
    tf.keras.layers.Dense(32, activation='tanh')
])

Время прогнозирования для отдельных записей было:

  1. model.predict(input): 18 мс
  2. model(input): 1,3 мс
  3. Модель преобразована в TensorFlow Lite: 43us

Время на преобразование модели составило 2 секунды.

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

class LiteModel:

    @classmethod
    def from_file(cls, model_path):
        return LiteModel(tf.lite.Interpreter(model_path=model_path))

    @classmethod
    def from_keras_model(cls, kmodel):
        converter = tf.lite.TFLiteConverter.from_keras_model(kmodel)
        tflite_model = converter.convert()
        return LiteModel(tf.lite.Interpreter(model_content=tflite_model))

    def __init__(self, interpreter):
        self.interpreter = interpreter
        self.interpreter.allocate_tensors()
        input_det = self.interpreter.get_input_details()[0]
        output_det = self.interpreter.get_output_details()[0]
        self.input_index = input_det["index"]
        self.output_index = output_det["index"]
        self.input_shape = input_det["shape"]
        self.output_shape = output_det["shape"]
        self.input_dtype = input_det["dtype"]
        self.output_dtype = output_det["dtype"]

    def predict(self, inp):
        inp = inp.astype(self.input_dtype)
        count = inp.shape[0]
        out = np.zeros((count, self.output_shape[1]), dtype=self.output_dtype)
        for i in range(count):
            self.interpreter.set_tensor(self.input_index, inp[i:i+1])
            self.interpreter.invoke()
            out[i] = self.interpreter.get_tensor(self.output_index)[0]
        return out

    def predict_single(self, inp):
        """ Like predict(), but only for a single record. The input data can be a Python list. """
        inp = np.array([inp], dtype=self.input_dtype)
        self.interpreter.set_tensor(self.input_index, inp)
        self.interpreter.invoke()
        out = self.interpreter.get_tensor(self.output_index)
        return out[0]

Полный код теста и график можно найти здесь:https://medium.com/@micwurm/using-tensorflow-lite-to-speed-up-predictions-a3954886eb98

Другие вопросы по тегам