更新-2020年1月15日:小さいバッチサイズの現在のベストプラクティスは、入力をモデルに直接フィードすることです。つまりpreds = model(x)
、層がトレイン/推論で異なる動作をする場合model(x, training=False)
。最新のコミットごとに、これは文書化されています。
私はこれらをベンチマークしていませんが、Gitディスカッションによれば、predict_on_batch()
特にTF 2.1の改善により、試す価値もあります。
究極のカルプリット:self._experimental_run_tf_function = True
。それはだ実験。しかし、それは実際に悪いことではありません。
TensorFlow開発者が読んでいるすべての人へ:コードをクリーンアップします。それは混乱です。また、1つの関数が1つのことを行うなど、重要なコーディング方法に違反しています。_process_inputs
と同様に、「入力の処理」以外にも多くのことを行い_standardize_user_data
ます。「私は十分に支払われていません」-しかし、あなたはあなた自身のものを理解するのに費やされた余分な時間で、そしてより明確なコードでより簡単に解決されるバグであなたの問題ページを埋めるユーザーで支払います。
概要:で少し遅くなりますcompile()
。
compile()
に異なる予測関数を割り当てる内部フラグを設定しpredict
ます。この関数は、呼び出しごとに新しいグラフを作成し、コンパイルされていない場合に比べて速度を低下させます。ただし、違いが顕著になるのは、トレーニング時間がデータ処理時間よりもはるかに短い場合のみです。我々は場合増加、少なくとも中規模のモデルのサイズを、2は等しくなります。下部のコードを参照してください。
データ処理時間のこのわずかな増加は、増幅されたグラフ機能によって十分に補われます。モデルグラフを1つだけ保持する方が効率的であるため、1つのプリコンパイルは破棄されます。それでも、モデルがデータに比べて小さい場合はcompile()
、モデルを推論しなくてもよいでしょう。回避策については、他の回答を参照してください。
私は何をすべきか?
一番下のコードにあるように、コンパイルされたモデルとコンパイルされていないモデルのパフォーマンスを比較します。
- コンパイルはより高速です:
predict
コンパイルされたモデルで実行します。
- コンパイルが遅い:コンパイルさ
predict
れていないモデルで実行します。
はい、どちらも可能であり、(1)データサイズに依存します。(2)モデルサイズ; (3)ハードウェア。一番下のコードは実際にはコンパイルされたモデルの方が高速であることを示していますが、10回の反復は小さなサンプルです。「ハウツー」については、他の回答の「回避策」を参照してください。
詳細:
これはデバッグに少し時間がかかりましたが、楽しかったです。以下では、私が発見した主要な犯人について説明し、いくつかの関連文書を引用し、究極のボトルネックにつながったプロファイラーの結果を示します。
(FLAG == self.experimental_run_tf_function
、簡潔にするため)
Model
デフォルトではでインスタンス化されますFLAG=False
。compile()
に設定しTrue
ます。
predict()
予測関数を取得することを含み、 func = self._select_training_loop(x)
predict
とcompile
に渡される特別なクワーグがなければ、他のすべてのフラグは次のようになります。
- (A)
FLAG==True
->func = training_v2.Loop()
- (B)
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
- ソースコードdocstringから、(A)はグラフに大きく依存しており、より多くの分散戦略を使用しており、opはグラフ要素を作成および破棄する傾向があり、パフォーマンスに「影響する」可能性があります(します)。
真の原因:_process_inputs()
、ランタイムの81%を占めています。その主要なコンポーネントは?_create_graph_function()
、ランタイムの72%。この方法でもない存在のために(B) 。中規模モデルを使用して、しかし、_process_inputs
含むランタイムの1%未満。下部にコード、プロファイリング結果が続きます。
データプロセッサ:
(A):、<class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
で使用され_process_inputs()
ます。関連するソースコード
(B) :numpy.ndarray
によって返さconvert_eager_tensors_to_numpy
。関連するソースコード、そしてここ
モデル実行機能(例:予測)
(A):分布関数、およびここ
(B):分布関数(異なる)、ここ
プロファイラー:私の他の回答「小さなモデル」、およびこの回答「中規模モデル」のコードの結果:
小さなモデル:1000反復、compile()
小さなモデル:1000回の反復、いいえ compile()
中規模モデル:10反復
の影響に関する文書化(間接的)compile()
:ソース
他のTensorFlow演算とは異なり、Pythonの数値入力をテンソルに変換しません。さらに、新しいグラフは、Pythonの数値ごとに生成されます。たとえば、呼び出してg(2)
、g(3)
2つの新しいグラフを生成します
function
入力形状とデータ型の一意のセットごとに個別のグラフをインスタンス化します。たとえば、次のコードスニペットでは、各入力の形状が異なるため、3つの異なるグラフがトレースされます。
単一のtf.functionオブジェクトは、内部で複数の計算グラフにマップする必要がある場合があります。これはパフォーマンスとしてのみ表示されます(トレースグラフの計算コストとメモリコストはゼロではありません)が、プログラムの正確さに影響を与えることはありません。
対抗法:
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)
出力:
34.8542 sec
34.7435 sec