تفويض تسريع وحدة معالجة الرسومات لنظام التشغيل iOS

يمكن أن يؤدي استخدام وحدات معالجة الرسومات (GPU) لتشغيل نماذج تعلُّم الآلة (ML) إلى تحسين أداء النموذج وتجربة المستخدم بشكل كبير في التطبيقات التي تمكِّن تكنولوجيا تعلُّم الآلة. على أجهزة iOS، يمكنك تفعيل استخدام تفويض تسريع تنفيذ وحدة معالجة الرسومات. تعمل البرامج المفوَّضة كبرامج تشغيل لأجهزة TensorFlow Lite، ما يسمح لك بتشغيل الرمز البرمجي للنموذج على معالِجات وحدة معالجة الرسومات.

توضّح هذه الصفحة كيفية تفعيل تسريع وحدة معالجة الرسومات لطُرز TensorFlow Lite في تطبيقات iOS. لمزيد من المعلومات حول استخدام مفوَّض وحدة معالجة الرسومات في TensorFlow Lite، بما في ذلك أفضل الممارسات والتقنيات المتقدمة، راجِع صفحة مفوَّضي وحدة معالجة الرسومات.

استخدام وحدة معالجة الرسومات مع واجهة برمجة التطبيقات الخاصة بميزة "الترجمة الفورية"

توفر واجهة برمجة تطبيقات الترجمة الفورية في TensorFlow Lite مجموعة من واجهات برمجة التطبيقات العامة للأغراض العامة لإنشاء تطبيقات التعلم الآلي. ترشدك التعليمات التالية إلى كيفية إضافة دعم وحدة معالجة الرسومات إلى أحد تطبيقات iOS. يفترض هذا الدليل أنّ لديك تطبيق iOS يمكنه تنفيذ نموذج تعلُّم الآلة بنجاح باستخدام TensorFlow Lite.

تعديل Podfile ليشمل دعم وحدة معالجة الرسومات

بدءًا من الإصدار TensorFlow Lite 2.3.0، يتم استبعاد تفويض وحدة معالجة الرسومات من المجموعة المتسلسلة لتقليل الحجم الثنائي. يمكنك تضمينها من خلال تحديد مواصفات فرعية لمجموعة 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.

إعداد تفويض وحدة معالجة الرسومات واستخدامه

يمكنك استخدام تفويض وحدة معالجة الرسومات مع واجهة برمجة التطبيقات الترجمة الفورية في 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);
      

ملاحظات حول استخدام لغة واجهة برمجة التطبيقات لوحدة معالجة الرسومات

  • يمكن لإصدارات TensorFlow Lite التي تسبق الإصدار 2.4.0 استخدام واجهة برمجة التطبيقات C API من أجل Objective-C فقط.
  • لا تتوفر واجهة برمجة تطبيقات C++ إلا عند استخدام bazel أو إصدار TensorFlow Lite بنفسك. لا يمكن استخدام واجهة برمجة تطبيقات C++ مع CocoaPods.
  • عند استخدام TensorFlow Lite مع مفوَّض وحدة معالجة الرسومات الذي لديه C++ ، يمكنك الحصول على تفويض وحدة معالجة الرسومات من خلال وظيفة TFLGpuDelegateCreate() ثم تمريره إلى Interpreter::ModifyGraphWithDelegate() بدلاً من إرسال طلب إلى Interpreter::AllocateTensors().

الإنشاء والاختبار باستخدام وضع الإصدار

يمكنك التغيير إلى إصدار باستخدام إعدادات مسرِّعة أعمال Metal API المناسبة للحصول على أداء أفضل وإجراء الاختبار النهائي. يوضح هذا القسم كيفية تفعيل إصدار وتهيئة إعداد لتسريع Metal.

للتبديل إلى إصدار إصدار:

  1. عدِّل إعدادات الإصدار من خلال اختيار المنتج > المخطط > تعديل المخطط...، ثم اختيار تشغيل.
  2. في علامة التبويب المعلومات، غيِّر إعدادات الإصدار إلى إصدار وأزِل العلامة من المربّع تصحيح الأخطاء القابل للتنفيذ. إعداد
الإصدار
  3. انقر على علامة التبويب الخيارات وغيِّر التقاط إطار وحدة معالجة الرسومات إلى متوقّف والتحقّق من صحة واجهة برمجة التطبيقات Mel API إلى متوقّف.
    في إعداد خيارات المعادن
  4. تأكد من اختيار "الإصدارات فقط" استنادًا إلى بنية 64 بت. ضمن مستكشف المشروع > tflite_camera_example > PROJECT > your_project_name > إعدادات الإنشاء، اضبط إنشاء بنية نشطة فقط > الإصدار على نعم. إعداد خيارات الإصدار

