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:
- การจัดหมวดหมู่รูปภาพ MobileNet v1 (224x224) (ดาวน์โหลดโมเดลลอย) (ดาวน์โหลดโมเดลเชิงปริมาณ)
(แบบจำลองการจำแนกภาพที่ออกแบบมาสำหรับแอปพลิเคชันการมองเห็นแบบเคลื่อนที่และแบบฝัง) - การตรวจจับวัตถุ MobileNet v2 SSD (ดาวน์โหลด)
(แบบจำลองการจำแนกภาพที่ตรวจจับวัตถุหลายชิ้นด้วยกล่องขอบ) - การตรวจจับวัตถุ MobileNet v1(300x300) Single Shot Detector (SSD) (ดาวน์โหลด)
- PoseNet สำหรับการประมาณค่าท่าทาง (ดาวน์โหลด)
(แบบจำลองการมองเห็นที่ประมาณท่าทางของบุคคลในภาพหรือวิดีโอ)
ไม่รองรับการเร่งความเร็ว 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