iOS 用の GPU アクセラレーション デリゲート

画像処理装置(GPU)を使用して機械学習(ML)モデルを実行すると、モデルのパフォーマンスと ML 対応アプリケーションのユーザー エクスペリエンスを大幅に改善できます。iOS デバイスでは、デリゲートを使用することで、GPU アクセラレーションによるモデル実行を有効にできます。デリゲートは、TensorFlow Lite のハードウェア ドライバとして機能し、GPU プロセッサでモデルのコードを実行できます。

このページでは、iOS アプリで TensorFlow Lite モデルの GPU アクセラレーションを有効にする方法について説明します。ベスト プラクティスや高度な手法など、TensorFlow Lite の GPU デリゲートの使用について詳しくは、GPU デリゲートのページをご覧ください。

Interpreter API で GPU を使用する

TensorFlow Lite インタープリタ API は、ML アプリケーションを構築するための汎用 API のセットを提供します。以下の手順では、iOS アプリに GPU サポートを追加する方法について説明します。このガイドでは、TensorFlow Lite を使用して ML モデルを正常に実行できる iOS アプリがすでにあることを前提としています。

Podfile を変更して GPU サポートを追加する

TensorFlow Lite 2.3.0 リリース以降では、バイナリサイズを削減するために、Pod から GPU デリゲートが除外されています。これを含めるには、TensorFlowLiteSwift Pod のサブ仕様を指定します。

pod 'TensorFlowLiteSwift/Metal', '~> 0.0.1-nightly',

または

pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']

また、バージョン 2.4.0 以降で利用可能な Objective-C、または C API を使用する場合は、TensorFlowLiteObjC または TensorFlowLiteC を使用することもできます。

GPU デリゲートを初期化して使用する

さまざまなプログラミング言語の TensorFlow Lite インタープリタ API で GPU デリゲートを使用できます。Swift と Objective-C をおすすめしますが、C++ と C も使用できます。2.4 より前のバージョンの TensorFlow Lite を使用している場合は、C を使用する必要があります。次のコードサンプルは、これらの各言語でデリゲートを使用する方法を示しています。

Swift

import TensorFlowLite

// Load model ...

// Initialize TensorFlow Lite interpreter with the GPU delegate.
let delegate = MetalDelegate()
if let interpreter = try Interpreter(modelPath: modelPath,
                                      delegates: [delegate]) {
  // Run inference ...
}
      

Objective-C

// Import module when using CocoaPods with module support
@import TFLTensorFlowLite;

// Or import following headers manually
#import "tensorflow/lite/objc/apis/TFLMetalDelegate.h"
#import "tensorflow/lite/objc/apis/TFLTensorFlowLite.h"

// Initialize GPU delegate
TFLMetalDelegate* metalDelegate = [[TFLMetalDelegate alloc] init];

// Initialize interpreter with model path and GPU delegate
TFLInterpreterOptions* options = [[TFLInterpreterOptions alloc] init];
NSError* error = nil;
TFLInterpreter* interpreter = [[TFLInterpreter alloc]
                                initWithModelPath:modelPath
                                          options:options
                                        delegates:@[ metalDelegate ]
                                            error:&error];
if (error != nil) { /* Error handling... */ }

if (![interpreter allocateTensorsWithError:&error]) { /* Error handling... */ }
if (error != nil) { /* Error handling... */ }

// Run inference ...
      

C++

// Set up interpreter.
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
tflite::ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);

// Prepare GPU delegate.
auto* delegate = TFLGpuDelegateCreate(/*default options=*/nullptr);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));

// Clean up.
TFLGpuDelegateDelete(delegate);
      

C(2.4.0 より前)

#include "tensorflow/lite/c/c_api.h"
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"

// Initialize model
TfLiteModel* model = TfLiteModelCreateFromFile(model_path);

// Initialize interpreter with GPU delegate
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();
TfLiteDelegate* delegate = TFLGPUDelegateCreate(nil);  // default config
TfLiteInterpreterOptionsAddDelegate(options, metal_delegate);
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
TfLiteInterpreterOptionsDelete(options);

TfLiteInterpreterAllocateTensors(interpreter);

NSMutableData *input_data = [NSMutableData dataWithLength:input_size * sizeof(float)];
NSMutableData *output_data = [NSMutableData dataWithLength:output_size * sizeof(float)];
TfLiteTensor* input = TfLiteInterpreterGetInputTensor(interpreter, 0);
const TfLiteTensor* output = TfLiteInterpreterGetOutputTensor(interpreter, 0);

// Run inference
TfLiteTensorCopyFromBuffer(input, inputData.bytes, inputData.length);
TfLiteInterpreterInvoke(interpreter);
TfLiteTensorCopyToBuffer(output, outputData.mutableBytes, outputData.length);

// Clean up
TfLiteInterpreterDelete(interpreter);
TFLGpuDelegateDelete(metal_delegate);
TfLiteModelDelete(model);
      

