Keras modeli derlemeden sonra neden daha yavaş tahmin ediyor?


23

tahmin hızı keras

Teoride, ağırlıklar sabit bir boyuta sahip olduğundan tahmin sabit olmalıdır. Derlemeden sonra hızımı nasıl geri alabilirim (optimize ediciyi kaldırmaya gerek olmadan)?

İlişkili deneye bakın: https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiments/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true


Ben derleme sonra modeli sığdırmak gerektiğini düşünüyorum sonra tahmin etmek için eğitimli modeli kullanın. Buraya
naif

@ naive Fitting konu ile ilgisi yoktur. Ağın nasıl çalıştığını biliyorsanız, tahminin neden daha yavaş olduğunu merak edersiniz. Tahmin yaparken, matris çarpımı için sadece ağırlıklar kullanılır ve ağırlıklar derlemeden önce ve sonra sabitlenmelidir, bu nedenle tahmin süresi sabit kalmalıdır.
off99555

Bunun için alakasız olduğunu biliyorum konuyla . Ve, ağın, karşılaştığınız görevlerin ve doğruluk karşılaştırmasının aslında anlamsız olduğuna işaret etmek için nasıl çalıştığını bilmesine gerek yoktur. Modeli bazı veriler üzerine oturtmadan tahmin ettiğiniz ve aslında harcanan zamanı karşılaştırıyorsunuz. Bu, sinir ağı için olağan veya doğru kullanım durumları değildir
saf

3
@naive Sorun, doğruluk veya model tasarımı ile ilgisi olmayan derlenmiş veya derlenmemiş model performansının anlaşılması ile ilgilidir. Bu, TF kullanıcılarına mal olabilecek meşru bir konudur - bu soru üzerine tökezleyene kadar bunun hakkında bir fikrim yoktu.
OverLordGoldDragon

1
Yapamazsınız @naive fitolmadan compile; optimizer herhangi bir ağırlığı güncellemek için bile mevcut değildir. predict olabilir olmadan kullanılamaz fitveya compilecevabım açıklandığı gibi, ama performans farkı bu dramatik olmamalı - dolayısıyla sorunu.
OverLordGoldDragon

Yanıtlar:


22

GÜNCELLEME - 1/15/2020 : küçük parti boyutları için mevcut en iyi uygulama, girdileri modele doğrudan beslemek olmalıdır - yani preds = model(x), katmanlar tren / çıkarımda farklı davranıyorsa model(x, training=False). Son işlem başına, bu artık belgelenmiştir .

Bunları karşılaştırmadım, ancak Git tartışmasına göre , predict_on_batch()özellikle TF 2.1'deki iyileştirmelerle de denemeye değer .


SON Suçlu : self._experimental_run_tf_function = True. Bu var deneysel . Ama aslında kötü değil.

Herhangi bir TensorFlow geliştirici okumasına: kodunuzu temizleyin . Bu bir karmaşa. Ve bir işlev bir şey yapar gibi önemli kodlama uygulamalarını ihlal eder ; "işlem girdileri" den çok daha fazlasını _process_inputsyapar . "Yeterince maaş almıyorum" - ama sen bunu kendi eşyalarını anlamak harcanan uzatmalarda, ödeme ve böcek ile Sorunlar sayfasına dolum kullanıcıların daha kolay daha net koduyla çözüldü._standardize_user_data


Özet : sadece biraz daha yavaş compile().

compile()için farklı bir tahmin işlevi atayan dahili bir bayrak ayarlar predict. Bu işlev her çağrı üzerine yeni bir grafik oluşturur ve derlenmemiş olana göre yavaşlar. Ancak, fark sadece tren süresi veri işleme süresinden çok daha kısa olduğunda belirgindir . Biz ise artış için model boyutunu en azından orta ölçekli, iki eşit hale gelir. En alttaki koda bakın.

Veri işleme süresindeki bu küçük artış, güçlendirilmiş grafik kapasitesi ile telafi edilenden daha fazladır. Sadece bir model grafiğini tutmak daha verimli olduğu için, bir ön derleme atılır. Bununla birlikte : modeliniz verilere göre küçükse, compile()model çıkarımı olmadan daha iyi durumda olursunuz . Geçici çözüm için diğer cevabıma bakın.


NE YAPMALIYIM?

Altta kod var gibi derlenmemiş vs derlenmiş model performansını karşılaştırın.

  • Derlenmiş daha hızlı : predictderlenmiş bir model üzerinde çalıştırın .
  • Derlenmiş daha yavaş : predictderlenmemiş bir modelde çalıştırın .

Evet, her ikisi de mümkündür ve (1) veri boyutuna bağlı olacaktır; (2) model boyutu; (3) donanım. Alttaki kod aslında derlenmiş modelin daha hızlı olduğunu gösterir , ancak 10 yineleme küçük bir örnektir. "Nasıl yapılır" konusundaki diğer yanıtımda "geçici çözümler" konusuna bakın.


