适用于 iOS 的 GPU 加速代理

使用图形处理单元 (GPU) 运行机器学习 (ML) 模型可以显著提高模型的性能和支持机器学习的应用的用户体验。在 iOS 设备上,您可以使用代理来启用由 GPU 加速执行的模型。委托可充当 TensorFlow Lite 的硬件驱动程序,可让您在 GPU 处理器上运行模型的代码。

本页面介绍如何在 iOS 应用中为 TensorFlow Lite 模型启用 GPU 加速。如需详细了解如何为 TensorFlow Lite 使用 GPU 代理,包括最佳实践和高级技术,请参阅 GPU 代理页面。

将 GPU 与 Explainer API 搭配使用

TensorFlow Lite Explainer API 提供了一组用于构建机器学习应用的通用 API。以下说明将指导您向 iOS 应用添加 GPU 支持。本指南假定您已经有一个可以使用 TensorFlow Lite 成功执行机器学习模型的 iOS 应用。

修改 Podfile 以支持 GPU

从 TensorFlow Lite 2.3.0 版本开始,GPU 代理已从 Pod 中排除,以缩减二进制文件的大小。您可以通过为 TensorFlowLiteSwift pod 指定子规范来包含这些规范:

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

OR

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

如果您想使用 Objective-C(适用于版本 2.4.0 及更高版本)或 C API,则也可以使用 TensorFlowLiteObjCTensorFlowLiteC

初始化和使用 GPU 代理

您可以将 GPU 代理与多种编程语言的 TensorFlow Lite Explainer API 搭配使用。建议使用 Swift 和 Objective-C,但您也可以使用 C++ 和 C。如果您使用的 TensorFlow Lite 版本低于 2.4,则必须使用 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。
  • 只有在您使用 bazel 或自行构建 TensorFlow Lite 时,才能使用 C++ API。C++ API 无法与 CocoaPods 搭配使用。
  • 将 TensorFlow Lite 与 GPU 委托 (C++) 搭配使用时,请通过 TFLGpuDelegateCreate() 函数获取 GPU 委托,然后将其传递给 Interpreter::ModifyGraphWithDelegate(),而不是调用 Interpreter::AllocateTensors()

在发布模式下进行构建和测试

更改为具有适当 Metal API 加速器设置的发布 build,以获得更好的性能并进行最终测试。本部分介绍如何启用发布 build 以及配置 Metal 加速设置。

如需更改为发布 build,请执行以下操作:

  1. 修改构建设置,方法是依次选择 Product > Scheme > Edit Scheme...,然后选择 Run
  2. Info 标签页上,将 Build Configuration 更改为 Release,然后取消选中 Debug executable设置版本
  3. 点击 Options 标签页,然后将 GPU Frame Capture 更改为 Disabled,将 Metal API Validation 更改为 Disabled
    在 Android Vitals 中
  4. 请务必选择基于 64 位架构的“仅限发布”build。在 Project navgator > tflite_camera_example > PROJECT > your_project_name > Build Settings 下,将 Build Active Architecture Only > Release 设置为 Yes在 Android Vitals 中

高级 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 纹理),则它可以保留在 GPU 内存中,而无需进入 CPU 内存。同样,如果网络的输出是可渲染图像的形式(例如图像样式转移操作),您可以直接在屏幕上显示结果。

为了实现最佳性能,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 代理概览。