ผู้รับมอบสิทธิ์ TensorFlow Lite NNAPI

Android Neural Networks API (NNAPI) พร้อมใช้งานบนอุปกรณ์ Android ทั้งหมดที่ใช้ Android 8.1 (API ระดับ 27) หรือสูงกว่า ให้การเร่งความเร็วสำหรับรุ่น TensorFlow Lite บนอุปกรณ์ Android ที่มีตัวเร่งฮาร์ดแวร์ที่รองรับ ได้แก่ :

  • หน่วยประมวลผลกราฟิก (GPU)
  • โปรเซสเซอร์สัญญาณดิจิตอล (DSP)
  • หน่วยประมวลผลประสาท (NPU)

ประสิทธิภาพจะแตกต่างกันไปขึ้นอยู่กับฮาร์ดแวร์เฉพาะที่มีอยู่ในอุปกรณ์

หน้านี้อธิบายวิธีใช้ผู้รับมอบสิทธิ์ NNAPI กับล่าม TensorFlow Lite ใน Java และ Kotlin สำหรับ Android C API โปรดดู เอกสารประกอบของ Android Native Developer Kit

กำลังลองใช้ผู้รับมอบสิทธิ์ NNAPI กับโมเดลของคุณเอง

การนำเข้าเกรเดิล

ผู้รับมอบสิทธิ์ NNAPI เป็นส่วนหนึ่งของล่าม Android TensorFlow Lite รุ่น 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)
}

...

ชวา

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 ยังมี เครื่องมือเปรียบเทียบโมเดลสำหรับ 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 ซึ่งส่งผลให้มีการดำเนินการแบบแยกส่วน เนื่องจากการซิงโครไนซ์ CPU/ตัวเร่งความเร็วมีค่าใช้จ่ายสูง ซึ่งอาจส่งผลให้ประสิทธิภาพช้าลงกว่าการดำเนินการทั้งเครือข่ายบน CPU เพียงอย่างเดียว

NNAPI ทำงานได้ดีที่สุดเมื่อรุ่นใช้เฉพาะ ops ที่รองรับ เท่านั้น เป็นที่ทราบกันว่ารุ่นต่อไปนี้เข้ากันได้กับ NNAPI:

ไม่รองรับการเร่งความเร็ว NNAPI เมื่อโมเดลมีเอาต์พุตที่มีขนาดไดนามิก ในกรณีนี้ คุณจะได้รับคำเตือนดังนี้:

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

เปิดใช้งานการใช้งาน CPU NNAPI

กราฟที่ตัวเร่งความเร็วไม่สามารถประมวลผลได้อย่างสมบูรณ์สามารถถอยกลับไปใช้ NNAPI CPU ได้ อย่างไรก็ตาม เนื่องจากโดยทั่วไปแล้วจะมีประสิทธิภาพน้อยกว่าล่าม TensorFlow ตัวเลือกนี้จึงปิดใช้งานตามค่าเริ่มต้นในผู้รับมอบสิทธิ์ NNAPI สำหรับ Android 10 (API ระดับ 29) หรือสูงกว่า หากต้องการแทนที่ลักษณะการทำงานนี้ ให้ตั้งค่า setUseNnapiCpu เป็น true ในวัตถุ NnApiDelegate.Options