Керас противоречивый прогноз времени - PullRequest
17 голосов
/ 17 февраля 2020

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

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

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

enter image description here

Я использую:

tensorflow 2.0.0
python 3.7.4

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

EDIT: predict_on_batch показывает то же поведение, но более разреженное: enter image description here

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

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

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

Ответы [ 2 ]

10 голосов
/ 19 февраля 2020

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

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

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


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

Update2 : вы можете сообщить об этом разработчикам здесь , если столкнетесь с этой проблемой ; там в основном я пою

2 голосов
/ 11 марта 2020

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

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-tenorflow-lite-to-ускорение-предсказания-a3954886eb98

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...