Ủy quyền tăng tốc GPU cho iOS

Việc sử dụng bộ xử lý đồ hoạ (GPU) để chạy các mô hình học máy (ML) có thể giúp cải thiện đáng kể hiệu suất của mô hình cũng như trải nghiệm người dùng của các ứng dụng hỗ trợ công nghệ học máy. Trên thiết bị iOS, bạn có thể cho phép sử dụng tính năng thực thi có tăng tốc GPU cho các mô hình bằng cách sử dụng uỷ quyền. Đối tượng uỷ quyền đóng vai trò là trình điều khiển phần cứng cho TensorFlow Lite, cho phép bạn chạy mã cho mô hình của mình trên các bộ xử lý GPU.

Trang này mô tả cách bật tính năng tăng tốc GPU cho các mô hình TensorFlow Lite trong ứng dụng iOS. Để biết thêm thông tin về cách sử dụng uỷ quyền GPU cho TensorFlow Lite, bao gồm cả các phương pháp hay nhất và kỹ thuật nâng cao, hãy xem trang uỷ quyền GPU.

Sử dụng GPU với API Phiên dịch

API Thông dịch của TensorFlow Lite cung cấp một bộ API đa năng để xây dựng các ứng dụng học máy. Các hướng dẫn sau đây sẽ hướng dẫn bạn cách thêm tính năng hỗ trợ GPU vào ứng dụng iOS. Hướng dẫn này giả định rằng bạn đã có một ứng dụng iOS có thể thực thi thành công mô hình học máy bằng TensorFlow Lite.

Sửa đổi Podfile để thêm tính năng hỗ trợ GPU

Kể từ bản phát hành TensorFlow Lite 2.3.0, uỷ quyền GPU sẽ bị loại trừ khỏi nhóm để giảm kích thước nhị phân. Bạn có thể bao gồm chúng bằng cách chỉ định thông số phụ cho nhóm TensorFlowLiteSwift:

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

OR

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

Bạn cũng có thể sử dụng TensorFlowLiteObjC hoặc TensorFlowLiteC nếu muốn sử dụng Objective-C, có sẵn cho phiên bản 2.4.0 trở lên hoặc API C.

Khởi động và sử dụng tính năng uỷ quyền GPU

Bạn có thể sử dụng đại biểu GPU thông qua API Phiên dịch của TensorFlow Lite với một số ngôn ngữ lập trình. Bạn nên dùng Swift và Objective-C, nhưng bạn cũng có thể sử dụng C++ và C. Bạn bắt buộc phải sử dụng C nếu đang sử dụng phiên bản TensorFlow Lite cũ hơn 2.4. Các ví dụ về mã sau đây trình bày cách sử dụng uỷ quyền với từng ngôn ngữ trong số này.

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 (trướ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);
      

Ghi chú về việc sử dụng ngôn ngữ của API GPU

  • Các phiên bản TensorFlow Lite trước 2.4.0 chỉ có thể sử dụng API C cho Objective-C.
  • Bạn chỉ có thể sử dụng API C++ khi sử dụng bazel hoặc tự xây dựng TensorFlow Lite. Bạn không thể sử dụng API C++ với CocoaPods.
  • Khi sử dụng TensorFlow Lite với uỷ quyền GPU với C++, hãy lấy uỷ quyền GPU thông qua hàm TFLGpuDelegateCreate(), sau đó truyền nó đến Interpreter::ModifyGraphWithDelegate(), thay vì gọi Interpreter::AllocateTensors().

Xây dựng và kiểm thử bằng chế độ phát hành

Hãy thay đổi sang bản phát hành có các chế độ cài đặt trình tăng tốc API Metal phù hợp để đạt được hiệu suất cao hơn và kiểm thử lần cuối. Phần này giải thích cách bật bản phát hành và định cấu hình chế độ cài đặt cho tính năng Tăng tốc Metal.

Cách thay đổi thành bản phát hành:

  1. Chỉnh sửa chế độ cài đặt bản dựng bằng cách chọn Product > Scheme > Edit Scheme... (Sản phẩm > Lược đồ > Chỉnh sửa lược đồ...) rồi chọn Run (Chạy).
  2. Trên thẻ Info (Thông tin), hãy thay đổi Build Configuration (Cấu hình bản dựng) thành Release (Bản phát hành) và bỏ đánh dấu chọn Debug thực thi (Gỡ lỗi tệp thực thi). thiết lập bản phát hành
  3. Nhấp vào thẻ Options (Tuỳ chọn) rồi thay đổi GPU Frame Capture (Chụp khung hình GPU) thành Disabled (Tắt) và Metal API Validation (Xác thực API Kim loại) thành Disabled (Tắt).
    thiết lập các tuỳ chọn kim loại
  4. Hãy nhớ chọn Bản dựng chỉ phát hành trên kiến trúc 64 bit. Trong phần Project navigator > tflite_camera_example > PROJECT > your_project_name > Build Settings (Trình điều hướng dự án > Trình điều hướng dự án > tflite_camera_example > PROJECT > your_project_name > Cài đặt bản dựng), hãy đặt Build Active Architecture Only > Release (Chỉ kiến trúc hoạt động > Phát hành) thành Yes (Có). thiết lập tuỳ chọn phát hành

