Многие пользователи указали, что это причина перехода на Pytorch, но мне еще предстоит найти оправдание / объяснение для того, чтобы пожертвовать самым важным практическим качеством, скоростью и стремлением к исполнению.
Ниже приведена производительность бенчмаркинга кода, TF1 по сравнению с TF2, где TF1 работает на 47–276% быстрее .
Мой вопрос: что на уровне графики или аппаратного обеспечения приводит к такому значительному замедлению?
Ищу подробный ответ - уже знаком с широкими понятиями. Соответствующий Git
Спецификации : CUDA 10.0.130, cuDNN 7.4.2, Python 3.7.4, Windows 10, GTX 1070
Результаты тестов :
ОБНОВЛЕНИЕ : отключение Eager Execution согласно приведенному ниже коду не помогает. Поведение, однако, противоречиво: иногда работа в графическом режиме значительно помогает, в других случаях она работает медленнее, чем в Eager.
Поскольку разработчики TF нигде не появляются, я сам исследую этот вопрос - могу следить за прогрессом в связанной проблеме Github.
ОБНОВЛЕНИЕ 2 : тонны экспериментальных результатов, чтобы поделиться, вместе с объяснениями; должно быть сделано сегодня.
Код теста :
# use tensorflow.keras... to benchmark tf.keras; used GPU for all above benchmarks
from keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from keras.layers import Flatten, Dropout
from keras.models import Model
from keras.optimizers import Adam
import keras.backend as K
import numpy as np
from time import time
batch_shape = (32, 400, 16)
X, y = make_data(batch_shape)
model_small = make_small_model(batch_shape)
model_small.train_on_batch(X, y) # skip first iteration which builds graph
timeit(model_small.train_on_batch, 200, X, y)
K.clear_session() # in my testing, kernel was restarted instead
model_medium = make_medium_model(batch_shape)
model_medium.train_on_batch(X, y) # skip first iteration which builds graph
timeit(model_medium.train_on_batch, 10, X, y)
Используемые функции :
def timeit(func, iterations, *args):
t0 = time()
for _ in range(iterations):
func(*args)
print("Time/iter: %.4f sec" % ((time() - t0) / iterations))
def make_small_model(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = Conv1D(128, 400, strides=4, padding='same')(ipt)
x = Flatten()(x)
x = Dropout(0.5)(x)
x = Dense(64, activation='relu')(x)
out = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)
model.compile(Adam(lr=1e-4), 'binary_crossentropy')
return model
def make_medium_model(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
x = LSTM(512, activation='relu', return_sequences=True)(x)
x = Conv1D(128, 400, strides=4, 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)
model.compile(Adam(lr=1e-4), 'binary_crossentropy')
return model
def make_data(batch_shape):
return np.random.randn(*batch_shape), np.random.randint(0, 2, (batch_shape[0], 1))
источник
Ответы:
ОБНОВЛЕНИЕ 18/02/2020 : я на скамейке 2,1 и 2,1 ночи; результаты смешанные. Все конфиги, кроме одного (модель и размер данных), работают так же быстро или намного быстрее, чем лучшие из TF2 и TF1. Тот, который медленнее и медленнее, это Large-Large - esp. в исполнении графика (от 1,6x до 2,5x медленнее ).
Кроме того, существуют огромные различия в воспроизводимости между Graph и Eager для большой модели, которую я тестировал, - которую нельзя объяснить случайностью / вычислительным параллелизмом. В настоящее время я не могу представить воспроизводимый код для этих утверждений в зависимости от времени, поэтому я настоятельно рекомендую протестировать его для своих собственных моделей.
Еще не открыли Git-проблему, но я прокомментировал оригинал - пока нет ответа. Я обновлю ответ (ы), как только прогресс будет достигнут.
Вердикт : нет , если вы знаете, что делаете. Но если вы этого не сделаете , это может стоить вам очень дорого - в среднем на несколько обновлений графического процессора и в худшем случае на несколько графических процессоров.
НАСТОЯЩИЙ ОТВЕТ : имеет целью дать общее описание проблемы, а также рекомендации о том, как выбрать конфигурацию обучения, соответствующую вашим потребностям. Подробное низкоуровневое описание, включающее все результаты сравнительного анализа + используемый код, см. В моем другом ответе.
Я буду обновлять свой ответ (ы) с дополнительной информацией, если я узнаю что-нибудь - можете добавить / пометить этот вопрос для справки.
РЕЗЮМЕ ВЫПУСКА : как подтвердил разработчик TensorFlow Q. Скотт Чжу, TF2 сфокусировал разработку на стремлении к исполнению и тесной интеграции с Keras, что включало быстрые изменения в источнике TF, в том числе на уровне графов. Преимущества: значительно расширенные возможности обработки, распространения, отладки и развертывания. Стоимость некоторых из них, однако, скорость.
Дело, однако, довольно сложнее. Это не только TF1 против TF2 - факторы, приводящие к значительным различиям в скорости поезда, включают в себя:
keras
противtf.keras
numpy
противtf.data.Dataset
против ...train_on_batch()
противfit()
model(x)
противmodel.predict(x)
против ...К сожалению, почти ничего из вышеперечисленного не зависит от другого, и каждый может по крайней мере удвоить время выполнения относительно другого. К счастью, вы можете определить, что будет работать лучше систематически и с помощью нескольких ярлыков - как я покажу.
ЧТО МНЕ ДЕЛАТЬ? В настоящее время единственный способ - провести эксперимент для конкретной модели, данных и оборудования. Ни одна отдельная конфигурация не всегда будет работать лучше, но есть и то, что нужно, а что нет, чтобы упростить поиск:
>> DO:
train_on_batch()
+numpy
+tf.keras
+ TF1 + Eager / Graphtrain_on_batch()
+numpy
+tf.keras
+ TF2 + Графикfit()
+numpy
+tf.keras
+ TF1 / TF2 + График + большая модель и данные>> НЕ
fit()
+numpy
+keras
для малых и средних моделей и данныхfit()
+numpy
+tf.keras
+ TF1 / TF2 + Eagertrain_on_batch()
+numpy
+keras
+ TF1 + Eager[Major]
tf.python.keras
; он может работать в 10-100 раз медленнее и с большим количеством ошибок; больше информацииlayers
,models
,optimizers
, и связанные с «вне коробки» импорта использования; Операции, утилиты и связанные с ними «частные» импорта - это хорошо, но, чтобы быть уверенным, проверьте наличие альтернатив и убедитесь, что они используются вtf.keras
Обратитесь к коду внизу моего другого ответа для примера настройки бенчмаркинга. Приведенный выше список основан главным образом на таблицах «ЭТАЛОНОВ» в другом ответе.
ОГРАНИЧЕНИЯ вышеупомянутых ДЕЙСТВИЙ И НЕ:
Conv1D
иDense
- без RNN, разреженных данных / целей, входов 4 / 5D и других настроекnumpy
иtf.data.Dataset
, в то время как существует много других форматов; см другой ответПочему TF2 пожертвовал самым практичным качеством, скоростью и энергичным исполнением? Понятно, что нет - график все еще доступен. Но если вопрос «зачем вообще стремиться»:
.__dict__
. График, напротив, требует знакомства со специальными внутренними функциями, что значительно усложняет весь процесс отладки и самоанализа.КАК ВКЛЮЧИТЬ / ОТКЛЮЧИТЬ EAGER?
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ :
_on_batch()
методами в TF2; согласно разработчикам TF, они все еще используют более медленную реализацию, но не намеренно - то есть это должно быть исправлено. Смотрите другой ответ для деталей.ПРОСИТ ТЕНСОРФУЛЕВЫЕ УСТРОЙСТВА :
train_on_batch()
и аспект производительности, вызываяfit()
итеративно; пользовательские петли поезда важны для многих, особенно для меня.БЛАГОДАРНОСТИ : Спасибо
ОБНОВЛЕНИЯ :
14.11.19 - нашел модель (в моем реальном приложении), которая работает медленнее на TF2 для всех * конфигураций с входными данными Numpy. Различия составляли 13-19%, в среднем 17%. Различия между
keras
иtf.keras
, тем не менее, были более резкими: 18-40% , в среднем. 32% (оба TF1 и 2). (* - кроме Eager, для которого TF2 OOM'd)17.11.19 - разработчики обновили
on_batch()
методы в последнем коммите , заявив, что они улучшили скорость - будут выпущены в TF 2.1 или доступны сейчас какtf-nightly
. Поскольку я не могу запустить последний, откладываю скамейку до 2.1.источник
fit_generator
? ... Я практически никогда не хочу,train_on_batch
и управление собственным циклом обучения между партиями является огромным, огромным препятствием, которого следует избегать даже при больших затратах.fit
небольшой дополнительной обработки данных. Что касается железнодорожных циклов, я написал свой собственный, который в конечном итоге превратился в своего рода API;fit_generator
не хватает самоанализа, настраиваемости и сохранения / загрузки - так что для меня это абсолютный нету. Со временем я опубликую свой учебный цикл на Github.fit_generator
вашим приложением - это проверить.ЭТОТ ОТВЕТ : стремится предоставить подробное, графическое / аппаратное описание проблемы - включая циклы поезда TF2 против TF1, процессоры ввода данных и выполнения в режиме Eager vs. Graph. Сводка проблемы и рекомендации по ее решению см. В моем другом ответе.
РЕЗУЛЬТАТ ПРОИЗВОДИТЕЛЬНОСТИ : иногда один быстрее, иногда другой, в зависимости от конфигурации. Что касается TF2 против TF1, то в среднем они примерно на одном уровне, но существенные различия на основе конфигурации существуют, и TF1 превосходит TF2 чаще, чем наоборот. Смотрите «СРАВНЕНИЕ» ниже.
EAGER VS. GRAPH : смысл всего этого ответа для некоторых: согласно моему тестированию, TF2 медленнее, чем TF1. Подробности дальше вниз.
Принципиальное различие между ними заключается в следующем: Graph активно настраивает вычислительную сеть и выполняет ее, когда «ей говорят», тогда как Eager выполняет все при создании. Но история только начинается здесь:
Eager НЕ лишен Graph , и на самом деле он может быть главным образом Graph, вопреки ожиданиям. То, чем он в значительной степени является, является выполненным графом - это включает в себя веса модели и оптимизатора, составляющие большую часть графика.
Eager перестраивает часть собственного графа при исполнении ; Непосредственное следствие того, что Graph не полностью построен - смотрите результаты профилировщика. Это накладные расходы.
Eager медленнее с Numpy входами ; согласно этому Git-комментарию и коду, входные данные Numpy в Eager включают накладные расходы на копирование тензоров из CPU в GPU. Проходя через исходный код, различия в обработке данных очевидны; Eager напрямую передает Numpy, а Graph передает тензоры, которые затем оцениваются Numpy; не уверен в точном процессе, но последний должен включать оптимизацию на уровне GPU
TF2 Eager медленнее, чем TF1 Eager - это ... неожиданно. Смотрите результаты сравнительного анализа ниже. Различия варьируются от незначительных до значительных, но они последовательны. Не уверен, почему это так - если разработчик TF уточнит, обновит ответ.
TF2 против TF1 : цитирование соответствующих частей разработчика TF, Q. Scott Zhu's, ответ - с моим акцентом и переписыванием:
С последним предложением последнего абзаца выше и последним пунктом нижеследующего абзаца:
Я не согласен - согласно моим результатам профилирования, которые показывают, что обработка входных данных в Eager значительно медленнее, чем в Graph. Кроме того, не уверены,
tf.data.Dataset
в частности, но Eager неоднократно вызывает несколько одинаковых методов преобразования данных - см. Профилировщик.И, наконец, связанный коммит dev: значительное количество изменений для поддержки циклов Keras v2 .
Петли поезда : в зависимости от (1) Eager vs. Graph; (2) формат входных данных, в которых обучение будет продолжаться по отдельному циклу поезда - в TF2
_select_training_loop()
, training.py , один из:Каждый из них обрабатывает распределение ресурсов по-разному и влияет на производительность и возможности.
Петли поезда:
fit
противtrain_on_batch
,keras
противtf.keras
: каждая из четырех использует разные петли поезда, хотя, возможно, не во всех возможных комбинациях.keras
'fit
, например, использует формуfit_loop
, напримерtraining_arrays.fit_loop()
, и ееtrain_on_batch
можно использоватьK.function()
.tf.keras
имеет более сложную иерархию, частично описанную в предыдущем разделе.Обучающие циклы: документация - соответствующая исходная документация по некоторым различным методам выполнения:
Процессоры ввода данных : аналогично вышеописанному, процессор выбирается индивидуально в зависимости от внутренних флагов, установленных в соответствии с конфигурациями времени выполнения (режим выполнения, формат данных, стратегия распределения). Самый простой случай с Eager, который работает напрямую с массивами Numpy. Для некоторых конкретных примеров, смотрите этот ответ .
РАЗМЕР МОДЕЛИ, РАЗМЕР ДАННЫХ:
convert_to_tensor
в зависимости от времени преобразования данных (см. «ПРОФИЛЬЕР»).ЭТАЛОНЫ : измельченное мясо. - Документ Word - Электронная таблица Excel
Терминология :
(1 - longer_time / shorter_time)*100
; обоснование: нас интересует , какой фактор один быстрее другого;shorter / longer
на самом деле нелинейное отношение, не полезно для прямого сравнения+
если TF2 быстрее+
если график быстрееPROFILER :
PROFILER - Объяснение : Spyder 3.3.6 IDE профилировщик.
Некоторые функции повторяются в гнездах других; следовательно, трудно отследить точное разделение между функциями «обработки данных» и «обучения», поэтому будет некоторое совпадение - как это выражено в самом последнем результате.
% вычисленных значений во время выполнения минус время сборки
_func = func
будут профилироваться какfunc
), которые смешиваются во время сборки - следовательно, необходимо исключить этоИСПЫТАТЕЛЬНАЯ СРЕДА :
МЕТОДОЛОГИЯ :
batch_size
иnum_channels
Conv1D
,Dense
«Обучающиеся» слои; RNN, которых избегают для каждой реализации TF. различияlayers.Embedding()
) или разреженные цели (например,SparseCategoricalCrossEntropy()
ОГРАНИЧЕНИЯ : «полный» ответ объяснил бы каждый возможный цикл поезда и итератор, но это, безусловно, выходит за рамки моих временных возможностей, несуществующей зарплаты или общей необходимости. Результаты так же хороши, как методология - интерпретировать с открытым разумом.
КОД :
источник
model.compile
безrun_eagerly=True
аргументов. В активном режиме вы можете запустить часть своего кода в графическом режиме, используяtf.function
. Поэтому я думаю, что реализация по умолчаниюcompile
состоит в том, чтобы создавать вычислительный граф вместо того, чтобы активно его запускать из соображений производительности. Также обратите внимание, что если ваша модель является сверточной, то вы не увидите ускорения в графическом режиме, поскольку взаимодействие с Python минимально. Если вы выполняете много математических операций, это может иметь большое значение (также в использовании памяти).model.compile
безrun_eagerly=True
обеспечения режима графика, или нет?model.compile
илиmodel.fit
должен гарантировать , что обучение проходит в графическом режиме внутри.run_eagerly=True
в качестве параметра для компиляции». (source tenorflow.org/guide/keras/overview ) Поэтому я, если вы не пройдетеrun_eagerly=True
модель, МОЖЕТ работать в графическом режиме. Я не уверен, что является решающим фактором, но почему он не работает в графическом режиме, если он более эффективен, чем стремиться.