مندوب TensorFlow Lite NNAPI

تتوفر واجهة برمجة تطبيقات Android Neural Networks (NNAPI) على جميع أجهزة Android التي تعمل بنظام التشغيل Android 8.1 (مستوى API 27) أو أعلى. يوفر تسريعًا لنماذج TensorFlow Lite على أجهزة Android مع مسرعات الأجهزة المدعومة بما في ذلك:

  • وحدة معالجة الرسومات (GPU)
  • معالج الإشارة الرقمية (DSP)
  • وحدة المعالجة العصبية (NPU)

سيختلف الأداء اعتمادًا على الأجهزة المحددة المتوفرة على الجهاز.

توضح هذه الصفحة كيفية استخدام مفوض NNAPI مع مترجم TensorFlow Lite في Java وKotlin. بالنسبة لواجهات برمجة تطبيقات Android C، يرجى الرجوع إلى وثائق Android Native Developer Kit .

تجربة مندوب NNAPI على النموذج الخاص بك

استيراد Gradle

يعد مندوب NNAPI جزءًا من مترجم TensorFlow Lite Android، الإصدار 1.14.0 أو أعلى. يمكنك استيراده إلى مشروعك عن طريق إضافة ما يلي إلى ملف gradle للوحدة النمطية الخاصة بك:

dependencies {
   implementation 'org.tensorflow:tensorflow-lite:+'
}

تهيئة مندوب NNAPI

أضف الكود لتهيئة مفوض NNAPI قبل تهيئة مترجم TensorFlow Lite.

كوتلين

import android.content.res.AssetManager
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.nnapi.NnApiDelegate
import java.io.FileInputStream
import java.io.IOException
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
...

val options = Interpreter.Options()
var nnApiDelegate: NnApiDelegate? = null
// Initialize interpreter with NNAPI delegate for Android Pie or above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    nnApiDelegate = NnApiDelegate()
    options.addDelegate(nnApiDelegate)
}
val assetManager = assets

// Initialize TFLite interpreter
val tfLite: Interpreter
try {
    tfLite = Interpreter(loadModelFile(assetManager, "model.tflite"), options)
} catch (e: Exception) {
    throw RuntimeException(e)
}

// Run inference
// ...

// Unload delegate
tfLite.close()
nnApiDelegate?.close()

...

@Throws(IOException::class)
private fun loadModelFile(assetManager: AssetManager, modelFilename: String): MappedByteBuffer {
    val fileDescriptor = assetManager.openFd(modelFilename)
    val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
    val fileChannel = inputStream.channel
    val startOffset = fileDescriptor.startOffset
    val declaredLength = fileDescriptor.declaredLength
    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}

...

java

import android.content.res.AssetManager;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.nnapi.NnApiDelegate;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
...

Interpreter.Options options = (new Interpreter.Options());
NnApiDelegate nnApiDelegate = null;
// Initialize interpreter with NNAPI delegate for Android Pie or above
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    nnApiDelegate = new NnApiDelegate();
    options.addDelegate(nnApiDelegate);
}

AssetManager assetManager = getAssets();
// Initialize TFLite interpreter
try {
    tfLite = new Interpreter(loadModelFile(assetManager, "model.tflite"), options);
} catch (Exception e) {
    throw new RuntimeException(e);
}

// Run inference
// ...

// Unload delegate
tfLite.close();
if(null != nnApiDelegate) {
    nnApiDelegate.close();
}

...

private MappedByteBuffer loadModelFile(AssetManager assetManager, String modelFilename) throws IOException {
    AssetFileDescriptor fileDescriptor = assetManager.openFd(modelFilename);
    FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
    FileChannel fileChannel = inputStream.getChannel();
    long startOffset = fileDescriptor.getStartOffset();
    long declaredLength = fileDescriptor.getDeclaredLength();
    return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}

...

أفضل الممارسات

اختبار الأداء قبل النشر

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

بالنسبة للمطورين المتقدمين، يقدم TensorFlow Lite أيضًا أداة معيارية نموذجية لنظام Android .

إنشاء قائمة استبعاد الأجهزة

في الإنتاج، قد تكون هناك حالات حيث لا يعمل NNAPI كما هو متوقع. نوصي المطورين بالاحتفاظ بقائمة من الأجهزة التي لا ينبغي لها استخدام تسريع NNAPI مع نماذج معينة. يمكنك إنشاء هذه القائمة بناءً على قيمة "ro.board.platform" ، والتي يمكنك استردادها باستخدام مقتطف الكود التالي:

String boardPlatform = "";

try {
    Process sysProcess =
        new ProcessBuilder("/system/bin/getprop", "ro.board.platform").
        redirectErrorStream(true).start();

    BufferedReader reader = new BufferedReader
        (new InputStreamReader(sysProcess.getInputStream()));
    String currentLine = null;

    while ((currentLine=reader.readLine()) != null){
        boardPlatform = line;
    }
    sysProcess.destroy();
} catch (IOException e) {}

Log.d("Board Platform", boardPlatform);

بالنسبة للمطورين المتقدمين، فكر في الاحتفاظ بهذه القائمة عبر نظام التكوين عن بعد. يعمل فريق TensorFlow بنشاط على إيجاد طرق لتبسيط وأتمتة اكتشاف وتطبيق تكوين NNAPI الأمثل.

توضيح

يعمل التكميم على تقليل حجم النموذج باستخدام أعداد صحيحة 8 بت أو عوامات 16 بت بدلاً من أعداد 32 بت للحساب. أحجام النماذج ذات الأعداد الصحيحة 8 بت هي ربع الإصدارات العائمة 32 بت؛ العوامات ذات 16 بت هي نصف الحجم. يمكن أن يؤدي التكميم إلى تحسين الأداء بشكل كبير على الرغم من أن العملية قد تستبدل بعض دقة النموذج.

هناك أنواع متعددة من تقنيات التكميم المتاحة بعد التدريب، ولكن للحصول على أقصى قدر من الدعم والتسريع على الأجهزة الحالية، نوصي بتكميم الأعداد الصحيحة الكاملة . يقوم هذا الأسلوب بتحويل كل من الوزن والعمليات إلى أعداد صحيحة. تتطلب عملية التكميم هذه مجموعة بيانات تمثيلية حتى تعمل.

استخدم النماذج والعمليات المدعومة

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

يعمل NNAPI بشكل أفضل عندما تستخدم النماذج العمليات المدعومة فقط. من المعروف أن النماذج التالية متوافقة مع NNAPI:

لا يتم دعم تسريع NNAPI أيضًا عندما يحتوي النموذج على مخرجات ذات حجم ديناميكي. في هذه الحالة، سوف تحصل على تحذير مثل:

ERROR: Attempting to use a delegate that only supports static-sized tensors \
with a graph that has dynamic-sized tensors.

تمكين تنفيذ وحدة المعالجة المركزية NNAPI

يمكن للرسم البياني الذي لا يمكن معالجته بالكامل بواسطة المسرع الرجوع إلى تطبيق NNAPI CPU. ومع ذلك، نظرًا لأن هذا عادةً ما يكون أقل أداءً من مترجم TensorFlow، فسيتم تعطيل هذا الخيار افتراضيًا في مفوض NNAPI لنظام Android 10 (مستوى API 29) أو أعلى. لتجاوز هذا السلوك، قم بتعيين setUseNnapiCpu إلى true في كائن NnApiDelegate.Options .