UPDATE - 1/15/2020 : praktik terbaik saat ini untuk ukuran batch kecil adalah untuk memberi makan input ke model secara langsung - yaitu preds = model(x)
, dan jika lapisan berperilaku berbeda di kereta / inferensi model(x, training=False)
,. Per komit terbaru, ini sekarang didokumentasikan .
Saya belum membuat tolok ukur ini, tetapi per diskusi Git , ini juga layak untuk dicoba predict_on_batch()
- terutama dengan peningkatan pada TF 2.1.
ULTIMATE PENYEBAB : self._experimental_run_tf_function = True
. Itu eksperimental . Tapi itu sebenarnya tidak buruk.
Untuk membaca pengembang TensorFlow: bersihkan kode Anda . Ini berantakan. Dan itu melanggar praktik pengkodean penting, seperti satu fungsi melakukan satu hal ; _process_inputs
melakukan banyak lebih dari "input proses", sama untuk _standardize_user_data
. "Saya tidak dibayar cukup" - tetapi Anda lakukan membayar, di waktu tambahan menghabiskan memahami barang-barang Anda sendiri, dan pada pengguna mengisi halaman Masalah Anda dengan bug lebih mudah diselesaikan dengan kode yang lebih jelas.
RINGKASAN : Hanya sedikit lebih lambat compile()
.
compile()
menetapkan bendera internal yang menetapkan fungsi prediksi berbeda predict
. Fungsi ini membangun grafik baru pada setiap panggilan, memperlambatnya relatif terhadap yang tidak dikompilasi. Namun, perbedaannya hanya diucapkan ketika waktu kereta jauh lebih pendek dari waktu pemrosesan data . Jika kita meningkatkan ukuran model menjadi setidaknya menengah, keduanya menjadi sama. Lihat kode di bagian bawah.
Sedikit peningkatan dalam waktu pemrosesan data ini lebih dari dikompensasi oleh kemampuan grafik yang diperkuat. Karena lebih efisien jika hanya menyimpan satu grafik model, satu pra-kompilasi dibuang. Meskipun demikian : jika model Anda relatif kecil terhadap data, Anda lebih baik tanpa compile()
inferensi model. Lihat jawaban saya yang lain untuk solusinya.
APA YANG HARUS SAYA LAKUKAN?
Bandingkan kinerja model yang dikompilasi vs yang tidak dikompilasi seperti yang saya miliki dalam kode di bagian bawah.
- Dikompilasi lebih cepat : dijalankan
predict
pada model yang dikompilasi.
- Dikompilasi lebih lambat : dijalankan
predict
pada model yang tidak dikompilasi.
Ya, keduanya mungkin, dan itu akan tergantung pada (1) ukuran data; (2) ukuran model; (3) perangkat keras. Kode di bagian bawah sebenarnya menunjukkan model yang dikompilasi menjadi lebih cepat, tetapi 10 iterasi adalah sampel kecil. Lihat "pemecahan masalah" di jawaban saya yang lain untuk "bagaimana caranya".
RINCIAN :
Butuh beberapa saat untuk debug, tetapi menyenangkan. Di bawah ini saya menggambarkan penyebab utama yang saya temukan, mengutip beberapa dokumentasi yang relevan, dan menunjukkan hasil profiler yang mengarah pada hambatan utama.
( FLAG == self.experimental_run_tf_function
, untuk singkatnya)
Model
secara instantiate default dengan FLAG=False
. compile()
set ke True
.
predict()
melibatkan perolehan fungsi prediksi, func = self._select_training_loop(x)
- Tanpa kwarg khusus yang diteruskan ke
predict
dan compile
, semua bendera lainnya sedemikian rupa sehingga:
- (A)
FLAG==True
->func = training_v2.Loop()
- (B)
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
- Dari dokumentasi kode sumber , (A) sangat bergantung pada grafik, menggunakan lebih banyak strategi distribusi, dan ops cenderung membuat & menghancurkan elemen grafik, yang "dapat" mempengaruhi kinerja.
Benar pelakunya : _process_inputs()
, akuntansi untuk 81% dari runtime . Komponen utamanya? _create_graph_function()
, 72% dari runtime . Metode ini bahkan tidak ada untuk (B) . Menggunakan model menengah, bagaimanapun, _process_inputs
terdiri dari kurang dari 1% dari runtime . Kode di bawah, dan hasil profiling mengikuti.
PROSESOR DATA :
(A) :, <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
digunakan dalam _process_inputs()
. Kode sumber yang relevan
(B) : numpy.ndarray
, dikembalikan oleh convert_eager_tensors_to_numpy
. Kode sumber yang relevan , dan di sini
FUNGSI PELAKSANAAN MODEL (mis. Prediksi)
(A) : fungsi distribusi , dan di sini
(B) : fungsi distribusi (berbeda) , dan di sini
PROFILER : hasil untuk kode di jawaban saya yang lain, "model kecil", dan dalam jawaban ini, "model sedang":
Model kecil : 1000 iterasi,compile()
Model kecil : 1000 iterasi, no compile()
Model medium : 10 iterasi
DOKUMENTASI (tidak langsung) pada efek dari compile()
: sumber
Tidak seperti operasi TensorFlow lainnya, kami tidak mengonversi input numerik python ke tensor. Selain itu, grafik baru dihasilkan untuk setiap nilai numerik python yang berbeda , misalnya panggilan g(2)
dan g(3)
akan menghasilkan dua grafik baru
function
instantiate grafik terpisah untuk setiap set unik bentuk input dan tipe data . Misalnya, cuplikan kode berikut akan menghasilkan tiga grafik berbeda yang dilacak, karena setiap input memiliki bentuk yang berbeda
Objek fungsi tf. tunggal mungkin perlu memetakan ke beberapa grafik perhitungan di bawah tenda. Ini harus terlihat hanya karena kinerja (melacak grafik memiliki biaya komputasi dan memori bukan nol ) tetapi tidak boleh mempengaruhi kebenaran program
CONTOH COUNTER :
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)
Output :
34.8542 sec
34.7435 sec