נציג TensorFlow Lite NNAPI

ה- Android Neural Networks API (NNAPI) זמין בכל מכשירי אנדרואיד עם אנדרואיד 8.1 (API רמת 27) ומעלה. הוא מספק האצה לדגמי TensorFlow Lite במכשירי אנדרואיד עם מאיצי חומרה נתמכים כולל:

  • יחידת עיבוד גרפי (GPU)
  • מעבד אותות דיגיטלי (DSP)
  • יחידת עיבוד עצבית (NPU)

הביצועים ישתנו בהתאם לחומרה הספציפית הזמינה במכשיר.

דף זה מתאר כיצד להשתמש בנציג NNAPI עם מתורגמן TensorFlow Lite ב-Java ו-Kotlin. עבור ממשקי API של Android C, עיין בתיעוד של Android Native Developer Kit .

מנסה את נציג NNAPI על הדגם שלך

יבוא גרדל

נציג NNAPI הוא חלק מהמתורגמן TensorFlow Lite Android, מהדורה 1.14.0 ומעלה. אתה יכול לייבא אותו לפרויקט שלך על ידי הוספת הדברים הבאים לקובץ הדרגה של המודול שלך:

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

...

שיטות עבודה מומלצות

בדוק את הביצועים לפני הפריסה

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

למפתחים מתקדמים, TensorFlow Lite מציעה גם כלי מידוד לדגם עבור אנדרואיד .

צור רשימת אי הכללת מכשירים

בייצור, ייתכנו מקרים שבהם 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 CPU

גרף שלא ניתן לעיבוד מלא על ידי מאיץ יכול ליפול חזרה למימוש NNAPI CPU. עם זאת, מכיוון שזה בדרך כלל פחות ביצועי מהמתורגמן TensorFlow, אפשרות זו מושבתת כברירת מחדל בנציג NNAPI עבור אנדרואיד 10 (API רמה 29) ומעלה. כדי לעקוף התנהגות זו, הגדר את setUseNnapiCpu ל- true באובייקט NnApiDelegate.Options .