DETAYLAR :

Bu hata ayıklamak biraz zaman aldı, ama eğlenceliydi. Aşağıda, keşfettiğim önemli suçluları tarif ediyorum, bazı ilgili belgeleri belirtiyorum ve nihai darboğa yol açan profiler sonuçlarını gösteriyoruz.

( FLAG == self.experimental_run_tf_function, kısalık için)

  1. Modelvarsayılan olarak ile başlar FLAG=False. compile()olarak ayarlar True.
  2. predict() tahmin işlevini edinmeyi içerir, func = self._select_training_loop(x)
  3. Herhangi bir özel kwarg geçmeden predictve compilediğer tüm bayraklar şöyledir:
    • (A) FLAG==True ->func = training_v2.Loop()
    • (B) FLAG==False ->func = training_arrays.ArrayLikeTrainingLoop()
  4. Kaynaktan kaynak kodu docstringe , (A) ' , ağırlıklı olarak grafik güvenen daha dağıtım stratejisi kullanır ve op oluşturma ve (do) "olabilir" grafik elementleri, darbe performansı yok yatkındır.

Gerçek suçlu : _process_inputs(), muhasebesi çalışma zamanının% 81 . Ana bileşeni mi? _create_graph_function(), Çalışma süresinin% 72'si . Bu yöntem (B) için bile mevcut değildir . Bununla birlikte, orta boyutlu bir modelin kullanılması, çalışma süresinin% 1'inden daha azını içerir . En alttaki kod ve profil oluşturma sonuçları aşağıdadır._process_inputs


VERİ İŞLEMCİLERİ :

(A) :, <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>içinde kullanılır _process_inputs(). İlgili kaynak kodu

(B) :, numpy.ndarraytarafından döndürüldü convert_eager_tensors_to_numpy. İlgili kaynak kodu ve burada


MODEL YÜRÜTME FONKSİYONU (örn. Tahmin)

(A) : dağıtım fonksiyonu ve burada

(B) : dağıtım fonksiyonu (farklı) ve burada


PROFILER : diğer cevabımdaki "minik model" ve bu cevaba "orta model" deki kod sonuçları:

Küçük model : 1000 iterasyon,compile()

Küçük model : 1000 iterasyon, hayır compile()

Orta model : 10 iterasyon


DOKÜMANTASYON (dolaylı olarak) etkileri compile(): kaynak

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 , örneğin çağrı g(2)ve g(3)iki yeni grafik oluşturur

function Her benzersiz giriş şekli ve veri türü kümesi için ayrı bir grafik başlatır . Örneğin, aşağıdaki kod snippet'i, her bir girişin farklı bir şekli olduğundan üç ayrı grafiğin izlenmesine neden olur

Tek bir tf.function nesnesinin kaputun altındaki birden çok hesaplama grafiğine eşlenmesi gerekebilir. Bu yalnızca performans olarak görünmelidir (izleme grafiklerinde sıfır olmayan bir hesaplama ve bellek maliyeti vardır ), ancak programın doğruluğunu etkilememelidir


KARŞI ÖRNEK :

from tensorflow.keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from tensorflow.keras.layers import Flatten, Dropout
from tensorflow.keras.models import Model
import numpy as np
from time import time

def timeit(func, arg, iterations):
    t0 = time()
    for _ in range(iterations):
        func(arg)
    print("%.4f sec" % (time() - t0))

batch_size = 32
batch_shape = (batch_size, 400, 16)
ipt   = Input(batch_shape=batch_shape)
x     = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
x     = LSTM(512, activation='relu', return_sequences=True)(ipt)
x     = Conv1D(128, 400, 1, 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)

X = np.random.randn(*batch_shape)
timeit(model.predict, X, 10)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 10)

Çıktılar :

34.8542 sec
34.7435 sec

1
Herhangi bir model boyutu için en yüksek tahmin hızını elde etmek için ne yapmamız gerektiğine dair sonuç nedir? Sadece yapmamak mı compile()?
off99555

3
@ off99555 "herhangi bir model boyutu için" - böyle bir şey yok. Tüm cevabı okuyun - eğer hata ayıklamak için saatler aldıysam, askerden birkaç dakika mantıksız olmamalı.
OverLordGoldDragon

Her şeyi okudum ama anlamak zor çünkü kodu hata ayıklayan ben değilim. Bu nedenle, hata ayıklama aşamasında bulduğunuz ara değişkenleri içermeyen bir sonuç vermeniz gerekir. Örneğin, "Modeliniz küçükse derleme kullanmayın. Modeliniz orta büyüklükteyse, derleme kullanabilirsiniz." Böyle bir şey.
off99555

