Керас невідповідний час передбачення


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

Для моєї заяви мені потрібно гарантувати виконання в певний час. Однак, враховуючи таку поведінку, це неможливо. Що йде не так? Це помилка в Керасі чи помилка в тендорфлоу-бекенді?

EDIT: predict_on_batchпоказує таку ж поведінку, однак, більш рідкісна: введіть тут опис зображення

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

EDIT 2: Я перейшов на останню версію tensorflow 1 (1.15). Проблема не тільки не існує, а й «нормальний» час прогнозування значно покращився! Я не вважаю, що ці два шипи є проблематичними, оскільки вони не з'явилися, коли я повторив тест (принаймні, не за тими ж показниками та лінійно зростаючим) і є відсотковими, не такими великими, як у першому сюжеті. введіть тут опис зображення

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


Така поведінка звучить передбачувано .... збільшення начебто лінійне. Якщо ви включите цю поведінку у свій розрахунок часу, чи не піде це? --- Я не знаю, що там відбувається .... але що трапиться, якщо ви спробуєте 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)діє з допомогою свого ITS __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 ітер, замість цього спробуйте 10 000 - з часом збільшення повинно припинитися. Як ви зазначали у своїх коментарях, цього не відбувається model(x); має сенс, оскільки задіяний ще один менший крок GPU ("перетворення в набір даних").

Update2 : ви могли б помилка в УБС тут про це , якщо ви стикаєтеся це питання; в основному я там співаю


Це хороша відповідь на те, чому один метод повільніше, але він не пояснює збільшення часу виконання протягом декількох циклів.
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метод, як модель Кераса. Зауважте, що його потрібно буде модифікувати для використання з моделями, які не мають лише одного одномірного входу та одного 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.