GPU API 言語使用に関する注意事項

  • 2.4.0 より前のバージョンの TensorFlow Lite では、Objective-C 用の C API のみを使用できます。
  • C++ API は、bazel を使用している場合、または TensorFlow Lite を独自にビルドしている場合にのみ使用できます。CocoaPods では C++ API を使用できません。
  • C++ で TensorFlow Lite と GPU デリゲートを使用する場合は、Interpreter::AllocateTensors() を呼び出す代わりに、TFLGpuDelegateCreate() 関数で GPU デリゲートを取得して、Interpreter::ModifyGraphWithDelegate() に渡します。

リリースモードでのビルドとテスト

パフォーマンスを向上させ、最終テストを行うには、適切な Metal API アクセラレータ設定のリリースビルドに変更します。このセクションでは、リリースビルドを有効にし、Metal アクセラレーションを設定する方法について説明します。

リリースビルドに変更するには:

  1. [Product] > [Scheeme] > [Edit Scheme...] の順に選択し、[Run] を選択してビルド設定を編集します。
  2. [Info] タブで、[Build Configuration] を [Release] に変更し、[Debug executable] チェックボックスをオフにします。リリースの設定
  3. [Options] タブをクリックし、[GPU Frame Capture] を [Disabled] に、[Metal API Validation] を [Disabled] に変更します。
    メタルオプションの設定
  4. 必ず [64 ビット アーキテクチャのリリース専用ビルド] を選択してください。[Project navigator] > [tflite_camera_example] > [PROJECT] > [your_project_name] > [Build Settings] で、[Build Active Architecture Only] > [Release] を [Yes] に設定します。リリースオプションの設定

高度な GPU サポート

このセクションでは、デリゲート オプション、入出力バッファ、量子化モデルの使用など、iOS 用の GPU デリゲートの高度な使用方法について説明します。

iOS 向けの委任オプション

GPU デリゲートのコンストラクタは、Swift APIObjective-C APIC API のオプションの struct を受け入れます。イニシャライザに 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)

// IS THE SAME AS THIS:
let delegate = MetalDelegate()
      

Objective-C

// 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];
      

C

// 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);
      

C++ API を使用した入出力バッファ

GPU で計算を行うには、GPU がデータを利用できる必要があります。この要件は、多くの場合、メモリのコピーを実行する必要があることを意味します。かなりの時間がかかる可能性があるため、可能であれば、データが CPU/GPU メモリの境界を超えることは避ける必要があります。通常、このような交差は避けられませんが、特別なケースでは、どちらか一方を省略できることもあります。

ネットワークの入力が GPU メモリにすでに読み込まれている画像(カメラフィードを含む GPU テクスチャなど)の場合は、CPU メモリに入らなくても GPU メモリに留まることができます。同様に、ネットワークの出力がレンダリング可能な画像形式(画像スタイル転送オペレーションなど)の場合は、結果を画面に直接表示できます。

最高のパフォーマンスを実現するため、TensorFlow Lite ではユーザーが TensorFlow ハードウェア バッファから直接読み書きでき、回避可能なメモリコピーをバイパスできます。

画像入力が GPU メモリ内にある場合は、まずそれを Metal 用の MTLBuffer オブジェクトに変換する必要があります。TFLGpuDelegateBindMetalBufferToTensor() 関数を使用して、TfLiteTensor をユーザーが準備した MTLBuffer に関連付けることができます。この関数は Interpreter::ModifyGraphWithDelegate() の後に呼び出す必要があります。また、推論出力はデフォルトで GPU メモリから CPU メモリにコピーされます。この動作を無効にするには、初期化中に Interpreter::SetAllowBufferHandleOutput(true) を呼び出します。

C++

#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#include "tensorflow/lite/delegates/gpu/metal_delegate_internal.h"

// ...

// Prepare GPU delegate.
auto* delegate = TFLGpuDelegateCreate(nullptr);

if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

interpreter->SetAllowBufferHandleOutput(true);  // disable default gpu->cpu copy
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->inputs()[0], user_provided_input_buffer)) {
  return false;
}
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->outputs()[0], user_provided_output_buffer)) {
  return false;
}

// Run inference.
if (interpreter->Invoke() != kTfLiteOk) return false;
      

デフォルトの動作を無効にすると、GPU メモリから CPU メモリに推論出力をコピーするには、出力テンソルごとに Interpreter::EnsureTensorDataIsReadable() を明示的に呼び出す必要があります。このアプローチは量子化モデルでも機能しますが、バッファは内部の逆量子化バッファにバインドされているため、float32 データに float32 サイズのバッファを使用する必要があります。

量子化モデル

iOS GPU デリゲート ライブラリは、デフォルトで量子化モデルをサポートしています。GPU デリゲートで量子化モデルを使用するために、コードを変更する必要はありません。次のセクションでは、テストや試験運用版の目的で量子化サポートを無効にする方法について説明します。

量子化モデルのサポートを無効にする

次のコードは、量子化モデルのサポートを無効にする方法を示しています。

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;

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

GPU アクセラレーションを使用した量子化モデルの実行の詳細については、GPU デリゲートの概要をご覧ください。