1
@ off99555 Yeterince adil; güncellenmiş. Yeni bölüm oldukça sağduyulu, ama hemen gerçekleşmediğini görebiliyorum.
OverLordGoldDragon

1
@ off99555 Test ettiğimden değil, ama çok büyük modeller (ResNet, vb) çok daha hızlı derlenmiş çalışabilir, esp. Eğer dağıtılmış birçok cihaz - olarak , (A) daha grafiklerle ve dağıtım-ağırdır. En kesin test, cevaptaki gibi bir testtir. TF lite hakkında
bilginiz yok

15

GÜNCELLEME : ayrı bir cevap olarak gönderilen gerçek cevaba bakınız; bu yazı ek bilgi içeriyor


.compile() Kayıplar, metrikler, degradeler ve kısmen optimize edici ve ağırlıkları da dahil olmak üzere TF / Keras grafiğinin çoğunu oluşturur ve bu da dikkate değer bir yavaşlamayı garanti eder.

Ne olduğunu beklenmedik yavaşlama ölçüde - Kendi deneye 10 kat ve için predict()herhangi bir ağırlık güncelleme etmeyen,. TF2'nin kaynak koduna bakıldığında, grafik öğeleri birbirine sıkıca bağlı görünüyor ve kaynakların "adil" olması gerekmiyor.

predictModeller genellikle derlenmiş olarak kullanıldığından, geliştiricilerin derlenmemiş bir modelin performansına olası göz ardı edilmesi - ancak pratikte bu kabul edilemez bir farktır. Ayrıca basit bir çözüm olduğu için "gerekli bir kötülük" de mümkündür (aşağıya bakınız).

Bu tam bir cevap değil ve umarım birisi burada sağlayabilir - değilse, TensorFlow'da bir Github sorunu açmanızı öneririm. (OP var; burada )


Çözüm : bir modeli eğitin , ağırlıklarını kaydedin , modeli derlemeden yeniden oluşturun, ağırlıkları yükleyin. Do not tüm modeli (örneğin kaydetmek model.save()yerine kullanmak - bu derlenmiş olarak yüklenebilir olacak şekilde,) model.save_weights()ve model.load_weights().

Geçici çözüm 2 : yukarıda, ancak kullanın load_model(path, compile=False); öneri kredisi: D. Möller


GÜNCELLEME : açıklığa kavuşturmak için, iyileştirici olduğu değil tam ile başlatılamaz compileonun dahil weightsve updatestensörlerle - Bu yapıldığında bir uydurma işlevine ilk çağrı (yapıldığında fit, train_on_batchvb) yoluyla model._make_train_function().

Dolayısıyla gözlemlenen davranış daha da gariptir. Daha kötüsü, yok optimizer bina değil başka yavaşlamalar ortaya (aşağıya bakınız) - "grafik boyutu" düşündüren burada ana bir açıklama değildir.


EDIT : bazı modellerde 30x yavaşlama . TensorFlow, ne yaptın. Aşağıdaki örnek:

from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
import numpy as np
from time import time

def timeit(func, arg, iterations):
    t0 = time()
    for _ in range(iterations):
        func(arg)
    print("%.4f sec" % (time() - t0))

ipt   = Input(shape=(4,))
x     = Dense(2, activation='relu')(ipt)
out   = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)

X = np.random.randn(32,4)

timeit(model.predict, X, 1000)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 1000)
model._make_train_function()  # build optimizer
timeit(model.predict, X, 1000)

Çıktılar :

0.9891 sec
29.785 sec
29.521 sec

1
İlginç. model.fit()Performans kaybının çok büyük olup olmadığını görmek için istekli yürütme ile dinamik bir döngüye karşı statik bir grafikle eğitimi test etmek istiyorum ...
Daniel Möller

1
Geçmişte Keras ve PyTorch (PyTorch'un daha hızlı olması) arasında önemli bir hız farkı fark edebildim.
Daniel Möller


2
Evet. Eğitimle ilgili kodu tahminin içine koymak kötü bir tasarım seçimidir. Çünkü kullanıcılar bu tahmin işlevini üretimde sırayla birçok kez kullanacaklardır. En az sürprize neden olmak için en hızlı çalışmalıdır. Numpy uygulamasına kıyasla, sadece bir matrisi çarpmanız, bir önyargı eklemeniz, etkinleştirmeniz ve yoğun bir katman için bu kadar olmanız gerekir. Herhangi bir kayıp fonksiyonu ile ilgilenmeye gerek yoktur.
off99555

1
İpucu, kullanabilirsiniz load_model(name, compile=False), ağırlık kaydetmek / yüklemek ve modeli yeniden oluşturmaktan daha kolaydır.
Daniel Möller
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.