משתמש להאצת GPU ל-iOS

השימוש ביחידות עיבוד גרפיות (GPU) להפעלת מודלים של למידת מכונה (ML) יכול לשפר משמעותית את ביצועי המודל ואת חוויית המשתמש באפליקציות שתומכות ב-ML. במכשירי iOS, אפשר להשתמש בהענקת גישה כדי להפעיל את המודלים שלך עם האצת GPU. בעלי הגישה פועלים כמנהלי התקנים של חומרה עבור TensorFlow Lite, ומאפשרים לכם להריץ את קוד המודל במעבדי GPU.

בדף הזה נסביר איך מפעילים האצה של GPU בדגמי TensorFlow Lite באפליקציות ל-iOS. למידע נוסף על השימוש בהענקת גישה ל-GPU ב-TensorFlow Lite, כולל שיטות מומלצות וטכניקות מתקדמות, קראו את הדף משתמשים שקיבלו הרשאה ל-GPU.

שימוש ב-GPU עם ממשק API לתרגום

Translateer API של TensorFlow Lite מספק קבוצה של ממשקי API לשימוש כללי לצורך פיתוח אפליקציות של למידת מכונה. ההוראות הבאות מסבירות איך להוסיף תמיכה ב-GPU לאפליקציה ל-iOS. המדריך הזה מיועד בהנחה שכבר יש לכם אפליקציה ל-iOS שיכולה להפעיל מודל ML בהצלחה באמצעות TensorFlow Lite.

יש לשנות את קובץ ה-Podfile כך שיכלול תמיכה ב-GPU

החל מגרסה 2.3.0 של TensorFlow Lite, הענקת הגישה ל-GPU מוחרגת מה-pod כדי לצמצם את הגודל הבינארי. אפשר לכלול אותם על ידי ציון תת-מפרט ל-pod של 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 עם Translateer 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.
  • ה-API של C++ זמין רק כשמשתמשים ב-bazel או בונים את TensorFlow Lite בעצמכם. לא ניתן להשתמש ב-API של C++ עם CocoaPods.
  • כשמשתמשים ב-TensorFlow Lite עם נציג GPU באמצעות C++ , צריך לקבל את הגישה ל-GPU באמצעות הפונקציה TFLGpuDelegateCreate(), ואז להעביר אותה ל-Interpreter::ModifyGraphWithDelegate(), במקום להפעיל את Interpreter::AllocateTensors().

פיתוח ובדיקה במצב השקה

כדי להשיג ביצועים טובים יותר ולצורך בדיקה סופית, כדאי לעבור לגרסת build של גרסה עם ההגדרות המתאימות של פעולת ההאצה של Metal API. בקטע הזה מוסבר איך מפעילים גרסת build של גרסה ואיך קובעים הגדרות להאצת מטאל.

כדי לעבור לגרסת build של גרסה:

  1. כדי לערוך את הגדרות ה-build, בוחרים באפשרות Product > schema > Edit Scheme... ואז בוחרים באפשרות Run.
  2. בכרטיסייה מידע, משנים את Build Configuration לגרסה ומבטלים את הסימון של קובץ ההפעלה של ניפוי הבאגים. הגדרת השקה
  3. לוחצים על הכרטיסייה Options ומשנים את GPU Frame Capture כ-Disabled ואת האפשרות Metal API Validation ל-Disabled.
    הגדרת אפשרויות ממתכת
  4. חשוב לבחור גרסאות build בלבד של ארכיטקטורת 64 סיביות. בקטע Project navigator > tflite_camera_example > PROJECT > your_project_name > Build Settings (הגדרת ארכיטקטורה פעילה בלבד > גרסה) להגדרה Yes (כן). הגדרת אפשרויות גרסה

תמיכה מתקדמת ב-GPU

הקטע הזה עוסק בשימושים מתקדמים של התכונה 'הענקת גישה ל-GPU' ל-iOS, כולל אפשרויות הענקת גישה, מאגרי קלט ופלט ושימוש במודלים מכווננים.

אפשרויות האצלה ל-iOS

ה-builder של בעל הגישה ל-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);
      

מאגרי קלט/פלט באמצעות ממשק API של C++

חישוב ב-GPU מחייב שהנתונים יהיו זמינים ל-GPU. לעיתים קרובות, המשמעות היא שצריך ליצור עותק של הזיכרון. כדאי להימנע ככל האפשר ממגבלות הזיכרון של המעבד (CPU) או ה-GPU, מכיוון שהדבר עלול לגזול זמן רב. בדרך כלל מעבר כזה הוא בלתי נמנע, אבל במקרים מיוחדים מסוימים ניתן להשמיט אחד מהשניים.

אם הקלט מהרשת הוא תמונה שכבר נטענה בזיכרון ה-GPU (לדוגמה, טקסטורה של GPU שכוללת את פיד המצלמה), הוא יכול להישאר בזיכרון ה-GPU בלי להזין אף פעם את זיכרון המעבד. באופן דומה, אם הפלט של הרשת הוא בצורת תמונה שניתן לעבד, כמו פעולה של העברת סגנון תמונה, אפשר להציג את התוצאה ישירות במסך.

כדי להשיג את הביצועים הטובים ביותר, TensorFlow Lite מאפשר למשתמשים לקרוא ישירות ממאגר הנתונים הזמני של TensorFlow ולכתוב בו, ולעקוף עותקי זיכרון שאפשר להימנע מהם.

בהנחה שקלט התמונה נמצא בזיכרון GPU, קודם צריך להמיר אותו לאובייקט MTLBuffer עבור מתכת. אפשר לשייך 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() לכל ttensor פלט. הגישה הזו עובדת גם במודלים מספריים, אבל עדיין צריך להשתמש במאגר נתונים זמני בגודל 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.