Керас несовместимое время предсказания
Я попытался оценить время предсказания моей модели 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')
])
Время прогнозирования для отдельных записей было:
model.predict(input)
: 18 мсmodel(input)
: 1,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