UPDATE - 1/15/2020 : поточна краща практика для невеликих обсягів партій повинні годувати входи моделі безпосередньо - тобто preds = model(x)
, і якщо шари поводяться по- різному на потяги / виводу, model(x, training=False)
. За останнім зобов'язанням, це зараз задокументовано .
Я цього не оцінював, але в рамках дискусії Git також варто спробувати, predict_on_batch()
особливо з покращенням TF 2.1.
ULTIMATE Винуватець : 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)
- Без будь-яких спеціальних кваргів, переданих до
predict
та compile
, всі інші прапори такі:
- (А)
FLAG==True
->func = training_v2.Loop()
- (В)
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
- З вихідного коду рядка документації , (А) в значній мірі залежить граф-, використовує більше стратегії розподілу і ОПС схильні до створення і руйнувати елементи графа, які «можуть» (DO) впливає на продуктивність.
Справжній винуватець : _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) : функція розподілу (різні) , і тут
ПРОФІЛЕР : результати для коду в іншій моїй відповіді, "крихітна модель", і в цій відповіді "середня модель":
Крихітна модель : 1000 ітерацій,compile()
Крихітна модель : 1000 ітерацій, ні compile()
Середня модель : 10 ітерацій
ДОКУМЕНТАЦІЯ (опосередковано) на вплив compile()
: джерела
На відміну від інших операцій TensorFlow, ми не перетворюємо числові входи python в тензори. Більше того, для кожного окремого числового значення пітона формується новий графік , наприклад виклик g(2)
і g(3)
генерує два нові графіки
function
створює окремий графік для кожного унікального набору вхідних форм і типів даних . Наприклад, наступний фрагмент коду призведе до відстеження трьох різних графіків, оскільки кожен вхід має різну форму
Одному об'єкту tf.function може знадобитися зіставити декілька обчислювальних графіків під кришкою. Це повинно бути видно лише як продуктивність (графіки відстеження мають ненульову обчислювальну та пам’ятну вартість ), але не повинна впливати на правильність програми
КОНТРОЛЬНИЙ ПРИКЛАД :
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