Ответ 1
КРУПНЫЙ КОНТРОЛЬ: self._experimental_run_tf_function = True
. Это экспериментальный. Но это на самом деле не плохо.
Любой читатель TensorFlow, читающий: очистите свой код. Это беспорядок. И это нарушает важные методы кодирования, такие как одна функция выполняет одну вещь; _process_inputs
делает гораздо больше, чем "входы процесса", то же самое для _standardize_user_data
. "Мне не платят достаточно", но вы платите, тратя дополнительное время на то, чтобы понять свои собственные вещи, а пользователи заполняют страницу "Проблемы" ошибками, которые легче устранить с помощью более четкого кода.
РЕЗЮМЕ: это немного медленнее с compile()
.
compile()
устанавливает внутренний флаг, который назначает другую функцию прогнозирования для predict
. Эта функция создает новый граф при каждом вызове, замедляя его по сравнению с некомпилированным. Однако эта разница проявляется только тогда, когда время поезда намного меньше времени обработки данных. Если мы увеличим размер модели по крайней мере до среднего, оба станут равными. Посмотрите код внизу.
Это небольшое увеличение времени обработки данных более чем компенсируется усиленной графикой. Поскольку более эффективно хранить только один граф модели, одна прекомпиляция отбрасывается. Тем не менее: если ваша модель мала по сравнению с данными, вам лучше без compile()
для вывода модели. Смотрите мой другой ответ для обходного пути.
ЧТО ДЕЛАТЬ?
Сравните скомпилированную производительность модели с некомпилированной, как в коде внизу.
- Скомпилировано быстрее: запустите
predict
на скомпилированной модели. - Скомпилировано медленнее: запустите
predict
на некомпилированной модели.
Да, оба варианта возможны, и это будет зависеть от (1) размера данных; (2) размер модели; (3) аппаратное обеспечение. Код внизу на самом деле показывает, что скомпилированная модель работает быстрее, но 10 итераций - небольшой пример. См. "Обходные пути" в моем другом ответе для "с практическими рекомендациями".
РЕКВИЗИТЫ:
Это заняло некоторое время для отладки, но было весело. Ниже я опишу ключевых виновников, которых я обнаружил, процитирую некоторую соответствующую документацию и покажу результаты профилировщика, которые привели к окончательному узкому месту.
(FLAG == self.experimental_run_tf_function
, для краткости)
Model
по умолчанию создается с помощьюFLAG=False
.compile()
устанавливает его вTrue
.predict()
включает в себя получение функции прогнозирования,func = self._select_training_loop(x)
- Без каких-либо специальных kwargs, переданных
predict
иcompile
, все остальные флаги таковы:- (A)
FLAG==True
→func = training_v2.Loop()
- (B)
FLAG==False
→func = training_arrays.ArrayLikeTrainingLoop()
- (A)
- Из строки документации исходного кода source code docstring, (A) сильно зависит от графика, использует больше стратегии распространения, а операции создаются и создаются & уничтожение элементов графа, которые могут (могут) влиять на производительность.
Истинный виновник: _process_inputs()
, что составляет 81% времени выполнения. Его основной компонент? _create_graph_function()
, 72% времени выполнения. Этот метод даже не существует для (B). Однако при использовании модели среднего размера _process_inputs
составляет менее чем на 1% времени выполнения. Код внизу и результаты профилирования следуют.
ПРОЦЕССОРЫ ДАННЫХ:
(A): <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
, используется в _process_inputs()
. Соответствующий исходный код
(B): numpy.ndarray
, возвращено convert_eager_tensors_to_numpy
. Соответствующий исходный код и здесь
ФУНКЦИЯ ИСПОЛНЕНИЯ МОДЕЛИ (например, прогноз)
(A): функция распределения и здесь
(B): функция распределения (другая) и здесь
PROFILER: результаты для кода в моем другом ответе "крошечная модель" и в этом ответе "средняя модель":
Крошечная модель: 1000 итераций, compile()
Крошечная модель: 1000 итераций, нет compile()
Средняя модель: 10 итераций
ДОКУМЕНТАЦИЯ (косвенно) о влиянии compile()
: источника source
В отличие от других операций TensorFlow, мы не конвертируем python числовые входы в тензоры. Кроме того, новый граф генерируется для каждого отличное числовое значение питона, например, вызов
g(2)
иg(3)
создать два новых графика
function
создает отдельный граф для каждого уникального набора ввода формы и типы данных. Например, следующий фрагмент кода приведет в трех различных графиках, которые отслеживаются, так как каждый вход имеет разные формаОдин объект tf.function может потребоваться отобразить на несколько графов вычислений. под капотом. Это должно быть видно только как производительность performance (трассировка графиков имеет ненулевая вычислительная стоимость и стоимость памяти), но это не должно влиять на правильность программы
Контрпример:
from tensorflow.keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from tensorflow.keras.layers import Flatten, Dropout
from tensorflow.keras.models import Model
import numpy as np
from time import time
def timeit(func, arg, iterations):
t0 = time()
for _ in range(iterations):
func(arg)
print("%.4f sec" % (time() - t0))
batch_size = 32
batch_shape = (batch_size, 400, 16)
ipt = Input(batch_shape=batch_shape)
x = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
x = LSTM(512, activation='relu', return_sequences=True)(ipt)
x = Conv1D(128, 400, 1, padding='same')(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)
out = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)
X = np.random.randn(*batch_shape)
timeit(model.predict, X, 10)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 10)
Результаты:
34.8542 sec
34.7435 sec