Path: blob/master/site/ja/lite/performance/gpu_advanced.md
38423 views
GPU で TensorFlow Lite を使用する
TensorFlow Lite では、複数のハードウェアアクセラレータがサポートされています。このドキュメントでは、Android(OpenCL または OpenGL ES 3.1 以上)および iOS(iOS 8 以上)で TensorFlow Lite デリゲート API を使用して GPU バックエンドを使用する方法について説明します。
GPU アクセラレーションの利点
速度
GPU は、大規模に実行する並列化可能なワークロードで高い処理能力を実現するように設計されています。そのため、これは多数の演算で構成されるディープニューラルネットに適しています。各演算は、より小さなワークロードに簡単に分割でき、並列に実行する入力テンソルで機能するため、通常レイテンシが低くなります。現在、最良のシナリオでは、GPU での推論は以前は利用できなかったリアルタイムアプリケーションで十分に速く実行できます。
精度
GPU は、16 ビットまたは 32 ビットの浮動小数点数を使用して計算を行い、(CPU とは異なり)最適なパフォーマンスを得るために量子化を必要としません。精度の低下によりモデルでの量子化が不可能になる場合、GPU でニューラルネットワークを実行すると、この問題が解消される場合があります。
電力効率
GPU の推論のもう 1 つの利点は、電力効率です。GPU は非常に効率的かつ最適化された方法で計算を実行するため、同じタスクを CPU で実行する場合よりも消費電力と発熱が少なくなります。
サポートする演算子
GPU では TensorFlow Lite は、16 ビットおよび 32 ビットの浮動小数点精度で次の演算をサポートします。
ADDAVERAGE_POOL_2DCONCATENATIONCONV_2DDEPTHWISE_CONV_2D v1-2EXPFULLY_CONNECTEDLOGISTICLSTM v2(Basic LSTM のみ)MAX_POOL_2DMAXIMUMMINIMUMMULPADPRELURELURELU6RESHAPERESIZE_BILINEAR v1-3SOFTMAXSTRIDED_SLICESUBTRANSPOSE_CONV
デフォルトでは、すべての演算はバージョン 1 でのみサポートされています。実験的な量子化サポートを有効にすると、適切なバージョンが許可されます (ADD v2 など)。
基本的な使い方
Android でモデルアクセラレーションを呼び出す方法は 2 つありますが、Android Studio ML Model Binding または TensorFlow Lite インタープリタを使用しているかによって、方法は異なります。
TensorFlow Lite Interpreter を使用して Android でモデルアクセラレーションを呼び出す
tensorflow-lite-gpuパッケージを既存のtensorflow-liteパッケージと共に、既存のdependenciesブロックに追加します。
次に、TfLiteDelegateを使用して GPU で TensorFlow Lite を実行します。Java では、Interpreter.OptionsからGpuDelegateを指定できます。
Kotlin
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.CompatibilityList
import org.tensorflow.lite.gpu.GpuDelegate import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.gpu.CompatibilityList;
import org.tensorflow.lite.gpu.GpuDelegate;
// Initialize interpreter with GPU delegate
Interpreter.Options options = new Interpreter.Options();
CompatibilityList compatList = CompatibilityList();
if(compatList.isDelegateSupportedOnThisDevice()){
// if the device has a supported GPU, add the GPU delegate
GpuDelegate.Options delegateOptions = compatList.getBestOptionsForThisDevice();
GpuDelegate gpuDelegate = new GpuDelegate(delegateOptions);
options.addDelegate(gpuDelegate);
} else {
// if the GPU is not supported, run on 4 threads
options.setNumThreads(4);
}
Interpreter interpreter = new Interpreter(model, options);
// Run inference
writeToInput(input);
interpreter.run(input, output);
readFromOutput(output);
Android (C/C++)
Android C/C++ 向け TensorFlow Lite GPU を使用する場合、GPU デリゲートはTfLiteGpuDelegateV2Create()で作成し、TfLiteGpuDelegateV2Delete()で破棄できます。
TfLiteGpuDelegateOptionsV2を見て、カスタムオプションを使用してデリゲートインスタンスを作成します。TfLiteGpuDelegateOptionsV2Default()でデフォルトオプションを初期化し、必要に応じて変更します。
Android C/C++ 向け TFLite GPU では、Bazel ビルドシステムを使用します。デリゲートは、次のコマンドなどを使用して構築できます。
注意: Interpreter::ModifyGraphWithDelegate()またはInterpreter::Invoke()を呼び出す場合、呼び出し元はその時点のスレッドにEGLContextを持ち、Interpreter::Invoke()は、同じEGLContextから呼び出す必要があります。EGLContextが存在しない場合、デリゲートは内部的に作成しますが、開発者はInterpreter::Invoke()が常にInterpreter::ModifyGraphWithDelegate()を呼び出すスレッドと同じスレッドから呼び出されるようにする必要があります。
iOS (C++)
注意:Swift/Objective-C/C のユースケースについては、GPU デリゲートガイドを参照してください。
注意:これは、bazel を使用している場合、または TensorFlow Lite を自分でビルドしている場合にのみ使用できます。C++ API は CocoaPods では使用できません。
GPU で TensorFlow Lite を使用するには、TFLGpuDelegateCreate()を介して GPU デリゲートを取得し、(Interpreter::AllocateTensors()を呼び出す代わりに)それをInterpreter::ModifyGraphWithDelegate()に渡します。
高度な利用法
iOS のデリゲートオプション
GPU デリゲートのコンストラクタは、オプションのstructを受け入れます。(Swift API、Objective-C API、C API)
nullptr(C API)を初期化子に渡すと、または初期化子に何も渡さないと(Objective-C と Swift API)、デフォルトのオプションが設定されます(上記の基本的な使用例で説明されています)。
Swift
// THIS:
var options = MetalDelegate.Options()
options.isPrecisionLossAllowed = false
options.waitType = .passive
options.isQuantizationEnabled = true
let delegate = MetalDelegate(options: options) // THIS:
TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
options.precisionLossAllowed = false;
options.waitType = TFLMetalDelegateThreadWaitTypePassive;
options.quantizationEnabled = true;
TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] initWithOptions:options];
// IS THE SAME AS THIS:
TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] init];
// THIS:
const TFLGpuDelegateOptions options = {
.allow_precision_loss = false,
.wait_type = TFLGpuDelegateWaitType::TFLGpuDelegateWaitTypePassive,
.enable_quantization = true,
};
TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
// IS THE SAME AS THIS:
TfLiteDelegate* delegate = TFLGpuDelegateCreate(nullptr);
nullptrまたはデフォルトのコンストラクタを使用すると便利ですが、オプションを明示的に設定して、将来デフォルト値が変更された場合の予期しない動作を回避することをお勧めします。
GPU で量子化モデルを実行する
このセクションでは、GPU デリゲートが 8 ビットの量子化モデルを高速化する方法について説明します。以下のようなあらゆる種類の量子化が対象となります。
量子化認識トレーニングでトレーニングされたモデル
トレーニング後のダイナミックレンジ量子化
パフォーマンスを最適化するには、浮動小数点入出力テンソルを持つモデルを使用します。
仕組み
GPU バックエンドは浮動小数点の実行のみをサポートするため、元のモデルの「浮動小数点ビュー」を与えて量子化モデルを実行します。上位レベルで、次のような手順が含まれます。
定数テンソル(重み/バイアスなど)は、GPU メモリに一度逆量子化されます。これは、デリゲートが TFLite Interpreter に適用されるときに発生します。
8 ビット量子化されている場合、GPU プログラムへの入出力は、推論ごとにそれぞれ逆量子化および量子化されます。これは、TFLite の最適化されたカーネルを使用して CPU 上で行われます。
GPU プログラムは、演算の間に量子化シミュレータを挿入することにより、量子化された動作を模倣するように変更されます。これは、演算時にアクティベーションが量子化中に学習された境界に従うことが期待されるモデルに必要です。
この機能は、次のデリゲートオプションを使用して有効にできます。
Android
Android API は、デフォルトで量子化モデルをサポートしています。無効にするには、次の手順に従います。
C++ API
Java API
iOS
iOS API は、デフォルトで量子化モデルをサポートしています。無効にするには、次の手順に従います。
Swift
var options = MetalDelegate.Options()
options.isQuantizationEnabled = false
let delegate = MetalDelegate(options: options)
Objective-C
TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
options.quantizationEnabled = false;
C
TFLGpuDelegateOptions options = TFLGpuDelegateOptionsDefault();
options.enable_quantization = false;入出力バッファ(iOS、C++ API のみ)
注意:これは、bazel を使用している場合、または TensorFlow Lite を自分でビルドしている場合にのみ使用できます。C++ API は CocoaPods では使用できません。
GPU で計算を実行するには、データを GPU で使用できるようにする必要があり、多くの場合、メモリコピーの実行が必要になります。これにはかなり時間がかかる可能性があるため、可能であれば CPU/GPU のメモリ境界を超えないようにしてください。通常、このような交差は避けられませんが、一部の特殊なケースでは、どちらか一方を省略できます。
ネットワークの入力が GPU メモリに既に読み込まれている画像(たとえば、カメラフィードを含む GPU テクスチャ)である場合、CPU メモリに読み込むことなく、GPU メモリに保持できます。また、ネットワークの出力がレンダリング可能な画像(たとえば、画像スタイルの転送)の形式である場合は、画面に直接表示できます。
TensorFlow Lite では、最高のパフォーマンスを実現するために、TensorFlow ハードウェアバッファから直接読み書きできるので、回避可能なメモリコピーをバイパスできます。
画像入力が GPU メモリにある場合、最初に Metal のMTLBufferオブジェクトに変換する必要があります。TfLiteTensor をユーザーが準備したMTLBufferにTFLGpuDelegateBindMetalBufferToTensor()を関連付けることができます。TFLGpuDelegateBindMetalBufferToTensor()は、Interpreter::ModifyGraphWithDelegate()の後に呼び出す必要があることに注意してください。さらに、推論出力はデフォルトで、GPU メモリから CPU メモリにコピーされます。この動作は、初期化中にInterpreter::SetAllowBufferHandleOutput(true)を呼び出すことで無効にできます。
注意: デフォルトの動作が無効になっている場合、GPU メモリから CPU メモリに推論出力をコピーするには、各出力テンソルに対してInterpreter::EnsureTensorDataIsReadable()を明示的に呼び出す必要があります。
注意: これは量子化モデルでも機能しますが、バッファは内部の逆量子化バッファにバインドされるため、float32 データを含む float32 サイズのバッファが必要です。
GPU デリゲートのシリアル化
前の初期化からの GPU カーネルコードとモデルデータのシリアル化を使用すると、GPU デリゲートの初期化のレイテンシーを 90% まで抑えることができます。この改善は、時間を節約するためにディスク容量を交換することで達成されます。この機能は、以下のサンプルコードで示されるように、いくつかの構成オプションで有効にすることができます。
C++
TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default();
options.experimental_flags |= TFLITE_GPU_EXPERIMENTAL_FLAGS_ENABLE_SERIALIZATION;
options.serialization_dir = kTmpDir;
options.model_token = kModelToken; GpuDelegate delegate = new GpuDelegate(
new GpuDelegate.Options().setSerializationParams(
/* serializationDir= */ serializationDir,
/* modelToken= */ modelToken));
Interpreter.Options options = (new Interpreter.Options()).addDelegate(delegate);
シリアル化機能を使用する場合、コードが以下の実装ルールでコンパイルすることを確認してください。
シリアル化データを他のアプリがアクセスできないディレクトリに保存します。Android デバイスでは、現在のアプリケーションに非公開の場所にポイントする
getCodeCacheDir()を使用します。モデルトークンは、特定のモデルのデバイスに一意である必要があります。モデルトークンは、モデルデータからフィンガープリントを生成することで計算できます(
farmhash::Fingerprint64を使用するなど)。
注意: この機能には、シリアル化サポートを提供する OpenCL SDK が必要です。
ヒントとコツ
演算によっては CPU では簡単で GPU ではコストが高くなる可能性があります。このような演算の 1 つのクラスは、
BATCH_TO_SPACE、SPACE_TO_BATCH、SPACE_TO_DEPTHなど、さまざまな形の変形演算です。ネットワークアーキテクトの論理的思考のためだけにこれらの演算がネットワークに挿入されている場合、パフォーマンスのためにそれらを削除することをお勧めします。GPU では、テンソルデータは 4 チャネルにスライスされます。したがって、形状
[B,H,W,5]のテンソルに対する計算は、形状[B,H,W,8]のテンソルに対する計算とほぼ同じように実行されますが、パフォーマンスは[B,H,W,4]と比べて大幅に低下します。たとえば、カメラハードウェアが RGBA の画像フレームをサポートしている場合、メモリコピー (3 チャネル RGB から 4 チャネル RGBX へ) を回避できるため、4 チャネル入力のフィードは大幅に速くなります。
最高のパフォーマンスを得るには、モバイル向けに最適化されたネットワークアーキテクチャで分類器を再トレーニングします。これは、デバイス上の推論の最適化の重要な部分です。