Hỗ trợ GPU nâng cao

Phần này đề cập đến các cách sử dụng nâng cao của đại biểu GPU cho iOS, bao gồm cả các tuỳ chọn uỷ quyền, vùng đệm đầu vào và đầu ra, cũng như việc sử dụng các mô hình lượng tử hoá.

Tuỳ chọn uỷ quyền cho iOS

Hàm khởi tạo cho tính năng uỷ quyền GPU chấp nhận struct các tuỳ chọn trong API Swift, API Objective-CAPI C. Việc truyền nullptr (API C) hoặc không chuyển (API Objective-C và Swift) đến trình khởi tạo sẽ đặt các tuỳ chọn mặc định (được giải thích trong ví dụ về Cách sử dụng cơ bản ở trên).

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

Vùng đệm đầu vào/đầu ra sử dụng API C++

Việc tính toán trên GPU yêu cầu GPU có sẵn dữ liệu. Yêu cầu này thường có nghĩa là bạn phải sao chép bộ nhớ. Bạn nên tránh để dữ liệu vượt quá ranh giới bộ nhớ của CPU/GPU nếu có thể, vì điều này có thể gây mất một khoảng thời gian đáng kể. Thông thường, những trường hợp như vậy là không thể tránh khỏi, nhưng trong một số trường hợp đặc biệt, bạn có thể bỏ qua một trong hai trường hợp trên.

Nếu dữ liệu đầu vào của mạng là một hình ảnh đã được tải trong bộ nhớ GPU (ví dụ: hoạ tiết GPU chứa nguồn cấp dữ liệu máy ảnh), thì dữ liệu đó có thể nằm trong bộ nhớ GPU mà không cần đi vào bộ nhớ CPU. Tương tự, nếu đầu ra của mạng ở dạng hình ảnh có thể kết xuất hình ảnh, chẳng hạn như thao tác chuyển kiểu hình ảnh, thì bạn có thể trực tiếp hiển thị kết quả trên màn hình.

Để đạt được hiệu suất cao nhất, TensorFlow Lite giúp người dùng có thể trực tiếp đọc và ghi vào vùng đệm phần cứng TensorFlow, đồng thời bỏ qua các bản sao bộ nhớ tránh được.

Giả sử hình ảnh đầu vào nằm trong bộ nhớ GPU, trước tiên, bạn phải chuyển đổi hình ảnh đó thành đối tượng MTLBuffer cho Metal. Bạn có thể liên kết TfLiteTensor với MTLBuffer do người dùng chuẩn bị với hàm TFLGpuDelegateBindMetalBufferToTensor(). Xin lưu ý rằng hàm này phải được gọi sau Interpreter::ModifyGraphWithDelegate(). Ngoài ra, theo mặc định, kết quả dự đoán được sao chép từ bộ nhớ GPU sang bộ nhớ CPU. Bạn có thể tắt hành vi này bằng cách gọi Interpreter::SetAllowBufferHandleOutput(true) trong quá trình khởi chạy.

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;
      

Sau khi tắt hành vi mặc định, việc sao chép đầu ra dự đoán từ bộ nhớ GPU sang bộ nhớ CPU sẽ yêu cầu một lệnh gọi rõ ràng đến Interpreter::EnsureTensorDataIsReadable() cho mỗi tensor đầu ra. Phương pháp này cũng áp dụng cho các mô hình lượng tử hoá, nhưng bạn vẫn cần sử dụng bộ đệm có kích thước float32 với dữ liệu float32, vì bộ đệm này được liên kết với bộ đệm khử lượng tử nội bộ.

Mô hình lượng tử hoá

Theo mặc định, thư viện uỷ quyền GPU của iOS hỗ trợ các mô hình lượng tử hoá. Bạn không cần thực hiện bất cứ thay đổi nào về mã để sử dụng các mô hình lượng tử hoá với uỷ quyền GPU. Phần sau đây giải thích cách tắt tính năng hỗ trợ lượng tử cho mục đích kiểm thử hoặc thử nghiệm.

Tắt tính năng hỗ trợ mô hình lượng tử hoá

Mã sau đây cho biết cách tắt tính năng hỗ trợ cho các mô hình lượng tử hoá.

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

Để biết thêm thông tin về cách chạy các mô hình lượng tử hoá bằng tính năng tăng tốc GPU, hãy xem nội dung tổng quan về uỷ quyền GPU.