التوافق مع وحدة معالجة الرسومات المتقدّمة

يتناول هذا القسم الاستخدامات المتقدمة لمفوض وحدة معالجة الرسومات لنظام التشغيل iOS، بما في ذلك خيارات التفويض والمخازن الاحتياطية للإدخال والإخراج واستخدام النماذج الكمية.

خيارات التفويض لنظام التشغيل iOS

تقبل الدالة الإنشائية لتفويض وحدة معالجة الرسومات struct من الخيارات في Swift API وObjective-C API وC API. يؤدي تمرير nullptr (واجهة برمجة تطبيقات C) أو لا شيء (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

وتتطلّب العمليات الحسابية على وحدة معالجة الرسومات إتاحة البيانات لوحدة معالجة الرسومات. يعني هذا الشرط غالبًا أنه يجب عليك عمل نسخة من الذاكرة. يجب أن تتجنب تجاوز حدود ذاكرة وحدة المعالجة المركزية (CPU)/وحدة معالجة الرسومات إن أمكن، لأن هذا قد يستغرق وقتًا طويلاً. عادةً، لا مفر من هذا العبور، ولكن في بعض الحالات الخاصة، يمكن حذف أحدهما أو الآخر.

إذا كان إدخال الشبكة عبارة عن صورة تم تحميلها بالفعل في ذاكرة وحدة معالجة الرسومات (على سبيل المثال، بنية وحدة معالجة الرسومات التي تحتوي على خلاصة الكاميرا)، يمكن أن تبقى هذه الصورة في ذاكرة وحدة معالجة الرسومات بدون الحاجة إلى الدخول إلى ذاكرة وحدة المعالجة المركزية (CPU). وبالمثل، إذا كانت نتائج الشبكة في شكل صورة قابلة للعرض، مثل عملية نقل نمط الصورة، يمكنك عرض النتيجة على الشاشة مباشرةً.

ولتحقيق أفضل أداء، يتيح TensorFlow Lite للمستخدمين القراءة مباشرةً من المخزن المؤقت لأجهزة TensorFlow والكتابة فيه وتجاوز نُسخ الذاكرة التي لا يمكن تجنبها.

على افتراض أنّ إدخال الصورة في ذاكرة وحدة معالجة الرسومات، يجب أولاً تحويله إلى كائن MTLBuffer للمعدن. يمكنك ربط TfLiteTensor بـ MTLBuffer أعدّها المستخدم بالدالة TFLGpuDelegateBindMetalBufferToTensor(). تجدر الإشارة إلى أنّه يجب استدعاء هذه الدالة بعد Interpreter::ModifyGraphWithDelegate(). بالإضافة إلى ذلك، يتم نسخ مخرجات الاستنتاج افتراضيًا من ذاكرة وحدة معالجة الرسومات إلى ذاكرة وحدة المعالجة المركزية (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;
      

بعد إيقاف السلوك التلقائي، يتطلب نسخ مخرجات الاستنتاج من ذاكرة وحدة معالجة الرسومات إلى ذاكرة وحدة المعالجة المركزية استدعاءً صريحًا إلى Interpreter::EnsureTensorDataIsReadable() لكل مفرد مخرج. يعمل هذا النهج أيضًا مع النماذج الكمية، لكن ما زلت بحاجة إلى استخدام مخزن احتياطي بحجم float32 مع بيانات float32، لأن المخزن المؤقت يرتبط بالمورد الاحتياطي الداخلي غير المحدد كميًا.

النماذج الكمّية

إنّ مكتبات المفوَّضين في وحدة معالجة الرسومات لنظام التشغيل iOS تتوافق مع النماذج الكمية تلقائيًا. ولست بحاجة إلى إجراء أي تغييرات على الرموز البرمجية لاستخدام النماذج الكمية مع تفويض وحدة معالجة الرسومات. يوضح القسم التالي كيفية إيقاف الدعم الكمي لأغراض الاختبار أو التجربة.

إيقاف إتاحة النموذج الكمي

يوضّح الرمز التالي كيفية إيقاف إتاحة النماذج الكمية.

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

لمزيد من المعلومات عن تشغيل نماذج الكمية باستخدام تسريع وحدة معالجة الرسومات، يُرجى الاطّلاع على نظرة عامة على تفويض وحدة معالجة الرسومات.