Керас противоречивый прогноз времени

17

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

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 или ошибка в бэкэнде tenorflow?

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

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

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

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

ga97dil
источник
Такое поведение звучит предсказуемо ... увеличение является линейным. Если вы включите это поведение в свой расчет времени, не пойдет ли это? --- Я не знаю, что там происходит .... но что произойдет, если вы попробуете predict_on_batchвместо этого?
Даниэль Мёллер
Еще одна попытка, что происходит с y_pred = model(sample).numpy()и с y_pred = model(sample, training=False).numpy()?
Даниэль Мёллер
Я добавил свои выводы. Одинокие версии, кажется, не показывают поведение.
ga97dil
Но predict_classesвсе еще самый быстрый .... кажется. Как насчет просто predict?
Даниэль Мёллер
1
Я предполагаю, что это может быть какой-то тип очистки памяти ....
Даниэль Мёллер

Ответы:

10

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

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

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

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

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


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

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

OverLordGoldDragon
источник
Это хороший ответ на вопрос, почему один метод медленнее, но он не объясняет увеличение времени выполнения при нескольких запусках.
LLSv2.0
1
@ LLSv2.0 Не совсем уверен, но обновленный ответ - я все еще жду ответа от разработчиков, когда я сам поднял эту проблему здесь
OverLordGoldDragon
1
@ ga97dil Да, тогда у меня нет объяснений - попробуйте спросить на Github, хотя вы можете столкнуться с длительным временем ответа.
OverLordGoldDragon
1
@ ga97dil Действительно, TF1 может быть намного быстрее, чем TF2 - хотя TF 2.1 стоит попробовать для небольших моделей и наборов данных, так как он является самым быстрым в моих тестах (не делал прогноз). Что еще более важно, если вы когда-либо используете TF2, я настоятельно рекомендую вам проверить воспроизводимость в Graph vs. Eager; Результаты могут отличаться очень в TF 2.1.
OverLordGoldDragon
1
Я добавил ваше сообщение в ветку Git и мой пост TF2 против TF1. Спасибо, что сообщили мне, что проблема исчезла в TF 1.
OverLordGoldDragon
2

Хотя я не могу объяснить несоответствия во времени выполнения, я могу рекомендовать вам попытаться преобразовать вашу модель в 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метод, подобный модели Keras. Обратите внимание, что его необходимо модифицировать для использования с моделями, которые не имеют только один 1-D вход и 1-D выход.

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

Майкл
источник
Круто, никогда не пробовал раньше, но, может быть, это стоило того. Спасибо за подсказку!
ga97dil