มอบสิทธิ์การเร่งความเร็วของ GPU สำหรับ iOS

การใช้หน่วยประมวลผลกราฟิก (GPU) เพื่อเรียกใช้โมเดลแมชชีนเลิร์นนิง (ML) จะช่วยปรับปรุงประสิทธิภาพของโมเดลและประสบการณ์ของผู้ใช้ในแอปพลิเคชันที่เปิดใช้ ML ได้อย่างมาก ในอุปกรณ์ iOS คุณจะเปิดใช้การดำเนินการโมเดลแบบเร่งด้วย GPU ได้โดยใช้การมอบสิทธิ์ ผู้มอบสิทธิ์ทำหน้าที่เป็นไดรเวอร์ฮาร์ดแวร์สำหรับ TensorFlow Lite ซึ่งจะช่วยให้คุณเรียกใช้โค้ดของโมเดลบนตัวประมวลผล GPU ได้

หน้านี้อธิบายวิธีเปิดใช้การเร่ง GPU สำหรับโมเดล TensorFlow Lite ในแอป iOS ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้การมอบสิทธิ์ GPU สำหรับ TensorFlow Lite รวมถึงแนวทางปฏิบัติแนะนำและเทคนิคขั้นสูงได้ที่หน้าการมอบสิทธิ์ GPU

ใช้ GPU กับ Mediation API

Translation API ของ TensorFlow Lite มีชุด API อเนกประสงค์สำหรับสร้างแอปพลิเคชันแมชชีนเลิร์นนิง วิธีการต่อไปนี้จะแนะนำการเพิ่มการรองรับ GPU ในแอป iOS โดยคู่มือนี้จะสมมติว่าคุณมีแอป iOS ที่เรียกใช้โมเดล ML ด้วย TensorFlow Lite ได้สำเร็จ

แก้ไข Podfile ให้รวมการรองรับ GPU

ตั้งแต่รุ่น TensorFlow Lite 2.3.0 ระบบจะไม่รวมการมอบสิทธิ์ GPU ออกจากพ็อดเพื่อลดขนาดไบนารี คุณรวมรายการเหล่านี้ได้โดยระบุข้อกำหนดย่อยสำหรับพ็อด TensorFlowLiteSwift ดังนี้

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

หรือ

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

นอกจากนี้ คุณยังสามารถใช้ TensorFlowLiteObjC หรือ TensorFlowLiteC ได้หากต้องการใช้ Objective-C ซึ่งมีให้ใช้งานในเวอร์ชัน 2.4.0 ขึ้นไป หรือ C API

เริ่มต้นและใช้การมอบสิทธิ์ GPU

คุณจะใช้มอบสิทธิ์ GPU กับ API ของ TensorFlow Lite กับภาษาโปรแกรมต่างๆ ได้ ขอแนะนำให้ใช้ Swift และ Objective-C แต่คุณก็ใช้ C++ และ C ได้เช่นกัน คุณจำเป็นต้องใช้ C หากใช้ TensorFlow Lite เวอร์ชันที่เก่ากว่า 2.4 ตัวอย่างโค้ดต่อไปนี้จะสรุปวิธีใช้ผู้รับมอบสิทธิ์กับแต่ละภาษา

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

  • TensorFlow Lite เวอร์ชันก่อนหน้า 2.4.0 จะใช้ได้เฉพาะ C API สำหรับ Objective-C
  • C++ API จะพร้อมใช้งานเฉพาะเมื่อใช้ Bazel หรือสร้าง TensorFlow Lite ด้วยตัวเอง ใช้ API ของ C++ กับ CocoaPods ไม่ได้
  • เมื่อใช้ TensorFlow Lite กับการมอบสิทธิ์ GPU ที่มี C++ ให้รับการมอบสิทธิ์ GPU ผ่านฟังก์ชัน TFLGpuDelegateCreate() แล้วส่งต่อไปยัง Interpreter::ModifyGraphWithDelegate() แทนการเรียกใช้ Interpreter::AllocateTensors()

สร้างและทดสอบด้วยโหมดเผยแพร่

เปลี่ยนเป็นบิลด์ของรุ่นที่มีการตั้งค่าตัวเร่งของ Metal API ที่เหมาะสมเพื่อให้ได้ประสิทธิภาพที่ดียิ่งขึ้นและสำหรับการทดสอบในขั้นตอนสุดท้าย ส่วนนี้อธิบายวิธีเปิดใช้บิลด์ของรุ่นและกำหนดค่าสำหรับการเร่งความเร็วโลหะ

วิธีเปลี่ยนไปใช้บิลด์ของรุ่น

  1. แก้ไขการตั้งค่าบิลด์โดยเลือกผลิตภัณฑ์ > รูปแบบ > แก้ไขรูปแบบ... แล้วเลือกเรียกใช้
  2. ในแท็บข้อมูล ให้เปลี่ยนการกำหนดค่าบิลด์เป็นเผยแพร่ และยกเลิกการเลือกไฟล์ปฏิบัติการการแก้ไขข้อบกพร่อง ตั้งค่าการเผยแพร่
  3. คลิกแท็บตัวเลือกและเปลี่ยน GPU Frame Capture เป็น Disabled และ Metal API Validation เป็น Disabled
    การตั้งค่าตัวเลือกโลหะ
  4. อย่าลืมเลือกบิลด์เฉพาะรุ่นบนสถาปัตยกรรม 64 บิต ในส่วน ตัวนำทางโปรเจ็กต์ > tflite_camera_example > PROJECT > your_project_name > การตั้งค่าบิลด์ ให้ตั้งค่าสร้างสถาปัตยกรรมแบบใช้งานเท่านั้น > รุ่น เป็น Yes ตั้งค่าตัวเลือกรุ่น

การรองรับ GPU ขั้นสูง

ส่วนนี้ครอบคลุมการใช้งานขั้นสูงของการมอบสิทธิ์ GPU สำหรับ iOS รวมถึงตัวเลือกการมอบสิทธิ์ บัฟเฟอร์อินพุตและเอาต์พุต และการใช้โมเดลที่ปรับขนาดแล้ว

ตัวเลือกการมอบสิทธิ์สำหรับ 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)

// 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 คุณต้องแปลงเป็นออบเจ็กต์ MTLBuffer สำหรับ Metal ก่อน คุณสามารถเชื่อมโยง TfLiteTensor กับ MTLBuffer ที่ผู้ใช้สร้างขึ้นด้วยฟังก์ชัน TFLGpuDelegateBindMetalBufferToTensor() โปรดทราบว่าต้องเรียกใช้ฟังก์ชันนี้หลังจาก 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() สำหรับ Tensor เอาต์พุตแต่ละรายการ วิธีการนี้ยังใช้ได้กับโมเดลที่วัดปริมาณด้วย แต่คุณจะยังใช้บัฟเฟอร์ขนาด Float32 ที่มีข้อมูล Float32 ได้อยู่ เนื่องจากบัฟเฟอร์จะผูกกับบัฟเฟอร์ภายในที่ตัดออกเชิงปริมาณแล้ว

โมเดลที่แบ่งปริมาณ

ไลบรารีที่มอบสิทธิ์ GPU ของ iOS รองรับโมเดลที่เล็กลงโดยค่าเริ่มต้น คุณไม่จำเป็นต้องเปลี่ยนแปลงโค้ดใดๆ เพื่อใช้โมเดลที่วัดปริมาณด้วยตัวมอบสิทธิ์ 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