Es wurde von vielen Benutzern als Grund für den Wechsel zu Pytorch angeführt, aber ich habe noch keine Rechtfertigung / Erklärung dafür gefunden, die wichtigste praktische Qualität, die Geschwindigkeit, für die eifrige Ausführung zu opfern.
Nachfolgend finden Sie die Code-Benchmarking-Leistung TF1 vs. TF2 - wobei TF1 zwischen 47% und 276% schneller läuft .
Meine Frage ist: Was führt auf Grafik- oder Hardwareebene zu einer so deutlichen Verlangsamung?
Auf der Suche nach einer detaillierten Antwort - bin bereits mit umfassenden Konzepten vertraut. Relevante Git
Specs : CUDA 10.0.130, cuDNN 7.4.2, Python 3.7.4, 10 Windows 1070 GTX
Benchmark-Ergebnisse :
UPDATE : Das Deaktivieren der Eager-Ausführung gemäß dem folgenden Code hilft nicht . Das Verhalten ist jedoch inkonsistent: Manchmal hilft das Ausführen im Grafikmodus erheblich, manchmal läuft es im Vergleich zu Eager langsamer .
Da TF-Entwickler nirgendwo auftauchen, werde ich diese Angelegenheit selbst untersuchen - kann den Fortschritt in der verknüpften Github-Ausgabe verfolgen.
UPDATE 2 : Tonnen von experimentellen Ergebnissen, die zusammen mit Erklärungen geteilt werden müssen; sollte heute gemacht werden.
Benchmark-Code :
# 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)
Verwendete Funktionen :
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))
quelle
Antworten:
UPDATE 18.02.2020 : Ich habe 2.1 und 2.1 pro Nacht auf die Bank gesetzt . Die Ergebnisse sind gemischt. Alle bis auf eine Konfiguration (Modell- und Datengröße) sind so schnell oder viel schneller als die besten von TF2 und TF1. Derjenige, der langsamer und dramatisch langsamer ist, ist Large-Large - insb. in der Grafikausführung ( 1,6x bis 2,5x langsamer ).
Darüber hinaus gibt es extreme Unterschiede in der Reproduzierbarkeit zwischen Graph und Eager für ein großes Modell, das ich getestet habe - eines, das nicht durch Zufälligkeit / Rechenparallelität erklärt werden kann. Ich kann derzeit keinen reproduzierbaren Code für diese Ansprüche pro Zeitbeschränkung präsentieren. Daher empfehle ich dringend, diesen für Ihre eigenen Modelle zu testen.
Ich habe noch keine Git-Ausgabe zu diesen Themen geöffnet, aber ich habe das Original kommentiert - noch keine Antwort. Ich werde die Antwort (en) aktualisieren, sobald Fortschritte erzielt wurden.
VERDICT : Ist es nicht , wenn Sie wissen, was Sie tun. Wenn Sie dies nicht tun , könnte es Sie viel kosten - im Durchschnitt durch ein paar GPU-Upgrades und im schlimmsten Fall durch mehrere GPUs.
DIESE ANTWORT : Ziel ist es, eine allgemeine Beschreibung des Problems sowie Richtlinien für die Entscheidung über die für Ihre Anforderungen spezifische Schulungskonfiguration bereitzustellen. Eine detaillierte Beschreibung auf niedriger Ebene, die alle Benchmarking-Ergebnisse + den verwendeten Code enthält, finden Sie in meiner anderen Antwort.
Ich werde meine Antwort (en) mit weiteren Informationen aktualisieren, wenn ich etwas erfahre - kann diese Frage als Referenz mit einem Lesezeichen versehen / "markieren".
ZUSAMMENFASSUNG DER AUSGABE : Wie von einem TensorFlow-Entwickler, Q. Scott Zhu, bestätigt , konzentrierte sich TF2 auf die eifrige Ausführung und enge Integration mit Keras, was umfassende Änderungen der TF-Quelle beinhaltete - auch auf Grafikebene. Vorteile: Stark erweiterte Verarbeitungs-, Verteilungs-, Debug- und Bereitstellungsfunktionen. Die Kosten für einige davon sind jedoch Geschwindigkeit.
Die Angelegenheit ist jedoch ziemlich komplex. Es ist nicht nur TF1 vs. TF2 - Faktoren, die zu signifikanten Unterschieden in der Zuggeschwindigkeit führen, sind:
keras
vs.tf.keras
numpy
vs.tf.data.Dataset
vs. ...train_on_batch()
vs.fit()
model(x)
vs.model.predict(x)
vs. ...Leider ist fast keiner der oben genannten Punkte unabhängig vom anderen, und jeder kann die Ausführungszeit im Vergleich zum anderen mindestens verdoppeln. Glücklicherweise können Sie systematisch und mit ein paar Verknüpfungen bestimmen, was am besten funktioniert - wie ich zeigen werde.
WAS SOLLTE ICH TUN? Derzeit ist der einzige Weg - Experimentieren Sie für Ihr spezifisches Modell, Ihre Daten und Ihre Hardware. Keine einzelne Konfiguration funktioniert immer am besten - aber es gibt Vor- und Nachteile, um Ihre Suche zu vereinfachen:
>> DO:
train_on_batch()
+numpy
+tf.keras
+ TF1 + Eifrig / Grafiktrain_on_batch()
+numpy
+tf.keras
+ TF2 + Graphfit()
+numpy
+tf.keras
+ TF1 / TF2 + Grafik + großes Modell & Daten>> NICHT:
fit()
+numpy
+keras
Für kleine und mittlere Modelle und Datenfit()
+numpy
+tf.keras
+ TF1 / TF2 + Eifrigtrain_on_batch()
+numpy
+keras
+ TF1 + Eifrig[Major]
tf.python.keras
; es kann 10-100x langsamer laufen und mit vielen Fehlern; Mehr Infolayers
,models
,optimizers
, und in Verbindung stehenden "out-of-box" usage Einfuhren; Ops, Utils und verwandte "private" Importe sind in Ordnung - aber um sicherzugehen, prüfen Sie, ob Alts vorhanden sind und ob sie in verwendet werdentf.keras
Ein Beispiel für ein Benchmarking-Setup finden Sie im Code unten in meiner anderen Antwort. Die obige Liste basiert hauptsächlich auf den "BENCHMARKS" -Tabellen in der anderen Antwort.
EINSCHRÄNKUNGEN der oben genannten DO's & DON'T's:
Conv1D
undDense
- keine RNNs, spärliche Daten / Ziele, 4 / 5D-Eingänge und andere Konfigurationennumpy
undtf.data.Dataset
, während viele andere Formate existieren; siehe andere AntwortWarum hat TF2 die praktischste Qualität, die Geschwindigkeit, für eine eifrige Ausführung geopfert? Es ist nicht klar - Grafik ist noch verfügbar. Aber wenn die Frage ist "warum überhaupt eifrig":
.__dict__
. Im Gegensatz dazu erfordert Graph die Kenntnis spezieller Backend-Funktionen, was den gesamten Prozess des Debuggens und der Selbstbeobachtung erheblich verkompliziert.WIE AKTIVIEREN / DEAKTIVIEREN SIE EAGER?
ZUSÄTZLICHE INFO :
_on_batch()
Methoden in TF2; Laut TF-Entwickler verwenden sie immer noch eine langsamere Implementierung, aber nicht absichtlich - dh sie muss behoben werden. Siehe andere Antwort für Details.ANFRAGEN AN TENSORFLOW DEVS :
train_on_batch()
und den Leistungsaspekt desfit()
iterativen Aufrufs ; Kundenspezifische Zugschleifen sind für viele wichtig, besonders für mich.Danksagung : Danke an
UPDATES :
14.11.19 - Ich habe ein Modell (in meiner realen Anwendung) gefunden, das auf TF2 für alle * Konfigurationen mit Numpy-Eingabedaten langsamer läuft . Die Unterschiede lagen zwischen 13 und 19% und im Durchschnitt bei 17%. Die Unterschiede zwischen
keras
undtf.keras
waren jedoch dramatischer: 18-40% , Durchschn. 32% (sowohl TF1 als auch 2). (* - außer Eager, für die TF2 OOM'd)17.11.19 - Entwickler haben die
on_batch()
Methoden in einem kürzlich durchgeführten Commit aktualisiert und angegeben, dass die Geschwindigkeit verbessert wurde. Sie werden in TF 2.1 veröffentlicht oder sind ab sofort als verfügbartf-nightly
. Da ich letzteres nicht zum Laufen bringen kann, wird das Bänken bis 2.1 verzögert.quelle
fit_generator
? ... Ich möchte so gut wie nietrain_on_batch
und die Verwaltung meiner eigenen Trainingsschleife über mehrere Chargen hinweg ist ein riesiges Anti-Muster, das selbst bei hohen Kosten vermieden werden muss.fit
kleinen zusätzlichen Datenverarbeitungsaufwand handelt. Was Zugschleifen betrifft, habe ich meine eigene geschrieben, die sich letztendlich in eine Art API verwandelte.fit_generator
Es fehlt an Selbstbeobachtung, Anpassbarkeit und Speichern / Laden - also ein absolutes Nein für mich. Ich werde meine Trainingsschleife irgendwann auf Github veröffentlichen.fit_generator
Ihrer Anwendung zu testen, besteht darin, sie zu testen.DIESE ANTWORT : Ziel ist es, eine detaillierte Beschreibung des Problems auf Grafik- / Hardware-Ebene bereitzustellen - einschließlich TF2- / TF1-Zugschleifen, Eingabedatenprozessoren und Eager- / Grafikmodus-Ausführungen. Eine Zusammenfassung der Probleme und Richtlinien zur Lösung von Problemen finden Sie in meiner anderen Antwort.
LEISTUNGSPRÜFUNG : Je nach Konfiguration ist manchmal eine schneller, manchmal die andere. Was TF2 und TF1 angeht, sind sie im Durchschnitt ungefähr gleich, aber es gibt signifikante konfigurationsbasierte Unterschiede, und TF1 übertrumpft TF2 häufiger als umgekehrt. Siehe "BENCHMARKING" weiter unten.
EAGER VS. GRAFIK : Das Fleisch dieser ganzen Antwort für einige: TF2 ist nach meinen Tests langsamer als TF1. Details weiter unten.
Der grundlegende Unterschied zwischen den beiden besteht darin, dass Graph proaktiv ein Computernetzwerk einrichtet und ausgeführt wird, wenn es dazu aufgefordert wird - während Eager alles bei der Erstellung ausführt. Aber die Geschichte beginnt erst hier:
Eifrig ist NICHT frei von Graph und kann entgegen der Erwartung tatsächlich meistens Graph sein. Was es größtenteils ist, ist ausgeführtes Diagramm - dies schließt Modell- und Optimierungsgewichte ein, die einen großen Teil des Diagramms ausmachen.
Eager erstellt bei der Ausführung einen Teil des eigenen Diagramms neu . direkte Folge davon, dass Graph nicht vollständig erstellt wurde - siehe Profilerergebnisse. Dies hat einen Rechenaufwand.
Eifrig ist langsamer mit Numpy-Eingaben ; pro diesem Git Kommentar & Code, in Eager Numpy Eingänge umfassen die Overhead - Kosten Tensoren von CPU zu GPU zu kopieren. Beim Durchlaufen des Quellcodes sind die Unterschiede bei der Datenverarbeitung klar. Eifrig passiert Numpy direkt, während Graph Tensoren passiert, die dann zu Numpy ausgewertet werden. unsicher über den genauen Prozess, aber letztere sollten Optimierungen auf GPU-Ebene beinhalten
TF2 Eager ist langsamer als TF1 Eager - das ist ... unerwartet. Siehe Benchmarking-Ergebnisse unten. Die Unterschiede reichen von vernachlässigbar bis signifikant, sind jedoch konsistent. Unsicher, warum dies der Fall ist - wenn ein TF-Entwickler dies klarstellt, wird die Antwort aktualisiert.
TF2 vs. TF1 : Zitieren relevanter Teile der Antwort eines TF-Entwicklers, Q. Scott Zhu - mit ein wenig meiner Betonung und Umformulierung:
Mit dem letzten Satz des letzten Absatzes oben und dem letzten Satz des folgenden Absatzes:
Ich bin anderer Meinung - gemäß meinen Profilerstellungsergebnissen, die zeigen, dass die Verarbeitung der Eingabedaten von Eager wesentlich langsamer ist als die von Graph. Auch unsicher,
tf.data.Dataset
insbesondere, aber Eager ruft wiederholt mehrere der gleichen Datenkonvertierungsmethoden auf - siehe Profiler.Zuletzt das verknüpfte Commit von dev: Signifikante Anzahl von Änderungen zur Unterstützung der Keras v2-Schleifen .
Zugschleifen : abhängig von (1) Eifrig gegen Grafik; (2) Eingangsdatenformat, in Ausbildung wird mit einer deutlichen Zuge Schleife ablaufen - in TF2
_select_training_loop()
, training.py , von:Jeder behandelt die Ressourcenzuweisung unterschiedlich und hat Konsequenzen für Leistung und Leistungsfähigkeit.
Zugschleifen:
fit
vstrain_on_batch
,keras
vstf.keras
.: Jede der vier verwendet unterschiedliche Zugschleifen, wenn auch möglicherweise nicht in jeder möglichen Kombination.keras
'verwendetfit
zum Beispiel eine Form vonfit_loop
z. B.training_arrays.fit_loop()
undtrain_on_batch
kann verwendet werdenK.function()
.tf.keras
hat eine komplexere Hierarchie, die teilweise im vorherigen Abschnitt beschrieben wurde.Train Loops: Dokumentation - relevante Quelldokumentation zu einigen der verschiedenen Ausführungsmethoden:
Eingabedatenprozessoren : Ähnlich wie oben wird der Prozessor von Fall zu Fall ausgewählt, abhängig von den internen Flags, die gemäß den Laufzeitkonfigurationen (Ausführungsmodus, Datenformat, Verteilungsstrategie) gesetzt wurden. Der einfachste Fall ist mit Eager, das direkt mit Numpy-Arrays funktioniert. Einige spezifische Beispiele finden Sie in dieser Antwort .
MODELLGRÖSSE, DATENGRÖSSE:
convert_to_tensor
unter "PROFILER").BENCHMARKS : das gemahlene Fleisch. - Word-Dokument - Excel-Tabelle
Terminologie :
(1 - longer_time / shorter_time)*100
; Begründung: Wir sind daran interessiert, welcher Faktor einer schneller ist als der andere.shorter / longer
ist eigentlich eine nichtlineare Beziehung, die für einen direkten Vergleich nicht nützlich ist+
wenn TF2 schneller ist+
Wenn Graph schneller istPROFILER :
PROFILER - Erläuterung : Spyder 3.3.6 IDE-Profiler.
Einige Funktionen werden in Nestern anderer wiederholt; Daher ist es schwierig, die genaue Trennung zwischen den Funktionen "Datenverarbeitung" und "Training" zu ermitteln, sodass es zu Überschneidungen kommt - wie im allerletzten Ergebnis deutlich.
% Zahlen berechnet für Laufzeit minus Build-Zeit
_func = func
werden als profiliertfunc
), was sich in der Erstellungszeit mischt - daher muss sie ausgeschlossen werdenPRÜFUMGEBUNG :
METHODIK :
batch_size
undnum_channels
Conv1D
,Dense
‚erlernbar‘ Schichten; RNNs werden pro TF-Version vermieden. Unterschiedelayers.Embedding()
) oder spärlichen Ziele (zSparseCategoricalCrossEntropy()
EINSCHRÄNKUNGEN : Eine "vollständige" Antwort würde jede mögliche Zugschleife und jeden möglichen Iterator erklären, aber das geht sicherlich über meine Zeitfähigkeit, meinen nicht vorhandenen Gehaltsscheck oder meine allgemeine Notwendigkeit hinaus. Die Ergebnisse sind nur so gut wie die Methodik - offen interpretieren.
CODE :
quelle
model.compile
ohnerun_eagerly=True
Argument aufrufen . Im eifrigen Modus können Sie einen Teil Ihres Codes im Grafikmodus mit ausführentf.function
. Daher denke ich, dass die Standardimplementierung darincompile
besteht, ein Rechendiagramm zu erstellen, anstatt es aus Leistungsgründen eifrig auszuführen. Beachten Sie auch, dass bei einem Faltungsmodell keine Beschleunigung im Diagrammmodus angezeigt wird, da die Python-Interaktion minimal ist. Wenn Sie viele Matheoperationen durchführen, kann dies einen großen Unterschied machen (auch bei der Speichernutzung).model.compile
ohne jedochrun_eagerly=True
den Grafikmodus sicherzustellen , oder nicht?model.compile
odermodel.fit
muss sichergestellt werden, dass das Training intern im Grafikmodus ausgeführt wird.run_eagerly=True
als zu kompilierender Parameter übergeben wird." (Quelle tensorflow.org/guide/keras/overview ) Daher kann ich, wenn Sie dasrun_eagerly=True
Modell nicht bestehen, im Grafikmodus laufen. Ich bin mir nicht sicher, was der entscheidende Faktor ist, aber warum sollte es nicht im Grafikmodus ausgeführt werden, wenn es effizienter als eifrig ist.