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

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

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

インタプリタ API で GPU を使用する

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

Podfile を変更して GPU サポートを含める

TensorFlow Lite 2.3.0 リリース以降、バイナリ サイズを削減するために GPU デリゲートがポッドから除外されます。 TensorFlowLiteSwiftポッドのサブスペックを指定することで、それらを含めることができます。

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 デリゲートを初期化して使用する

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

迅速

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 ...
}
      

目的-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言語使用上の注意事項

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

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

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

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

  1. [Product] > [Scheme] > [Edit Scheme...]を選択し、 [Run]を選択してビルド設定を編集します。
  2. [情報]タブで、 [ビルド構成] を[リリース]に変更し、 [実行可能ファイルのデバッグ]のチェックを外します。リリースの設定
  3. [オプション]タブをクリックし、 [GPU フレーム キャプチャ] を [無効]に、 [メタル API 検証][無効]に変更します。
    メタルオプションの設定
  4. 必ず 64 ビット アーキテクチャ上のリリース専用ビルドを選択してください。 [プロジェクト ナビゲーター] > [tflite_camera_example] > [プロジェクト] > [your_project_name] > [ビルド設定] で、 [アクティブなアーキテクチャのみのビルド] > [リリース][はい]に設定します。 リリースオプションの設定

高度な GPU サポート

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

iOS のデリゲート オプション

GPU デリゲートのコンストラクターは、 Swift APIObjective-C API 、およびC APIのオプションのstructを受け入れます。 nullptr (C API) または何も渡さない (Objective-C および Swift API) をイニシャライザに渡すと、デフォルトのオプションが設定されます (これについては、上記の基本的な使用例で説明しています)。

迅速

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

目的-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 デリゲートで量子化モデルを使用するためにコードを変更する必要はありません。次のセクションでは、テストまたは実験目的で量子化サポートを無効にする方法について説明します。

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

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

迅速

    var options = MetalDelegate.Options()
    options.isQuantizationEnabled = false
    let delegate = MetalDelegate(options: options)
      

目的-C

    TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
    options.quantizationEnabled = false;
      

C

    TFLGpuDelegateOptions options = TFLGpuDelegateOptionsDefault();
    options.enable_quantization = false;

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

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