Theoretisch sollte die Vorhersage konstant sein, da die Gewichte eine feste Größe haben. Wie bekomme ich meine Geschwindigkeit nach dem Kompilieren zurück (ohne das Optimierungsprogramm entfernen zu müssen)?
Siehe zugehöriges Experiment: https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true
python
performance
tensorflow
keras
jupyter-notebook
off99555
quelle
quelle
fit
ohnecompile
; Der Optimierer existiert nicht einmal, um Gewichte zu aktualisieren.predict
kann ohnefit
odercompile
wie in meiner Antwort beschrieben verwendet werden, aber der Leistungsunterschied sollte nicht so dramatisch sein - daher das Problem.Antworten:
UPDATE - 15.01.2020 : Die derzeitige bewährte Methode für kleine Losgrößen sollte darin bestehen, Eingaben direkt in das Modell einzuspeisen - dh
preds = model(x)
und wenn sich Ebenen beim Zug / bei der Inferenz unterschiedlich verhaltenmodel(x, training=False)
. Nach dem letzten Commit ist dies jetzt dokumentiert .Ich habe diese nicht bewertet, aber laut Git-Diskussion lohnt es sich auch, es zu versuchen
predict_on_batch()
- insbesondere mit Verbesserungen in TF 2.1.ULTIMATIVER SCHULD :
self._experimental_run_tf_function = True
. Es ist experimentell . Aber es ist nicht wirklich schlecht.Für alle TensorFlow-Entwickler: Bereinigen Sie Ihren Code . Es ist ein Chaos. Und es verstößt gegen wichtige Codierungspraktiken, z. B. wenn eine Funktion eine Sache tut ;
_process_inputs
macht viel mehr als "Prozesseingaben", das gleiche gilt für_standardize_user_data
. "Ich werde nicht genug bezahlt" - aber Sie zahlen, wenn Sie mehr Zeit damit verbringen, Ihre eigenen Sachen zu verstehen, und wenn Benutzer Ihre Issues-Seite mit Fehlern füllen, die mit einem klareren Code leichter behoben werden können.ZUSAMMENFASSUNG : Es ist nur ein bisschen langsamer mit
compile()
.compile()
setzt ein internes Flag, das eine andere Vorhersagefunktion zuweistpredict
. Diese Funktion erstellt bei jedem Aufruf ein neues Diagramm und verlangsamt es im Vergleich zu nicht kompilierten. Der Unterschied ist jedoch nur dann ausgeprägt, wenn die Zugzeit viel kürzer als die Datenverarbeitungszeit ist . Wenn wir die Modellgröße auf mindestens mittelgroß erhöhen , werden beide gleich. Siehe Code unten.Diese leichte Verlängerung der Datenverarbeitungszeit wird durch die verstärkte Grafikfähigkeit mehr als kompensiert. Da es effizienter ist, nur ein Modelldiagramm beizubehalten, wird das eine Vorkompilierungsprogramm verworfen. Nichtsdestotrotz : Wenn Ihr Modell im Verhältnis zu Daten klein ist, sind Sie ohne
compile()
Modellschluss besser dran . Siehe meine andere Antwort für eine Problemumgehung.WAS SOLLTE ICH TUN?
Vergleichen Sie die kompilierte und die nicht kompilierte Modellleistung wie im Code unten.
predict
Auf einem kompilierten Modell ausführen .predict
Auf einem nicht kompilierten Modell ausführen .Ja, beides ist möglich und hängt von (1) der Datengröße ab. (2) Modellgröße; (3) Hardware. Der Code unten zeigt tatsächlich, dass das kompilierte Modell schneller ist, aber 10 Iterationen sind ein kleines Beispiel. Siehe "Problemumgehungen" in meiner anderen Antwort für die "Anleitung".
DETAILS :
Das Debuggen dauerte eine Weile, hat aber Spaß gemacht. Im Folgenden beschreibe ich die Haupttäter, die ich entdeckt habe, zitiere einige relevante Dokumentationen und zeige Profiler-Ergebnisse, die zum endgültigen Engpass geführt haben.
(der
FLAG == self.experimental_run_tf_function
Kürze halber)Model
Standardmäßig instanziiert mitFLAG=False
.compile()
setzt es aufTrue
.predict()
beinhaltet den Erwerb der Vorhersagefunktion,func = self._select_training_loop(x)
predict
und übergeben werdencompile
, sind alle anderen Flags so, dass:FLAG==True
->func = training_v2.Loop()
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
Wahr Schuldige :
_process_inputs()
für Buchhaltung 81% der Laufzeit . Seine Hauptkomponente?_create_graph_function()
, 72% der Laufzeit . Diese Methode existiert nicht einmal für (B) . Verwendung eines mittelgroßen Modell jedoch_process_inputs
weist weniger als 1% der Laufzeit . Code unten und Profilerstellungsergebnisse folgen.DATENVERARBEITER :
(A) :
<class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
verwendet in_process_inputs()
. Relevanter Quellcode(B) :
numpy.ndarray
, zurückgegeben vonconvert_eager_tensors_to_numpy
. Relevanter Quellcode und hierMODEL EXECUTION FUNCTION (zB vorhersagen)
(A) : Verteilungsfunktion und hier
(B) : Verteilungsfunktion (unterschiedlich) und hier
PROFILER : Ergebnisse für Code in meiner anderen Antwort "winziges Modell" und in dieser Antwort "mittleres Modell":
Winziges Modell : 1000 Iterationen,
compile()
Winziges Modell : 1000 Iterationen, Nr
compile()
Mittleres Modell : 10 Iterationen
DOKUMENTATION (indirekt) über die Auswirkungen von
compile()
: QuelleGegenbeispiel :
Ausgänge :
quelle
compile()
?UPDATE : Die tatsächliche Antwort wird als separate Antwort angezeigt. Dieser Beitrag enthält zusätzliche Informationen
.compile()
Richtet den Großteil des TF / Keras-Diagramms ein, einschließlich Verluste, Metriken, Gradienten und teilweise des Optimierers und seiner Gewichte - was eine bemerkenswerte Verlangsamung garantiert.Was ist unerwartet ist das Ausmaß der Verlangsamung - 10-fach auf meinem eigenen Experiment, und für
predict()
, die keine Gewichte nicht aktualisiert. Wenn man sich den Quellcode von TF2 ansieht, scheinen die Diagrammelemente eng miteinander verflochten zu sein, wobei die Ressourcen nicht unbedingt "fair" zugewiesen werden.Möglicherweise übersehen Entwickler
predict
die Leistung eines nicht kompilierten Modells, da Modelle normalerweise kompiliert verwendet werden. In der Praxis ist dies jedoch ein inakzeptabler Unterschied. Es ist auch möglich, dass es ein "notwendiges Übel" ist, da es eine einfache Problemumgehung gibt (siehe unten).Dies ist keine vollständige Antwort, und ich hoffe, dass jemand sie hier bereitstellen kann. Wenn nicht, würde ich vorschlagen, ein Github-Problem auf TensorFlow zu eröffnen. (OP hat; hier )
Problemumgehung : Trainieren Sie ein Modell, speichern Sie seine Gewichte , erstellen Sie das Modell neu, ohne es zu kompilieren, und laden Sie die Gewichte. Speichern Sie nicht das gesamte Modell (z. B.
model.save()
), da es kompiliert geladen wird. Verwenden Sie stattdessenmodel.save_weights()
undmodel.load_weights()
.Problemumgehung 2 : oben, aber verwenden
load_model(path, compile=False)
; Vorschlagsgutschrift: D. MöllerUPDATE : Um zu klären, wird Optimierer nicht vollständig mit instanziiert
compile
, einschließlich seinerweights
undupdates
Tensoren - dies erfolgt ist , wenn der erste Anruf zu einer Anpassungsfunktion vorgenommen wird (fit
,train_on_batch
, usw.), übermodel._make_train_function()
.Das beobachtete Verhalten ist daher noch seltsamer. Schlimmer noch, hat das Optimierungsprogramm den Bau nicht weitere Verlangsamungen (siehe unten) entlocken - was darauf hindeutet , „Diagrammgröße“ ist hier nicht die Haupterklärung.
EDIT : bei einigen Modellen eine 30-fache Verlangsamung . TensorFlow, was hast du getan? Beispiel unten:
Ausgänge :
quelle
model.fit()
gegen eine dynamische Schleife mit eifriger Ausführung testen möchte, um festzustellen, ob der Leistungsverlust zu groß ist ...load_model(name, compile=False)
, es ist einfacher als das Speichern / Laden von Gewichten und das Neuerstellen des Modells.