BU CEVAP : TF2 ve TF1 tren döngüleri, giriş veri işlemcileri ve Eager - Grafik modu uygulamaları dahil olmak üzere, sorunun ayrıntılı, grafik / donanım düzeyinde bir açıklamasını sağlamayı amaçlamaktadır. Sorun özeti ve çözüm yönergeleri için diğer cevabıma bakın.
PERFORMANS KARARI : yapılandırmaya bağlı olarak bazen biri daha hızlı, bazen diğeri daha hızlıdır. TF2'ye karşı TF1'e gelince, ortalama olarak eşitler, ancak önemli konfigürasyon tabanlı farklılıklar var ve TF1, TF2'yi tersine göre daha sık koyar. Aşağıdaki "BENCHMARKING" bölümüne bakın.
EAGER VS. GRAFİK : Bazıları için tüm bu cevabın eti: TF2'nin istekli TF1'lerden daha yavaş , benim testime göre. Ayrıntılar daha da aşağı.
Bu ikisi arasındaki temel fark şudur: Grafik proaktif olarak bir hesaplama ağı kurar ve 'söylendiğinde' yürütülür - oysa Eager yaratılış üzerine her şeyi yürütür. Ancak hikaye sadece burada başlıyor:
Istekli Grafikten yoksun değildir ve aslında beklentilerin aksine çoğunlukla Grafik olabilir . Büyük ölçüde nedir, yürütülür Grafik - bu, grafiğin büyük bir bölümünü içeren model ve optimize edici ağırlıkları içerir.
Istekli yürütme sırasında kendi grafiğin bir bölümünü yeniden oluşturur ; Grafiğin tam olarak oluşturulmamasının doğrudan sonucu - profiler sonuçlarına bakın. Bunun hesaplama yükü var.
İstekli Numpy girişleri ile daha yavaştır ; başına bu Git açıklama ve kod, istekli olarak Numpy girişleri CPU'dan GPU'ya tansörlerine kopyalama genel maliyet içerir. Kaynak kodu ile adım adım veri işleme farklılıkları açıktır; Eager doğrudan Numpy'yi geçerken Graph, daha sonra Numpy'yi değerlendiren tensörleri geçer; kesin işlemden emin değilsiniz ancak ikincisi GPU düzeyinde optimizasyonları içermelidir
TF2 Eager, TF1 Eager'den daha yavaş - bu ... beklenmedik. Aşağıdaki kıyaslama sonuçlarına bakın. Farklılıklar ihmal edilebilirden anlamlıya uzanır, ancak tutarlıdır. Durumun neden olduğundan emin değilsiniz - eğer bir TF geliştiricisi açıklarsa cevabı güncelleyecektir.
TF2 vs TF1 : Bir TF dev en, S. Scott Zhu'nun, ilgili bölümlerini alıntı tepki - w / my vurgu & rewording bit:
İstekli olarak, çalışma zamanının op'ları yürütmesi ve her python kodu satırı için sayısal değeri döndürmesi gerekir. Tek adımlı yürütmenin doğası yavaş olmasına neden olur .
TF2'de Keras, eğitim, değerlendirme ve tahmin için grafiğini oluşturmak üzere tf.fonksiyonundan yararlanır. Onlara model için "yürütme işlevi" diyoruz. TF1'de "yürütme işlevi", bazı ortak bileşenleri TF işlevi olarak paylaşan ancak farklı bir uygulamaya sahip olan bir FuncGraph'dı.
İşlem sırasında, bir şekilde train_on_batch (), test_on_batch () ve predict_on_batch () için yanlış bir uygulama bıraktık . Hala sayısal olarak doğrudurlar , ancak x_on_batch için yürütme işlevi, tf.function ile sarılmış bir python işlevi yerine saf bir python işlevidir. Bu yavaşlığa neden olur
TF2'de, tüm giriş verilerini bir tek giriş tipini işlemek için yürütme fonksiyonumuzu birleştirebileceğimiz bir tf.data.Dataset'e dönüştürürüz. Veri kümesi dönüşümünde bazı ek yükler olabilir ve bunun bir toplu işlem maliyeti yerine bir kerelik yalnızca ek yük olduğunu düşünüyorum
Yukarıdaki son paragrafın son cümlesi ve aşağıdaki paragrafın son cümlesi ile:
İstekli moddaki yavaşlığın üstesinden gelmek için, bir python işlevini grafiğe dönüştürecek @ tf.function var. Np dizisi gibi sayısal veri değeri beslendiğinde, tf.function işlevinin gövdesi, optimize edilen statik grafiğe dönüştürülür ve hızlı olan ve TF1 grafik modu ile benzer performansa sahip olması gereken son değeri döndürür.
Kabul etmiyorum - Eager'in girdi veri işlemesinin Graph'lardan çok daha yavaş olduğunu gösteren profil oluşturma sonuçlarıma göre. Ayrıca, tf.data.Dataset
özellikle emin değilsiniz, ancak Eager tekrar tekrar aynı veri dönüştürme yöntemlerinin birden çoğunu çağırır - bkz profiler.
Son olarak, dev'in bağlantılı taahhüdü: Keras v2 döngülerini desteklemek için önemli sayıda değişiklik .
Tren Döngüsü : (1) Eager - Graph; (2) giriş veri formatı, eğitim farklı bir tren döngüsüyle devam edecektir - TF2'de _select_training_loop()
, training.py , şunlardan biri:
training_v2.Loop()
training_distributed.DistributionMultiWorkerTrainingLoop(
training_v2.Loop()) # multi-worker mode
# Case 1: distribution strategy
training_distributed.DistributionMultiWorkerTrainingLoop(
training_distributed.DistributionSingleWorkerTrainingLoop())
# Case 2: generator-like. Input is Python generator, or Sequence object,
# or a non-distributed Dataset or iterator in eager execution.
training_generator.GeneratorOrSequenceTrainingLoop()
training_generator.EagerDatasetOrIteratorTrainingLoop()
# Case 3: Symbolic tensors or Numpy array-like. This includes Datasets and iterators
# in graph mode (since they generate symbolic tensors).
training_generator.GeneratorLikeTrainingLoop() # Eager
training_arrays.ArrayLikeTrainingLoop() # Graph
Her biri kaynak tahsisini farklı şekilde ele alır ve performans ve yetenek üzerinde sonuçlar doğurur.
Tren Döngüsü: fit
vs train_on_batch
, keras
vstf.keras
.: her dört kombinasyon da farklı tren döngülerini kullanır, ancak her olası kombinasyonda olmayabilir. keras
' fit
, Örneğin, bir formu kullanır fit_loop
, örneğin, training_arrays.fit_loop()
ve onun train_on_batch
kullanabilir K.function()
. tf.keras
önceki bölümde kısmen açıklanan daha karmaşık bir hiyerarşiye sahiptir.
Train Loops: belgeler - farklı yürütme yöntemlerinden bazıları hakkında ilgili kaynak öğretimi :
Diğer TensorFlow işlemlerinden farklı olarak, python sayısal girişlerini tensörlere dönüştürmüyoruz. Ayrıca, her farklı python sayısal değeri için yeni bir grafik oluşturulur
function
Her benzersiz giriş şekli ve veri türü kümesi için ayrı bir grafik başlatır .
Tek bir tf.function nesnesinin kaputun altındaki birden çok hesaplama grafiğine eşlenmesi gerekebilir. Bu yalnızca performans olarak görünür olmalıdır (izleme grafiklerinde sıfır dışında bir hesaplama ve bellek maliyeti vardır )
Giriş veri işlemcileri : yukarıdakine benzer şekilde, çalışma zamanı yapılandırmalarına (yürütme modu, veri biçimi, dağıtım stratejisi) göre ayarlanan dahili bayraklara bağlı olarak işlemci duruma göre seçilir. En basit durum, Numpy dizileriyle doğrudan çalışan Eager ile. Bazı özel örnekler için bu cevaba bakınız .
MODEL BOYUTU, VERİ BOYUTU:
- Belirleyicidir; tek bir yapılandırma tüm model ve veri boyutlarının üstüne çıkmadı.
- Model boyutuna göre veri boyutu önemlidir; küçük veri ve model için veri aktarımı (örn. CPU'dan GPU'ya) ek yüke hakim olabilir. Benzer şekilde, küçük havai işlemciler, veri dönüştürme süresi baskın olan büyük veriler üzerinde daha yavaş çalışabilir (bkz
convert_to_tensor
. "PROFILER")
- Hız, tren döngülerine ve giriş veri işlemcilerinin kaynakları idare etmenin farklı araçlarına göre değişir.
BENCHMARKS : öğütülmüş et. - Word Belgesi - Excel Elektronik Tablosu
Terminoloji :
- % -less sayıların tümü saniyelerdir
- % olarak hesaplanır
(1 - longer_time / shorter_time)*100
; gerekçe: Birinin diğerinden daha hızlı olduğu faktörle ilgileniyoruz ; shorter / longer
aslında doğrusal olmayan bir ilişkidir, doğrudan karşılaştırma için yararlı değildir
- % işaretinin belirlenmesi:
- TF2 ve TF1:
+
TF2 daha hızlıysa
- GvE (Grafik ve Eager):
+
Grafik daha hızlıysa
- TF2 = TensorFlow 2.0.0 + Keras 2.3.1; TF1 = Tensör Akışı 1.14.0 + Keras 2.2.5
PROFİL :
PROFILER - Açıklama : Spyder 3.3.6 IDE profiler.
Bazı fonksiyonlar diğerlerinin yuvalarında tekrarlanır; bu nedenle, "veri işleme" ve "eğitim" işlevleri arasındaki kesin ayrımı izlemek zordur, bu nedenle en son sonuçta belirtildiği gibi bir miktar çakışma olacaktır.
çalışma zamanı eksi derleme süresinden hesaplanan% rakamları
- 1 veya 2 kez olarak adlandırılan tüm (benzersiz) çalışma zamanlarının toplanmasıyla hesaplanan oluşturma süresi
- # Yineleme sayısıyla aynı kez adlandırılan tüm (benzersiz) çalışma zamanlarını ve yuvalarının çalışma zamanlarının bazılarını toplayarak hesaplanan tren zamanı
- İşlevler , maalesef , oluşturma süresinde karışan orijinal adlarına göre profillenir (yani
_func = func
profil olarak gönderilir func
) - bu nedenle hariç tutma ihtiyacı
TEST ORTAMI :
- En az arka plan görevi çalışarak alt kısımda yürütülen kod
- GPU, bu yayında önerildiği gibi, zamanlama tekrarlarından önce birkaç tekrarlamayla "ısındı"
- CUDA 10.0.130, cuDNN 7.6.0, TensorFlow 1.14.0 ve kaynaktan oluşturulan TensorFlow 2.0.0 ve Anaconda
- Python 3.7.4, Spyder 3.3.6 IDE
- GTX 1070, Windows 10, 24 GB DDR4 2,4 MHz RAM, i7-7700HQ 2,8 GHz CPU
METODOLOJİ :
- Karşılaştırma ölçütü 'küçük', 'orta' ve 'büyük' model ve veri boyutları
- Giriş veri boyutundan bağımsız olarak her model boyutu için parametre sayısını düzeltin
- "Daha büyük" modelde daha fazla parametre ve katman vardır
- "Daha büyük" veriler daha uzun bir diziye sahiptir, ancak aynı
batch_size
venum_channels
- Modeller sadece kullanım
Conv1D
, Dense
'öğrenilebilir' katmanları; TF versiyon implanna göre RNN'lerden kaçınıldı. farklar
- Model ve optimize edici grafik oluşturmayı atlamak için her zaman kıyaslama döngüsünün dışında bir tren uyumu gerçekleştirdi
- Seyrek veriler (ör.
layers.Embedding()
) Veya seyrek hedefler (ör.SparseCategoricalCrossEntropy()
SINIRLAMALAR : "tam" bir cevap, olası her tren döngüsünü ve yineleyiciyi açıklayacaktır, ancak bu kesinlikle benim zaman yeteneğimin, var olmayan maaş kontrolünün veya genel zorunluluğun ötesinde. Sonuçlar sadece metodoloji kadar iyidir - açık bir zihinle yorumlayın.
KOD :
import numpy as np
import tensorflow as tf
import random
from termcolor import cprint
from time import time
from tensorflow.keras.layers import Input, Dense, Conv1D
from tensorflow.keras.layers import Dropout, GlobalAveragePooling1D
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
import tensorflow.keras.backend as K
#from keras.layers import Input, Dense, Conv1D
#from keras.layers import Dropout, GlobalAveragePooling1D
#from keras.models import Model
#from keras.optimizers import Adam
#import keras.backend as K
#tf.compat.v1.disable_eager_execution()
#tf.enable_eager_execution()
def reset_seeds(reset_graph_with_backend=None, verbose=1):
if reset_graph_with_backend is not None:
K = reset_graph_with_backend
K.clear_session()
tf.compat.v1.reset_default_graph()
if verbose:
print("KERAS AND TENSORFLOW GRAPHS RESET")
np.random.seed(1)
random.seed(2)
if tf.__version__[0] == '2':
tf.random.set_seed(3)
else:
tf.set_random_seed(3)
if verbose:
print("RANDOM SEEDS RESET")
print("TF version: {}".format(tf.__version__))
reset_seeds()
def timeit(func, iterations, *args, _verbose=0, **kwargs):
t0 = time()
for _ in range(iterations):
func(*args, **kwargs)
print(end='.'*int(_verbose))
print("Time/iter: %.4f sec" % ((time() - t0) / iterations))
def make_model_small(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = Conv1D(128, 40, strides=4, padding='same')(ipt)
x = GlobalAveragePooling1D()(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_model_medium(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = ipt
for filters in [64, 128, 256, 256, 128, 64]:
x = Conv1D(filters, 20, strides=1, padding='valid')(x)
x = GlobalAveragePooling1D()(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_model_large(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = Conv1D(64, 400, strides=4, padding='valid')(ipt)
x = Conv1D(128, 200, strides=1, padding='valid')(x)
for _ in range(40):
x = Conv1D(256, 12, strides=1, padding='same')(x)
x = Conv1D(512, 20, strides=2, padding='valid')(x)
x = Conv1D(1028, 10, strides=2, padding='valid')(x)
x = Conv1D(256, 1, strides=1, padding='valid')(x)
x = GlobalAveragePooling1D()(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))
def make_data_tf(batch_shape, n_batches, iters):
data = np.random.randn(n_batches, *batch_shape),
trgt = np.random.randint(0, 2, (n_batches, batch_shape[0], 1))
return tf.data.Dataset.from_tensor_slices((data, trgt))#.repeat(iters)
batch_shape_small = (32, 140, 30)
batch_shape_medium = (32, 1400, 30)
batch_shape_large = (32, 14000, 30)
batch_shapes = batch_shape_small, batch_shape_medium, batch_shape_large
make_model_fns = make_model_small, make_model_medium, make_model_large
iterations = [200, 100, 50]
shape_names = ["Small data", "Medium data", "Large data"]
model_names = ["Small model", "Medium model", "Large model"]
def test_all(fit=False, tf_dataset=False):
for model_fn, model_name, iters in zip(make_model_fns, model_names, iterations):
for batch_shape, shape_name in zip(batch_shapes, shape_names):
if (model_fn is make_model_large) and (batch_shape is batch_shape_small):
continue
reset_seeds(reset_graph_with_backend=K)
if tf_dataset:
data = make_data_tf(batch_shape, iters, iters)
else:
data = make_data(batch_shape)
model = model_fn(batch_shape)
if fit:
if tf_dataset:
model.train_on_batch(data.take(1))
t0 = time()
model.fit(data, steps_per_epoch=iters)
print("Time/iter: %.4f sec" % ((time() - t0) / iters))
else:
model.train_on_batch(*data)
timeit(model.fit, iters, *data, _verbose=1, verbose=0)
else:
model.train_on_batch(*data)
timeit(model.train_on_batch, iters, *data, _verbose=1)
cprint(">> {}, {} done <<\n".format(model_name, shape_name), 'blue')
del model
test_all(fit=True, tf_dataset=False)