Teoretycznie przewidywanie powinno być stałe, ponieważ odważniki mają ustalony rozmiar. Jak odzyskać prędkość po kompilacji (bez konieczności usuwania optymalizatora)?
Zobacz powiązany eksperyment: 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
źródło
źródło
fit
bezcompile
; Optymalizator nawet nie istnieje, aby zaktualizować wagi.predict
można używać bezfit
lubcompile
zgodnie z opisem w mojej odpowiedzi, ale różnica w wydajności nie powinna być tak dramatyczna - stąd problem.Odpowiedzi:
AKTUALIZACJA - 15.01.2020 : prąd najlepsze praktyki dla małych seriach powinno karmić wejść do modelu bezpośrednio - tj
preds = model(x)
, a jeśli warstwy zachowują się inaczej w pociągu / wnioskowaniamodel(x, training=False)
. Według ostatniego zatwierdzenia jest to teraz udokumentowane .Nie przeprowadziłem testów porównawczych, ale na dyskusję na Git warto też spróbować
predict_on_batch()
- zwłaszcza z ulepszeniami w TF 2.1.ULTIMATE winowajcą :
self._experimental_run_tf_function = True
. To jest eksperymentalne . Ale tak naprawdę nie jest źle.Do każdego czytnika TensorFlow: wyczyść swój kod . To bałagan. I narusza ważne praktyki kodowania, takie jak jedna funkcja robi jedną rzecz ;
_process_inputs
robi o wiele więcej niż „dane wejściowe procesu”, to samo dla_standardize_user_data
. „Ja nie zapłacił za mało” - ale zrobić wynagrodzenia, w doliczonym czasie spędzonym zrozumienia własne rzeczy, aw użytkowników wypełniających swoją stronę problemy z błędami łatwiej rozwiązane z jaśniejszym kodu.PODSUMOWANIE : jest tylko trochę wolniejszy
compile()
.compile()
ustawia flagę wewnętrzną, która przypisuje inną funkcję predykcjipredict
. Ta funkcja tworzy nowy wykres przy każdym wywołaniu, spowalniając go względem nieskompilowanego. Różnica jest jednak wyraźna tylko wtedy, gdy czas pociągu jest znacznie krótszy niż czas przetwarzania danych . Jeśli zwiększymy rozmiar modelu do co najmniej średniej, oba staną się równe. Zobacz kod na dole.Ten niewielki wzrost czasu przetwarzania danych jest z nadwyżką kompensowany przez zwiększoną wydajność wykresu. Ponieważ utrzymywanie tylko jednego wykresu modelu jest bardziej wydajne, jeden przed kompilacją jest odrzucany. Niemniej jednak : jeśli twój model jest mały w stosunku do danych, lepiej jest bez
compile()
wnioskowania o model. Zobacz moją drugą odpowiedź w celu obejścia tego problemu.CO POWINIENEM ZROBIĆ?
Porównaj wydajność modelu skompilowaną z nieskompilowaną, jak mam w kodzie na dole.
predict
na skompilowanym modelu.predict
na nieskompilowanym modelu.Tak, oba są możliwe i będą zależeć od (1) wielkości danych; (2) rozmiar modelu; (3) sprzęt. Kod u dołu faktycznie pokazuje, że skompilowany model jest szybszy, ale 10 iteracji to mała próbka. Zobacz „obejścia” w mojej innej odpowiedzi na „instrukcje”.
SZCZEGÓŁY :
Debugowanie zajęło trochę czasu, ale było zabawne. Poniżej opisuję kluczowych sprawców, których odkryłem, przytaczam odpowiednią dokumentację i pokazuję wyniki profilowania, które doprowadziły do ostatecznego wąskiego gardła.
(
FLAG == self.experimental_run_tf_function
dla zwięzłości)Model
domyślnie tworzy się zFLAG=False
.compile()
ustawia toTrue
.predict()
obejmuje nabycie funkcji przewidywania,func = self._select_training_loop(x)
predict
icompile
, wszystkie inne flagi są takie, że:FLAG==True
->func = training_v2.Loop()
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
Prawdziwy winowajca :
_process_inputs()
, co stanowi 81% starcie . Jego główny składnik?_create_graph_function()
, 72% czasu działania . Ta metoda nawet nie istnieje dla (B) . Jednak użycie modelu średniej wielkości_process_inputs
stanowi mniej niż 1% czasu działania . Kod u dołu i wyniki profilowania.PROCESORZY DANYCH :
(A) :,
<class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
używane w_process_inputs()
. Odpowiedni kod źródłowy(B) :
numpy.ndarray
, zwrócony przezconvert_eager_tensors_to_numpy
. Odpowiedni kod źródłowy i tutajFUNKCJA WYKONANIA MODELU (np. Przewidywanie)
(A) : funkcja dystrybucji i tutaj
(B) : funkcja dystrybucji (inna) i tutaj
PROFILER : wyniki dla kodu w mojej drugiej odpowiedzi „malutki model”, a w tej odpowiedzi „średni model”:
Mały model : 1000 iteracji,
compile()
Mały model : 1000 iteracji, nie
compile()
Model średni : 10 iteracji
DOKUMENTACJA (pośrednio) na temat wpływu
compile()
: źródłaPRZYKŁAD :
Wyjścia :
źródło
compile()
?AKTUALIZACJA : zobacz rzeczywistą odpowiedź opublikowaną jako osobną odpowiedź; ten post zawiera dodatkowe informacje
.compile()
ustawia większość wykresów TF / Keras, w tym straty, metryki, gradienty, a częściowo optymalizator i jego wagi - co gwarantuje zauważalne spowolnienie.Co jest nieoczekiwana jest skala spowolnienia - 10-krotnie na moim własnym doświadczeniu, a
predict()
, który nie aktualizuje żadnych ciężarów. Patrząc na kod źródłowy TF2, elementy wykresu wydają się ściśle ze sobą powiązane, a zasoby niekoniecznie są przydzielane „sprawiedliwie”.Możliwe przeoczenie przez programistów
predict
wydajności nieskompilowanego modelu, ponieważ modele są zwykle kompilowane - ale w praktyce jest to niedopuszczalna różnica. Możliwe też, że jest to „zło konieczne”, ponieważ istnieje proste obejście tego problemu (patrz poniżej).To nie jest pełna odpowiedź i mam nadzieję, że ktoś może ją tutaj podać - jeśli nie, sugeruję otwarcie problemu z Github na TensorFlow. (OP ma; tutaj )
Obejście : wytrenuj model, zapisz jego wagi , odbuduj model bez kompilacji, załaduj wagi. Czy nie zapisać cały model (np
model.save()
), jak to będzie ładować skompilowany - zamiast używaćmodel.save_weights()
imodel.load_weights()
.Obejście 2 : powyżej, ale użyj
load_model(path, compile=False)
; sugestia: D. MöllerUPDATE : wyjaśnienie, optymalizator jest nie w pełni tworzony z
compile
tym jegoweights
andupdates
tensorów - odbywa się to po pierwsze wywołanie funkcji łącznika jest wykonana (fit
,train_on_batch
itp), poprzezmodel._make_train_function()
.Obserwowane zachowanie jest zatem jeszcze bardziej dziwne. Co gorsza, budowa optymalizator ma nie wywoływać jakiekolwiek dalsze spowolnienie (patrz niżej) - sugeruje „rozmiar Wykres” nie jest głównym wyjaśnienie tutaj.
EDYCJA : w niektórych modelach 30- krotne spowolnienie . TensorFlow, co zrobiłeś. Przykład poniżej:
Wyjścia :
źródło
model.fit()
porównaniu z dynamiczną pętlą z chętnym wykonywaniem, aby zobaczyć, czy utrata wydajności jest zbyt duża ...load_model(name, compile=False)
, jest to prostsze niż zapisywanie / ładowanie ciężarów i odtwarzanie modelu.