Android Neural Networks API (NNAPI) คือ Android C API ที่ออกแบบมาเพื่อเรียกใช้การดำเนินการที่ต้องใช้การประมวลผลอย่างหนักสำหรับแมชชีนเลิร์นนิงในอุปกรณ์ Android NNAPI ออกแบบมาเพื่อมอบฟังก์ชันการทำงานพื้นฐานสำหรับเฟรมเวิร์กแมชชีนเลิร์นนิงระดับสูงขึ้น เช่น TensorFlow Lite และ Caffe2 ที่ใช้สร้างและฝึกโครงข่ายประสาทเทียม API พร้อมใช้งานในอุปกรณ์ Android ทั้งหมดที่ใช้ Android 8.1 (API ระดับ 27) ขึ้นไป
NNAPI รองรับการกำหนดขอบเขตโดยการนำข้อมูลจากอุปกรณ์ Android ไปใช้กับโมเดลที่นักพัฒนาแอปกำหนดและฝึกไว้ก่อนหน้านี้ ตัวอย่างการอนุมาน ได้แก่ การจัดหมวดหมู่รูปภาพ การคาดการณ์พฤติกรรมของผู้ใช้ และการเลือกคำตอบที่เหมาะสมต่อการค้นหา
การอนุมานในอุปกรณ์มีประโยชน์หลายประการ ดังนี้
- เวลาในการตอบสนอง: คุณไม่จําเป็นต้องส่งคําขอผ่านการเชื่อมต่อเครือข่ายและรอการตอบกลับ ตัวอย่างเช่น การดำเนินการนี้อาจสําคัญสําหรับแอปพลิเคชันวิดีโอที่ประมวลผลเฟรมต่อเนื่องซึ่งมาจากกล้อง
- ความพร้อมใช้งาน: แอปพลิเคชันทำงานได้แม้จะอยู่นอกความครอบคลุมของเครือข่าย
- ความเร็ว: ฮาร์ดแวร์ใหม่ที่ใช้เฉพาะสำหรับการประมวลผลเครือข่ายระบบประสาทจะให้การประมวลผลที่เร็วกว่า CPU ที่ใช้ทั่วไปอย่างมาก
- ความเป็นส่วนตัว: ข้อมูลจะไม่ออกจากอุปกรณ์ Android
- ค่าใช้จ่าย: ไม่ต้องใช้ฟาร์มเซิร์ฟเวอร์เมื่อการประมวลผลทั้งหมดดำเนินการในอุปกรณ์ Android
แต่ก็มีข้อดีและข้อเสียต่างๆ ที่นักพัฒนาแอปควรคำนึงถึงดังนี้
- การใช้งานระบบ: การประเมินเครือข่ายประสาทเทียมต้องใช้การประมวลผลจำนวนมาก ซึ่งอาจทำให้แบตเตอรี่หมดเร็วขึ้น คุณควรพิจารณาตรวจสอบประสิทธิภาพแบตเตอรี่หากนี่เป็นข้อกังวลสำหรับแอปของคุณ โดยเฉพาะอย่างยิ่งสำหรับการคำนวณที่ใช้เวลานาน
- ขนาดแอปพลิเคชัน: ให้ความสำคัญกับขนาดของโมเดล โมเดลอาจใช้พื้นที่หลายเมกะไบต์ หากการรวมโมเดลขนาดใหญ่ไว้ใน APK จะส่งผลเสียต่อผู้ใช้อย่างไม่เหมาะสม คุณอาจต้องพิจารณาดาวน์โหลดโมเดลหลังจากติดตั้งแอป ใช้โมเดลขนาดเล็กลง หรือเรียกใช้การคํานวณในระบบคลาวด์ NNAPI ไม่มีฟังก์ชันการทำงานสำหรับการเรียกใช้โมเดลในระบบคลาวด์
ดู ตัวอย่าง API โครงข่ายระบบประสาทเทียมของ Android เพื่อดูตัวอย่างวิธีใช้ NNAPI
ทําความเข้าใจรันไทม์ Neural Networks API
NNAPI มีชื่อโดยไลบรารีแมชชีนเลิร์นนิง เฟรมเวิร์ก และเครื่องมือที่ช่วยให้นักพัฒนาซอฟต์แวร์ฝึกโมเดลนอกอุปกรณ์และทำให้ใช้งานได้ในอุปกรณ์ Android โดยทั่วไปแล้ว แอปจะไม่ใช้ NNAPI โดยตรง แต่จะใช้เฟรมเวิร์กแมชชีนเลิร์นนิงในระดับที่สูงขึ้นแทน เฟรมเวิร์กเหล่านี้อาจใช้ NNAPI ในการดำเนินการอนุมานแบบเร่งฮาร์ดแวร์ในอุปกรณ์ที่รองรับ
รันไทม์ของโครงข่ายระบบประสาทเทียมของ Android สามารถกระจายภาระงานการประมวลผลในอุปกรณ์ประมวลผลที่มีอยู่ได้อย่างมีประสิทธิภาพ ซึ่งรวมถึงฮาร์ดแวร์เครือข่ายระบบประสาท หน่วยประมวลผลกราฟิก (GPU) และตัวประมวลผลสัญญาณดิจิทัล (DSP) โดยเฉพาะ โดยอิงตามข้อกำหนดของแอปและความสามารถของฮาร์ดแวร์ในอุปกรณ์ Android
สำหรับอุปกรณ์ Android ที่ไม่มีไดรเวอร์ของผู้ให้บริการเฉพาะ รันไทม์ NNAPI จะดำเนินการตามคำขอใน CPU
รูปที่ 1 แสดงสถาปัตยกรรมระบบระดับสูงสําหรับ NNAPI
รูปแบบการเขียนโปรแกรม Neural Networks API
หากต้องการทําการคํานวณโดยใช้ NNAPI ก่อนอื่นคุณต้องสร้างกราฟที่มีทิศทางซึ่งกําหนดการคํานวณที่จะทํา กราฟการคำนวณนี้รวมกับข้อมูลที่ป้อน (เช่น น้ำหนักและความลำเอียงที่ส่งต่อมาจากเฟรมเวิร์กแมชชีนเลิร์นนิง) จะเป็นโมเดลสำหรับการประเมินรันไทม์ NNAPI
NNAPI ใช้การแยกแยะหลัก 4 รายการ ได้แก่
- โมเดล: กราฟการคํานวณของการดำเนินการทางคณิตศาสตร์และค่าคงที่ที่เรียนรู้ผ่านกระบวนการฝึก การดำเนินการเหล่านี้มีไว้สำหรับเครือข่ายประสาทโดยเฉพาะ ซึ่งรวมถึงการฟัซชัน 2 มิติ (2D) การกระตุ้นแบบโลจิสติก (sigmoid) การกระตุ้นแบบเชิงเส้นที่แก้ไขแล้ว (ReLU) และอื่นๆ การสร้างโมเดลเป็นการดำเนินการแบบซิงค์
เมื่อสร้างเสร็จแล้ว คุณจะนํารายงานไปใช้ซ้ำในชุดข้อความและการคอมไพล์ได้
ใน NNAPI โมเดลจะแสดงเป็น
ANeuralNetworksModel
อินสแตนซ์ - การคอมไพล์: แสดงการกำหนดค่าสำหรับการคอมไพล์โมเดล NNAPI เป็นโค้ดระดับล่าง การสร้างการคอมไพล์เป็นการดำเนินการแบบซิงค์ เมื่อสร้างสำเร็จแล้ว คุณจะนําไปใช้งานซ้ำในเธรดและการดำเนินการต่างๆ ได้ ใน NNAPI การคอมไพล์แต่ละรายการจะแสดงเป็นอินสแตนซ์
ANeuralNetworksCompilation
- หน่วยความจำ: แสดงหน่วยความจำที่ใช้ร่วมกัน ไฟล์ที่แมปหน่วยความจำ และบัฟเฟอร์หน่วยความจำที่คล้ายกัน การใช้บัฟเฟอร์หน่วยความจำทำให้รันไทม์ของ NNAPI โอนข้อมูลไปยังไดรเวอร์ได้อย่างมีประสิทธิภาพมากขึ้น โดยปกติแล้ว แอปจะสร้างบัฟเฟอร์หน่วยความจำที่แชร์ 1 รายการซึ่งมีเทนเซอร์ทั้งหมดที่จําเป็นในการกําหนดโมเดล คุณยังใช้บัฟเฟอร์หน่วยความจำเพื่อจัดเก็บอินพุตและเอาต์พุตสำหรับอินสแตนซ์การดำเนินการได้ด้วย ใน NNAPI บัฟเฟอร์หน่วยความจําแต่ละรายการจะแสดงเป็น
ANeuralNetworksMemory
อินสแตนซ์ การดำเนินการ: อินเทอร์เฟซสำหรับการใช้โมเดล NNAPI กับชุดอินพุตและรวบรวมผลลัพธ์ การดำเนินการจะดำเนินการแบบซิงค์หรือไม่ซิงค์ก็ได้
สําหรับการดําเนินการแบบไม่พร้อมกัน หลายเธรดจะรอการดําเนินการเดียวกันได้ เมื่อการดําเนินการนี้เสร็จสมบูรณ์ ระบบจะปล่อยเธรดทั้งหมด
ใน NNAPI การเรียกใช้แต่ละครั้งจะแสดงเป็น
ANeuralNetworksExecution
อินสแตนซ์
รูปที่ 2 แสดงขั้นตอนการเขียนโปรแกรมพื้นฐาน
ส่วนที่เหลือของส่วนนี้จะอธิบายขั้นตอนในการตั้งค่าโมเดล NNAPI ให้ทําการคํานวณ คอมไพล์โมเดล และเรียกใช้โมเดลที่คอมไพล์แล้ว
ให้สิทธิ์เข้าถึงข้อมูลการฝึก
ข้อมูลน้ำหนักและค่าอคติที่ผ่านการฝึกอาจจัดเก็บไว้ในไฟล์ หากต้องการให้รันไทม์ NNAPI เข้าถึงข้อมูลนี้ได้อย่างมีประสิทธิภาพ ให้สร้างอินสแตนซ์ ANeuralNetworksMemory
โดยเรียกใช้ฟังก์ชัน ANeuralNetworksMemory_createFromFd()
และส่งตัวระบุไฟล์ของไฟล์ข้อมูลที่เปิดอยู่ นอกจากนี้ คุณยังระบุ Flag การปกป้องหน่วยความจำและออฟเซตที่ภูมิภาคหน่วยความจำที่แชร์เริ่มต้นในไฟล์ได้ด้วย
// Create a memory buffer from the file that contains the trained data
ANeuralNetworksMemory* mem1 = NULL;
int fd = open("training_data", O_RDONLY);
ANeuralNetworksMemory_createFromFd(file_size, PROT_READ, fd, 0, &mem1);
แม้ว่าในตัวอย่างนี้เราจะใช้เพียงอินสแตนซ์เดียว
ANeuralNetworksMemory
สำหรับน้ำหนักทั้งหมดของเรา แต่ก็สามารถใช้อินสแตนซ์
ANeuralNetworksMemory
มากกว่า 1 รายการสำหรับหลายไฟล์ได้
ใช้บัฟเฟอร์ฮาร์ดแวร์เนทีฟ
คุณสามารถใช้บัฟเฟอร์ฮาร์ดแวร์แบบเนทีฟสำหรับอินพุต เอาต์พุต และค่าโอเปอเรนด์แบบคงที่ของโมเดล ในบางกรณี โปรแกรมเร่ง NNAPI สามารถเข้าถึงออบเจ็กต์ AHardwareBuffer
ได้โดยไม่ต้องให้โปรแกรมควบคุมคัดลอกข้อมูล AHardwareBuffer
มีการกำหนดค่าที่แตกต่างกันมากมาย และ NNAPI Accelerator บางรายการอาจรองรับการกำหนดค่าเหล่านี้ทั้งหมด ข้อจำกัดนี้ทำให้คุณต้องดูข้อจำกัดที่ระบุไว้ในเอกสารอ้างอิงANeuralNetworksMemory_createFromAHardwareBuffer
และทดสอบล่วงหน้าในอุปกรณ์เป้าหมายเพื่อให้แน่ใจว่าการคอมไพล์และการดำเนินการที่ใช้ AHardwareBuffer
ทำงานได้ตามที่คาดไว้ โดยใช้การกำหนดอุปกรณ์เพื่อระบุตัวเร่ง
หากต้องการอนุญาตให้รันไทม์ NNAPI เข้าถึงออบเจ็กต์ AHardwareBuffer
ให้สร้างอินสแตนซ์ ANeuralNetworksMemory
โดยเรียกใช้ฟังก์ชัน ANeuralNetworksMemory_createFromAHardwareBuffer
และส่งออบเจ็กต์ AHardwareBuffer
ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้
// Configure and create AHardwareBuffer object AHardwareBuffer_Desc desc = ... AHardwareBuffer* ahwb = nullptr; AHardwareBuffer_allocate(&desc, &ahwb); // Create ANeuralNetworksMemory from AHardwareBuffer ANeuralNetworksMemory* mem2 = NULL; ANeuralNetworksMemory_createFromAHardwareBuffer(ahwb, &mem2);
เมื่อ NNAPI ไม่ต้องเข้าถึงออบเจ็กต์ AHardwareBuffer
อีกต่อไป ให้ยกเลิกการจองอินสแตนซ์ ANeuralNetworksMemory
ที่เกี่ยวข้อง ดังนี้
ANeuralNetworksMemory_free(mem2);
หมายเหตุ:
- คุณใช้
AHardwareBuffer
ได้เฉพาะกับบัฟเฟอร์ทั้งหมดเท่านั้น และใช้กับพารามิเตอร์ARect
ไม่ได้ - รันไทม์ของ NNAPI จะไม่ล้างบัฟเฟอร์ คุณต้องตรวจสอบว่าเข้าถึงบัฟเฟอร์อินพุตและเอาต์พุตได้ก่อนที่จะกำหนดเวลาการเรียกใช้
- ไม่รองรับตัวบ่งชี้ไฟล์รั้วการซิงค์
- สำหรับ
AHardwareBuffer
ที่มีรูปแบบและบิตการใช้งานเฉพาะผู้ให้บริการ ขึ้นอยู่กับการใช้งานของผู้ให้บริการ เพื่อกำหนดว่าจะให้ไคลเอ็นต์หรือไดรเวอร์รับผิดชอบการล้างแคชหรือไม่
รุ่น
โมเดลคือหน่วยพื้นฐานของการประมวลผลใน NNAPI แต่ละโมเดลจะกำหนดโดยตัวถูกดำเนินการและการดำเนินการอย่างน้อย 1 รายการ
ออบเจ็กต์
ออบเจ็กต์ข้อมูลที่ใช้กำหนดกราฟ ซึ่งรวมถึงอินพุตและเอาต์พุตของโมเดล โหนดกลางที่มีข้อมูลที่ไหลจากการดำเนินการหนึ่งไปยังอีกการดำเนินการหนึ่ง และค่าคงที่ที่ส่งไปยังการดำเนินการเหล่านี้
ออบเจ็กต์ที่เพิ่มลงในโมเดล NNAPI ได้มีอยู่ 2 ประเภท ได้แก่ สเกลาร์และเทนเซอร์
สเกลาร์แสดงค่าเดี่ยว NNAPI รองรับค่าสเกลาร์ในรูปแบบบูลีน ทศนิยม 16 บิต ทศนิยม 32 บิต จำนวนเต็ม 32 บิต และจำนวนเต็ม 32 บิตแบบไม่ลงนาม
การดำเนินการส่วนใหญ่ใน NNAPI เกี่ยวข้องกับเทนเซอร์ เทนเซอร์คืออาร์เรย์ n มิติ NNAPI รองรับเทนเซอร์ที่มีค่าทศนิยม 16 บิต ทศนิยม 32 บิต ค่าที่ปรับเป็นจำนวนเต็ม 8 บิต ค่าที่ปรับเป็นจำนวนเต็ม 16 บิต จำนวนเต็ม 32 บิต และค่าบูลีน 8 บิต
ตัวอย่างเช่น รูปที่ 3 แสดงโมเดลที่มีการดำเนินการ 2 อย่าง ได้แก่ การเพิ่มตามด้วยคูณ โมเดลจะรับ Tensor อินพุตและสร้าง Tensor เอาต์พุต 1 รายการ
โมเดลด้านบนมีโอเปอเรนด์ 7 รายการ ระบบจะระบุตัวดำเนินการเหล่านี้โดยนัยจากดัชนีของลําดับที่เพิ่มลงในโมเดล อOperand แรกที่มีการเพิ่มจะมีดัชนี 0 อOperand ที่ 2 จะมีดัชนี 1 และอื่นๆ ตามลำดับ ตัวถูกดำเนินการ 1, 2, 3 และ 5 คือตัวถูกดำเนินการคงที่
ลำดับที่คุณเพิ่มตัวดำเนินการไม่มีผล เช่น ตัวดำเนินการเอาต์พุตของโมเดลอาจเป็นรายการแรกที่เพิ่ม ส่วนสําคัญคือการใช้ค่าดัชนีที่ถูกต้องเมื่ออ้างอิงไปยังออบเปอเรนด
ออบเจ็กต์การดำเนินการมีประเภท ซึ่งจะระบุเมื่อเพิ่มลงในโมเดล
ออบเจ็กต์จะไม่สามารถใช้เป็นทั้งอินพุตและเอาต์พุตของโมเดล
ออบเจ็กต์การดำเนินการทั้งหมดต้องเป็นอินพุตของโมเดล ค่าคงที่ หรือออบเจ็กต์การดำเนินการเอาต์พุตของการดำเนินการเพียง 1 รายการ
ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้โอเปอเรนด์ได้ที่ข้อมูลเพิ่มเติมเกี่ยวกับโอเปอเรนด์
การทำงาน
การดำเนินการจะระบุการคำนวณที่จะดำเนินการ การดำเนินการแต่ละรายการประกอบด้วยองค์ประกอบต่อไปนี้
- ประเภทการดำเนินการ (เช่น การบวก การคูณ การฟัซซิออน)
- รายการดัชนีของตัวถูกดำเนินการที่การดำเนินการใช้สำหรับอินพุต และ
- รายการดัชนีของออบเจ็กต์การดำเนินการที่การดำเนินการใช้สำหรับเอาต์พุต
ลำดับในรายการเหล่านี้มีความสำคัญ โปรดดูข้อมูลอ้างอิง NNAPI API สำหรับอินพุตและเอาต์พุตที่คาดไว้ของการดำเนินการแต่ละประเภท
คุณต้องเพิ่มตัวถูกดำเนินการที่การดำเนินการใช้หรือสร้างในโมเดลก่อนเพิ่มการดำเนินการ
ลำดับที่คุณเพิ่มการดำเนินการไม่มีผล NNAPI อาศัยความสัมพันธ์ที่สร้างขึ้นโดยกราฟการคํานวณของออพเพอร์แลนด์และการดำเนินการเพื่อกําหนดลําดับการดําเนินการ
การดำเนินการที่ NNAPI รองรับจะสรุปไว้ในตารางด้านล่าง
ปัญหาที่ทราบใน API ระดับ 28: เมื่อส่ง tensor ของ ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
ไปยังการดำเนินการ ANEURALNETWORKS_PAD
ซึ่งพร้อมใช้งานใน Android 9 (API ระดับ 28) ขึ้นไป เอาต์พุตจาก NNAPI อาจไม่ตรงกับเอาต์พุตจากเฟรมเวิร์กแมชชีนเลิร์นนิงระดับสูง เช่น TensorFlow Lite คุณควรส่งผ่านเฉพาะ ANEURALNETWORKS_TENSOR_FLOAT32
แทน
ปัญหานี้ได้รับการแก้ไขแล้วใน Android 10 (API ระดับ 29) ขึ้นไป
สร้างโมเดล
ในตัวอย่างนี้ เราจะสร้างโมเดลการดำเนินการ 2 รายการที่แสดงในรูปที่ 3
หากต้องการสร้างโมเดล ให้ทำตามขั้นตอนต่อไปนี้
เรียกใช้ฟังก์ชัน
ANeuralNetworksModel_create()
เพื่อกําหนดโมเดลว่างANeuralNetworksModel* model = NULL; ANeuralNetworksModel_create(&model);
เพิ่มตัวดำเนินการลงในโมเดลโดยเรียกใช้
ANeuralNetworks_addOperand()
ประเภทข้อมูลจะกำหนดโดยใช้รูปแบบข้อมูลANeuralNetworksOperandType
// In our example, all our tensors are matrices of dimension [3][4] ANeuralNetworksOperandType tensor3x4Type; tensor3x4Type.type = ANEURALNETWORKS_TENSOR_FLOAT32; tensor3x4Type.scale = 0.f; // These fields are used for quantized tensors tensor3x4Type.zeroPoint = 0; // These fields are used for quantized tensors tensor3x4Type.dimensionCount = 2; uint32_t dims[2] = {3, 4}; tensor3x4Type.dimensions = dims;
// We also specify operands that are activation function specifiers ANeuralNetworksOperandType activationType; activationType.type = ANEURALNETWORKS_INT32; activationType.scale = 0.f; activationType.zeroPoint = 0; activationType.dimensionCount = 0; activationType.dimensions = NULL;
// Now we add the seven operands, in the same order defined in the diagram ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 0 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 1 ANeuralNetworksModel_addOperand(model, &activationType); // operand 2 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 3 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 4 ANeuralNetworksModel_addOperand(model, &activationType); // operand 5 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 6สำหรับตัวถูกดำเนินการที่มีค่าคงที่ เช่น น้ำหนักและอคติที่แอปได้รับจากกระบวนการฝึก ให้ใช้ฟังก์ชัน
ANeuralNetworksModel_setOperandValue()
และANeuralNetworksModel_setOperandValueFromMemory()
ในตัวอย่างต่อไปนี้ เรากำหนดค่าคงที่จากไฟล์ข้อมูลการฝึกที่สอดคล้องกับบัฟเฟอร์หน่วยความจำที่เราสร้างไว้ในให้สิทธิ์การเข้าถึงข้อมูลการฝึก
// In our example, operands 1 and 3 are constant tensors whose values were // established during the training process const int sizeOfTensor = 3 * 4 * 4; // The formula for size calculation is dim0 * dim1 * elementSize ANeuralNetworksModel_setOperandValueFromMemory(model, 1, mem1, 0, sizeOfTensor); ANeuralNetworksModel_setOperandValueFromMemory(model, 3, mem1, sizeOfTensor, sizeOfTensor);
// We set the values of the activation operands, in our example operands 2 and 5 int32_t noneValue = ANEURALNETWORKS_FUSED_NONE; ANeuralNetworksModel_setOperandValue(model, 2, &noneValue, sizeof(noneValue)); ANeuralNetworksModel_setOperandValue(model, 5, &noneValue, sizeof(noneValue));สําหรับการดำเนินการแต่ละรายการในกราฟที่มีทิศทางที่คุณต้องการคํานวณ ให้เพิ่มการดำเนินการนั้นลงในโมเดลโดยเรียกใช้ฟังก์ชัน
ANeuralNetworksModel_addOperation()
แอปของคุณต้องระบุข้อมูลต่อไปนี้เป็นพารามิเตอร์ในการเรียกใช้นี้
- ประเภทการดำเนินการ
- จํานวนค่าอินพุต
- อาร์เรย์ของดัชนีสำหรับตัวถูกดำเนินการอินพุต
- จำนวนค่าเอาต์พุต
- อาร์เรย์ของดัชนีสำหรับออบเจ็กต์เอาต์พุต
โปรดทราบว่าไม่สามารถใช้ตัวถูกดำเนินการของทั้งอินพุตและเอาต์พุตของการดำเนินการเดียวกัน
// We have two operations in our example // The first consumes operands 1, 0, 2, and produces operand 4 uint32_t addInputIndexes[3] = {1, 0, 2}; uint32_t addOutputIndexes[1] = {4}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, addInputIndexes, 1, addOutputIndexes);
// The second consumes operands 3, 4, 5, and produces operand 6 uint32_t multInputIndexes[3] = {3, 4, 5}; uint32_t multOutputIndexes[1] = {6}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_MUL, 3, multInputIndexes, 1, multOutputIndexes);ระบุตัวดำเนินการที่โมเดลควรถือว่าเป็นอินพุตและเอาต์พุตโดยเรียกใช้ฟังก์ชัน
ANeuralNetworksModel_identifyInputsAndOutputs()
// Our model has one input (0) and one output (6) uint32_t modelInputIndexes[1] = {0}; uint32_t modelOutputIndexes[1] = {6}; ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, modelInputIndexes, 1 modelOutputIndexes);
คุณสามารถเลือกที่จะระบุว่า
ANEURALNETWORKS_TENSOR_FLOAT32
ได้รับอนุญาตให้คำนวณด้วยช่วงหรือความแม่นยำต่ำเท่ากับรูปแบบจุดลอยตัว 16 บิต IEEE 754 ด้วยการเรียกANeuralNetworksModel_relaxComputationFloat32toFloat16()
หรือไม่โทรหา
ANeuralNetworksModel_finish()
เพื่อกําหนดค่าโมเดลให้เสร็จสมบูรณ์ หากไม่มีข้อผิดพลาด ฟังก์ชันนี้จะแสดงผลรหัสANEURALNETWORKS_NO_ERROR
ANeuralNetworksModel_finish(model);
เมื่อสร้างโมเดลแล้ว คุณจะคอมไพล์โมเดลได้เท่าใดก็ได้และเรียกใช้การคอมไพล์แต่ละครั้งกี่ครั้งก็ได้
ควบคุมโฟลว์
หากต้องการรวมการไหลเวียนของการควบคุมในโมเดล NNAPI ให้ทําดังนี้
สร้างกราฟย่อยการดําเนินการที่เกี่ยวข้อง (กราฟย่อย
then
และelse
สำหรับคำสั่งIF
, กราฟย่อยcondition
และbody
สำหรับลูปWHILE
) เป็นโมเดลANeuralNetworksModel*
แบบสแตนด์อโลน ดังนี้ANeuralNetworksModel* thenModel = makeThenModel(); ANeuralNetworksModel* elseModel = makeElseModel();
สร้างโอเปอเรนด์ที่อ้างอิงโมเดลเหล่านั้นภายในโมเดลที่มีโฟลว์การควบคุม
ANeuralNetworksOperandType modelType = { .type = ANEURALNETWORKS_MODEL, }; ANeuralNetworksModel_addOperand(model, &modelType); // kThenOperandIndex ANeuralNetworksModel_addOperand(model, &modelType); // kElseOperandIndex ANeuralNetworksModel_setOperandValueFromModel(model, kThenOperandIndex, &thenModel); ANeuralNetworksModel_setOperandValueFromModel(model, kElseOperandIndex, &elseModel);
เพิ่มการดำเนินการขั้นตอนการควบคุมดังนี้
uint32_t inputs[] = {kConditionOperandIndex, kThenOperandIndex, kElseOperandIndex, kInput1, kInput2, kInput3}; uint32_t outputs[] = {kOutput1, kOutput2}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_IF, std::size(inputs), inputs, std::size(output), outputs);
การรวบรวม
ขั้นตอนการคอมไพล์จะระบุโปรเซสเซอร์ที่จะเรียกใช้โมเดลของคุณและขอให้ไดรเวอร์ที่เกี่ยวข้องเตรียมพร้อมสำหรับการดำเนินการ ซึ่งอาจรวมถึงการสร้างโค้ดเครื่องสำหรับโปรเซสเซอร์ที่โมเดลจะทำงานด้วย
หากต้องการคอมไพล์โมเดล ให้ทำตามขั้นตอนต่อไปนี้
เรียกใช้ฟังก์ชัน
ANeuralNetworksCompilation_create()
เพื่อสร้างอินสแตนซ์การคอมไพล์ใหม่// Compile the model ANeuralNetworksCompilation* compilation; ANeuralNetworksCompilation_create(model, &compilation);
คุณใช้การกำหนดอุปกรณ์เพื่อเลือกอุปกรณ์ที่จะเรียกใช้อย่างชัดเจนได้ (ไม่บังคับ)
คุณเลือกที่จะกำหนดวิธีใช้พลังงานแบตเตอรี่และความเร็วในการดำเนินการของรันไทม์ได้ โดยโทรไปที่
ANeuralNetworksCompilation_setPreference()
// Ask to optimize for low power consumption ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);
ค่ากําหนดที่คุณระบุได้มีดังนี้
ANEURALNETWORKS_PREFER_LOW_POWER
: เหมาะสำหรับการใช้งานในลักษณะที่ไม่ทำให้แบตเตอรี่หมดเร็ว ซึ่งเหมาะสําหรับการคอมไพล์ที่ทํางานบ่อยANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER
: ต้องการแสดงคำตอบเดียวโดยเร็วที่สุด แม้ว่าจะส่งผลให้ใช้พลังงานมากขึ้นก็ตาม ซึ่งเป็นค่าเริ่มต้นANEURALNETWORKS_PREFER_SUSTAINED_SPEED
: เพิ่มประสิทธิภาพอัตราการส่งข้อมูลของเฟรมต่อเนื่องกัน เช่น เมื่อประมวลผลเฟรมต่อเนื่องที่มาจากกล้อง
คุณเลือกตั้งค่าการแคชการคอมไพล์ได้โดยเรียกใช้
ANeuralNetworksCompilation_setCaching
// Set up compilation caching ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);
ใช้
getCodeCacheDir()
สําหรับcacheDir
token
ที่ระบุต้องไม่ซ้ำกันสำหรับแต่ละโมเดลภายในแอปพลิเคชันทำให้คำจำกัดความการรวบรวมเสร็จสิ้นโดยการเรียกใช้
ANeuralNetworksCompilation_finish()
หากไม่มีข้อผิดพลาด ฟังก์ชันนี้จะแสดงผลรหัสผลลัพธ์เป็นANEURALNETWORKS_NO_ERROR
ANeuralNetworksCompilation_finish(compilation);
การค้นหาและการกำหนดอุปกรณ์
ในอุปกรณ์ Android ที่ใช้ Android 10 (API ระดับ 29) ขึ้นไป NNAPI มีฟังก์ชันที่ช่วยให้ผู้ใช้งานสามารถเรียกใช้ไลบรารีและแอปเฟรมเวิร์กแมชชีนเลิร์นนิงเพื่อรับข้อมูลเกี่ยวกับอุปกรณ์ที่ใช้ได้และระบุอุปกรณ์ที่จะใช้สำหรับการดำเนินการ การให้ข้อมูลเกี่ยวกับอุปกรณ์ที่ใช้ได้ช่วยให้แอปได้รับเวอร์ชันที่แน่นอนของไดรเวอร์ที่พบในอุปกรณ์เพื่อหลีกเลี่ยงความเข้ากันไม่ได้ที่ทราบ การที่แอปสามารถระบุอุปกรณ์ที่จะเรียกใช้ส่วนต่างๆ ของโมเดลจะช่วยให้แอปสามารถเพิ่มประสิทธิภาพสำหรับอุปกรณ์ Android ที่มีการใช้งาน
การค้นหาอุปกรณ์
ใช้ ANeuralNetworks_getDeviceCount
เพื่อดูจำนวนอุปกรณ์ที่ใช้ได้ สําหรับอุปกรณ์แต่ละเครื่อง ให้ใช้
ANeuralNetworks_getDevice
เพื่อตั้งค่าอินสแตนซ์ ANeuralNetworksDevice
เป็นข้อมูลอ้างอิงถึงอุปกรณ์นั้น
เมื่อคุณมีข้อมูลอ้างอิงอุปกรณ์แล้ว คุณสามารถดูข้อมูลเพิ่มเติมเกี่ยวกับอุปกรณ์นั้นโดยใช้ฟังก์ชันต่อไปนี้
ANeuralNetworksDevice_getFeatureLevel
ANeuralNetworksDevice_getName
ANeuralNetworksDevice_getType
ANeuralNetworksDevice_getVersion
การกำหนดอุปกรณ์
ใช้
ANeuralNetworksModel_getSupportedOperationsForDevices
เพื่อดูการดำเนินการของโมเดลที่ทำงานในอุปกรณ์หนึ่งๆ ได้
หากต้องการควบคุมตัวเร่งที่ใช้สำหรับการดำเนินการ ให้เรียกใช้ ANeuralNetworksCompilation_createForDevices
แทน ANeuralNetworksCompilation_create
ใช้ออบเจ็กต์ ANeuralNetworksCompilation
ที่ได้มาตามปกติ
ฟังก์ชันจะแสดงข้อผิดพลาดหากโมเดลที่ระบุมีการดำเนินการที่อุปกรณ์ที่เลือกไม่รองรับ
หากระบุอุปกรณ์หลายเครื่อง รันไทม์จะมีหน้าที่กระจายงานไปยังอุปกรณ์ต่างๆ
การใช้งาน CPU NNAPI จะแสดงด้วย ANeuralNetworksDevice
ที่มีชื่อ nnapi-reference
และประเภท ANEURALNETWORKS_DEVICE_TYPE_CPU
เช่นเดียวกับอุปกรณ์อื่นๆ เมื่อเรียกใช้ ANeuralNetworksCompilation_createForDevices
ระบบจะไม่ใช้การติดตั้งใช้งาน CPU เพื่อจัดการกรณีที่คอมไพล์และเรียกใช้โมเดลไม่สำเร็จ
แอปพลิเคชันมีหน้าที่แบ่งโมเดลออกเป็นโมเดลย่อยที่ทำงานได้ในอุปกรณ์ที่ระบุ แอปพลิเคชันที่ไม่จำเป็นต้องแบ่งพาร์ติชันด้วยตนเองควรเรียกใช้ ANeuralNetworksCompilation_create
ที่ง่ายขึ้นต่อไปเพื่อใช้อุปกรณ์ที่มีอยู่ทั้งหมด (รวมถึง CPU) เพื่อเร่งความเร็วโมเดลนี้ หากอุปกรณ์ที่คุณระบุโดยใช้ ANeuralNetworksCompilation_createForDevices
ไม่รองรับรุ่นนั้นอย่างเต็มรูปแบบ ระบบจะแสดงผล ANEURALNETWORKS_BAD_DATA
การแบ่งพาร์ติชันโมเดล
เมื่อมีอุปกรณ์หลายเครื่องสำหรับโมเดล รันไทม์ของ NNAPI จะกระจายงานไปยังอุปกรณ์ต่างๆ เช่น หากมีการมอบอุปกรณ์มากกว่า 1 เครื่องให้กับ ANeuralNetworksCompilation_createForDevices
ระบบจะพิจารณาอุปกรณ์ที่ระบุไว้ทั้งหมดเมื่อจัดสรรงาน โปรดทราบว่าหากอุปกรณ์ CPU ไม่อยู่ในรายการ ระบบจะปิดใช้การดำเนินการของ CPU เมื่อใช้ ANeuralNetworksCompilation_create
จะมีการพิจารณาอุปกรณ์ที่ใช้ได้ทั้งหมด รวมถึง CPU ด้วย
การแจกจ่ายจะดำเนินการโดยเลือกจากรายการอุปกรณ์ที่ใช้ได้สำหรับการดำเนินการแต่ละรายการในโมเดล อุปกรณ์ที่รองรับการดำเนินการ และประกาศประสิทธิภาพที่ดีที่สุด เช่น เวลาที่ใช้ในการดำเนินการเร็วที่สุดหรือการใช้พลังงานต่ำที่สุด ทั้งนี้ขึ้นอยู่กับค่ากำหนดการดำเนินการที่ลูกค้าระบุ อัลกอริทึมการแบ่งพาร์ติชันนี้ไม่ได้คำนึงถึงการทำงานที่ไม่มีประสิทธิภาพที่อาจเกิดขึ้นจาก IO ระหว่างโปรเซสเซอร์ต่างๆ ดังนั้นเมื่อระบุโปรเซสเซอร์หลายรายการ (ไม่ว่าจะระบุอย่างชัดแจ้งเมื่อใช้ ANeuralNetworksCompilation_createForDevices
หรือระบุโดยนัยเมื่อใช้ ANeuralNetworksCompilation_create
) คุณควรตรวจสอบประสิทธิภาพของแอปพลิเคชันที่ได้
หากต้องการทำความเข้าใจว่า NNAPI แบ่งพาร์ติชันโมเดลของคุณอย่างไร ให้ตรวจสอบข้อความในบันทึก Android (ที่ระดับ INFO พร้อมแท็ก ExecutionPlan
)
ModelBuilder::findBestDeviceForEachOperation(op-name): device-index
op-name
คือชื่อที่สื่อความหมายของการดำเนินการในกราฟ และ
device-index
คือดัชนีของอุปกรณ์ที่เป็นไปได้ในรายการอุปกรณ์
รายการนี้เป็นอินพุตที่ป้อนให้กับ ANeuralNetworksCompilation_createForDevices
หรือหากใช้ ANeuralNetworksCompilation_createForDevices
จะเป็นรายการอุปกรณ์ที่แสดงผลเมื่อทำซ้ำในอุปกรณ์ทั้งหมดที่ใช้ ANeuralNetworks_getDeviceCount
และ ANeuralNetworks_getDevice
ข้อความ (ที่ระดับ INFO พร้อมแท็ก ExecutionPlan
)
ModelBuilder::partitionTheWork: only one best device: device-name
ข้อความนี้บ่งบอกว่ามีการเร่งกราฟทั้งหมดในอุปกรณ์
device-name
การลงมือปฏิบัติ
ขั้นตอนการดำเนินการจะใช้โมเดลกับชุดอินพุตและจัดเก็บเอาต์พุตการคำนวณไปยังบัฟเฟอร์ผู้ใช้หรือพื้นที่หน่วยความจำอย่างน้อย 1 รายการที่แอปของคุณจัดสรรไว้
หากต้องการเรียกใช้โมเดลที่คอมไพล์แล้ว ให้ทําตามขั้นตอนต่อไปนี้
เรียกใช้ฟังก์ชัน
ANeuralNetworksExecution_create()
เพื่อสร้างอินสแตนซ์การดำเนินการใหม่// Run the compiled model against a set of inputs ANeuralNetworksExecution* run1 = NULL; ANeuralNetworksExecution_create(compilation, &run1);
ระบุตําแหน่งที่แอปอ่านค่าอินพุตสําหรับการคํานวณ แอปของคุณอ่านค่าอินพุตได้จากบัฟเฟอร์ผู้ใช้หรือพื้นที่หน่วยความจำที่จัดสรรโดยเรียกใช้
ANeuralNetworksExecution_setInput()
หรือANeuralNetworksExecution_setInputFromMemory()
ตามลำดับ// Set the single input to our sample model. Since it is small, we won't use a memory buffer float32 myInput[3][4] = { ...the data... }; ANeuralNetworksExecution_setInput(run1, 0, NULL, myInput, sizeof(myInput));
ระบุตำแหน่งที่แอปของคุณเขียนค่าเอาต์พุต แอปสามารถเขียนค่าเอาต์พุตไปยังบัฟเฟอร์ผู้ใช้หรือพื้นที่หน่วยความจำที่จองไว้ได้โดยการเรียกใช้
ANeuralNetworksExecution_setOutput()
หรือANeuralNetworksExecution_setOutputFromMemory()
ตามลำดับ// Set the output float32 myOutput[3][4]; ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
กําหนดเวลาการเริ่มการดําเนินการโดยเรียกใช้ฟังก์ชัน
ANeuralNetworksExecution_startCompute()
หากไม่มีข้อผิดพลาด ฟังก์ชันนี้จะแสดงผลรหัสผลลัพธ์เป็นANEURALNETWORKS_NO_ERROR
// Starts the work. The work proceeds asynchronously ANeuralNetworksEvent* run1_end = NULL; ANeuralNetworksExecution_startCompute(run1, &run1_end);
เรียกใช้ฟังก์ชัน
ANeuralNetworksEvent_wait()
เพื่อรอให้การดำเนินการเสร็จสมบูรณ์ หากการดำเนินการสำเร็จ ฟังก์ชันนี้จะแสดงรหัสผลลัพธ์ของANEURALNETWORKS_NO_ERROR
การรอสามารถดำเนินการในเธรดอื่นที่ไม่ใช่เธรดเริ่มต้นการดำเนินการ// For our example, we have no other work to do and will just wait for the completion ANeuralNetworksEvent_wait(run1_end); ANeuralNetworksEvent_free(run1_end); ANeuralNetworksExecution_free(run1);
คุณสามารถใช้ชุดอินพุตอื่นกับโมเดลที่คอมไพล์แล้วได้หากต้องการ โดยการใช้อินสแตนซ์การคอมไพล์เดียวกันเพื่อสร้าง
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
ANeuralNetworksExecution
// Apply the compiled model to a different set of inputs ANeuralNetworksExecution* run2; ANeuralNetworksExecution_create(compilation, &run2); ANeuralNetworksExecution_setInput(run2, ...); ANeuralNetworksExecution_setOutput(run2, ...); ANeuralNetworksEvent* run2_end = NULL; ANeuralNetworksExecution_startCompute(run2, &run2_end); ANeuralNetworksEvent_wait(run2_end); ANeuralNetworksEvent_free(run2_end); ANeuralNetworksExecution_free(run2);
การดำเนินการแบบซิงโครนัส
การดําเนินการแบบไม่พร้อมกันจะใช้เวลาในการสร้างและซิงค์เธรด นอกจากนี้ เวลาในการตอบสนองยังแตกต่างกันไปอย่างมาก โดยเวลาในการตอบสนองที่นานที่สุดอาจสูงถึง 500 ไมโครวินาทีระหว่างเวลาที่แจ้งเตือนหรือตื่นเธรดกับเวลาที่ผูกเธรดกับแกน CPU
หากต้องการปรับปรุงเวลาในการตอบสนอง คุณสามารถกําหนดให้แอปพลิเคชันเรียกใช้การอนุมานแบบซิงค์กับรันไทม์แทน การเรียกใช้นี้จะแสดงผลเมื่อการอนุมานเสร็จสมบูรณ์เท่านั้น แทนที่จะแสดงผลเมื่อการอนุมานเริ่มต้นขึ้น แอปพลิเคชันจะเรียกใช้ ANeuralNetworksExecution_compute
เพื่อเรียกใช้รันไทม์แบบซิงค์แทนการเรียกใช้ ANeuralNetworksExecution_startCompute
เพื่อเรียกใช้รันไทม์แบบไม่พร้อมกัน การโทรไปยัง ANeuralNetworksExecution_compute
ไม่ได้รับ ANeuralNetworksEvent
และไม่ได้จับคู่กับการโทรไปยัง ANeuralNetworksEvent_wait
การดำเนินการแบบ Burst
ในอุปกรณ์ Android ที่ใช้ Android 10 (API ระดับ 29) ขึ้นไป NNAPI รองรับการดำเนินการถ่ายภาพอัจฉริยะผ่านออบเจ็กต์ ANeuralNetworksBurst
การดำเนินการแบบต่อเนื่องคือลําดับการดำเนินการของคอมไพล์เดียวกันที่เกิดขึ้นอย่างต่อเนื่อง เช่น การดำเนินการกับเฟรมของภาพจากกล้องหรือตัวอย่างเสียงที่ต่อเนื่องกัน การใช้ออบเจ็กต์ ANeuralNetworksBurst
อาจทําให้การดำเนินการเร็วขึ้น เนื่องจากออบเจ็กต์จะบอกให้ตัวเร่งใช้ทรัพยากรซ้ำระหว่างการดำเนินการ และตัวเร่งควรอยู่ในสถานะประสิทธิภาพสูงตลอดระยะเวลาของการทำงาน
ANeuralNetworksBurst
ทำให้เกิดการเปลี่ยนแปลงเพียงเล็กน้อยในเส้นทางการดําเนินการปกติ คุณสร้างออบเจ็กต์ภาพต่อเนื่องโดยใช้ ANeuralNetworksBurst_create
ตามที่แสดงในข้อมูลโค้ดต่อไปนี้
// Create burst object to be reused across a sequence of executions ANeuralNetworksBurst* burst = NULL; ANeuralNetworksBurst_create(compilation, &burst);
การดำเนินการแบบต่อเนื่องจะเป็นแบบพร้อมกัน แต่แทนที่จะใช้ ANeuralNetworksExecution_compute
ในการอนุมานแต่ละครั้ง คุณจับคู่ออบเจ็กต์ ANeuralNetworksExecution
ต่างๆ กับ ANeuralNetworksBurst
เดียวกันในการเรียกใช้ฟังก์ชัน ANeuralNetworksExecution_burstCompute
// Create and configure first execution object // ... // Execute using the burst object ANeuralNetworksExecution_burstCompute(execution1, burst); // Use results of first execution and free the execution object // ... // Create and configure second execution object // ... // Execute using the same burst object ANeuralNetworksExecution_burstCompute(execution2, burst); // Use results of second execution and free the execution object // ...
ทำให้ออบเจ็กต์ ANeuralNetworksBurst
เป็นอิสระด้วย ANeuralNetworksBurst_free
เมื่อไม่จำเป็นต้องใช้แล้ว
// Cleanup ANeuralNetworksBurst_free(burst);
คิวคําสั่งแบบไม่พร้อมกันและการดำเนินการแบบหลีกทาง
ใน Android 11 ขึ้นไป NNAPI รองรับวิธีอื่นในการกำหนดเวลาการดำเนินการแบบไม่พร้อมกันผ่านเมธอด ANeuralNetworksExecution_startComputeWithDependencies()
เมื่อใช้วิธีนี้ การดำเนินการจะรอให้มีการส่งสัญญาณเหตุการณ์ที่เกี่ยวข้องทั้งหมดก่อนที่จะเริ่มการประเมิน เมื่อการดําเนินการเสร็จสมบูรณ์และเอาต์พุตพร้อมใช้งาน ระบบจะส่งสัญญาณเหตุการณ์ที่แสดงผล
เหตุการณ์อาจได้รับการสนับสนุนจากSync Fence ทั้งนี้ขึ้นอยู่กับอุปกรณ์ที่จัดการการดำเนินการ คุณต้องเรียกใช้ ANeuralNetworksEvent_wait()
เพื่อรอเหตุการณ์และกู้คืนทรัพยากรที่ใช้ในการดำเนินการ คุณสามารถนําเข้ารั้วการซิงค์ไปยังออบเจ็กต์เหตุการณ์ได้โดยใช้ ANeuralNetworksEvent_createFromSyncFenceFd()
และส่งออกรั้วการซิงค์จากออบเจ็กต์เหตุการณ์ได้โดยใช้ ANeuralNetworksEvent_getSyncFenceFd()
เอาต์พุตที่มีขนาดแบบไดนามิก
หากต้องการรองรับโมเดลที่ขนาดของเอาต์พุตขึ้นอยู่กับข้อมูลอินพุต กล่าวคือ เมื่อกำหนดขนาดไม่ได้ในขณะที่ดำเนินการโมเดล ให้ใช้ ANeuralNetworksExecution_getOutputOperandRank
และ ANeuralNetworksExecution_getOutputOperandDimensions
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีดำเนินการ
// Get the rank of the output uint32_t myOutputRank = 0; ANeuralNetworksExecution_getOutputOperandRank(run1, 0, &myOutputRank); // Get the dimensions of the output std::vector<uint32_t> myOutputDimensions(myOutputRank); ANeuralNetworksExecution_getOutputOperandDimensions(run1, 0, myOutputDimensions.data());
ทำความสะอาดข้อมูล
ขั้นตอนล้างข้อมูลจะจัดการการปลดปล่อยทรัพยากรภายในที่ใช้สําหรับการคํานวณ
// Cleanup ANeuralNetworksCompilation_free(compilation); ANeuralNetworksModel_free(model); ANeuralNetworksMemory_free(mem1);
การจัดการข้อผิดพลาดและ CPU สำรอง
หากเกิดข้อผิดพลาดระหว่างการแบ่งพาร์ติชัน หากไดรเวอร์คอมไพล์ (ชิ้นส่วนของ) โมเดลไม่สำเร็จ หรือหากไดรเวอร์เรียกใช้ (ชิ้นส่วนของ) โมเดลที่คอมไพล์แล้วไม่สำเร็จ NNAPI อาจกลับไปใช้การดำเนินการอย่างน้อย 1 รายการบน CPU ของตัวเอง
หากไคลเอ็นต์ NNAPI มีการดำเนินการเวอร์ชันที่เพิ่มประสิทธิภาพ (เช่น TFLite) การปิดใช้ CPU สำรองและจัดการกับความล้มเหลวด้วยการใช้งานการเพิ่มประสิทธิภาพของไคลเอ็นต์อาจเป็นประโยชน์
ใน Android 10 หากการคอมไพล์ดำเนินการโดยใช้ ANeuralNetworksCompilation_createForDevices
ระบบจะปิดใช้สำรองสำหรับ CPU
ใน Android P การดำเนินการ NNAPI จะกลับไปที่ CPU หากการดำเนินการในไดรเวอร์ล้มเหลว
การดำเนินการนี้ใช้ได้กับ Android 10 เมื่อใช้ ANeuralNetworksCompilation_create
แทน ANeuralNetworksCompilation_createForDevices
ด้วย
การดำเนินการครั้งแรกจะเปลี่ยนกลับไปใช้พาร์ติชันเดียวนั้น และหากยังคงดำเนินการไม่สำเร็จ ก็จะลองใช้ทั้งโมเดลใน CPU อีกครั้ง
หากการแบ่งพาร์ติชันหรือการคอมไพล์ล้มเหลว ระบบจะลองใช้ทั้งโมเดลบน CPU
แต่ก็อาจมีบางกรณีที่ CPU ไม่รองรับการดำเนินการบางรายการ และการคอมไพล์หรือการดำเนินการในสถานการณ์เช่นนี้จะล้มเหลวแทนที่จะย้อนกลับ
แม้ว่าจะปิดใช้การสำรองข้อมูล CPU แล้ว แต่การดำเนินการในโมเดลที่กำหนดเวลาไว้บน CPU อาจยังคงอยู่ หาก CPU อยู่ในรายการตัวประมวลผลที่ระบุให้กับ ANeuralNetworksCompilation_createForDevices
และเป็นตัวประมวลผลเพียงตัวเดียวที่รองรับการดำเนินการเหล่านั้น หรือเป็นตัวประมวลผลที่อ้างว่ามีประสิทธิภาพดีที่สุดสำหรับการดำเนินการเหล่านั้น ระบบจะเลือก CPU ดังกล่าวเป็นผู้ดำเนินการหลัก (ไม่ใช่สำรอง)
ใช้ ANeuralNetworksCompilation_createForDevices
ในขณะที่ยกเว้น nnapi-reference
ออกจากรายการอุปกรณ์เพื่อให้แน่ใจว่าไม่มีการดำเนินการของ CPU
ตั้งแต่ Android P เป็นต้นไป คุณสามารถปิดใช้การแสดงผลสำรองในเวลาที่ดำเนินการในบิลด์ DEBUG ได้โดยการตั้งค่าพร็อพเพอร์ตี้ debug.nn.partition
เป็น 2
โดเมนของหน่วยความจำ
ใน Android 11 ขึ้นไป NNAPI รองรับโดเมนหน่วยความจำที่มีอินเทอร์เฟซตัวจัดสรรสำหรับหน่วยความจำแบบทึบ ซึ่งช่วยให้แอปพลิเคชันสามารถส่งหน่วยความจำแบบอุปกรณ์ไปยังการดำเนินการต่างๆ ได้ เพื่อไม่ให้ NNAPI คัดลอกหรือเปลี่ยนรูปแบบข้อมูลโดยไม่จำเป็นเมื่อดำเนินการติดต่อกันในไดรเวอร์เดียวกัน
ฟีเจอร์โดเมนหน่วยความจำมีไว้สำหรับ tensor ที่ส่วนใหญ่เป็นระบบภายในกับไดรเวอร์และไม่จําเป็นต้องเข้าถึงฝั่งไคลเอ็นต์บ่อยๆ ตัวอย่างเทนเซอร์ดังกล่าว ได้แก่ เทนเซอร์สถานะในโมเดลลำดับ สำหรับ tensor ที่จำเป็นต้องเข้าถึง CPU บ่อยครั้งในฝั่งไคลเอ็นต์ ให้ใช้พูลหน่วยความจำที่ใช้ร่วมกันแทน
หากต้องการจัดสรรหน่วยความจำแบบทึบ ให้ทำตามขั้นตอนต่อไปนี้
เรียกใช้ฟังก์ชัน
ANeuralNetworksMemoryDesc_create()
เพื่อสร้างตัวบ่งชี้หน่วยความจำใหม่// Create a memory descriptor ANeuralNetworksMemoryDesc* desc; ANeuralNetworksMemoryDesc_create(&desc);
ระบุบทบาทอินพุตและเอาต์พุตที่ต้องการทั้งหมดโดยเรียกใช้
ANeuralNetworksMemoryDesc_addInputRole()
และANeuralNetworksMemoryDesc_addOutputRole()
// Specify that the memory may be used as the first input and the first output // of the compilation ANeuralNetworksMemoryDesc_addInputRole(desc, compilation, 0, 1.0f); ANeuralNetworksMemoryDesc_addOutputRole(desc, compilation, 0, 1.0f);
(ไม่บังคับ) ระบุมิติข้อมูลหน่วยความจําโดยเรียกใช้
ANeuralNetworksMemoryDesc_setDimensions()
// Specify the memory dimensions uint32_t dims[] = {3, 4}; ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
สรุปคําจํากัดความของคําอธิบายโดยเรียกใช้
ANeuralNetworksMemoryDesc_finish()
ANeuralNetworksMemoryDesc_finish(desc);
allocate as many memories as you need by passing the descriptor to
ANeuralNetworksMemory_createFromDesc()
// Allocate two opaque memories with the descriptor ANeuralNetworksMemory* opaqueMem; ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
ปล่อยตัวระบุหน่วยความจำเมื่อไม่ต้องการแล้ว
ANeuralNetworksMemoryDesc_free(desc);
ไคลเอ็นต์จะใช้ออบเจ็กต์ ANeuralNetworksMemory
ที่สร้างขึ้นได้เฉพาะกับ ANeuralNetworksExecution_setInputFromMemory()
หรือ ANeuralNetworksExecution_setOutputFromMemory()
ตามบทบาทที่ระบุในออบเจ็กต์ ANeuralNetworksMemoryDesc
อาร์กิวเมนต์ออฟเซ็ตและความยาวต้องเป็น 0 ซึ่งหมายความว่าระบบใช้หน่วยความจำทั้งหมด ไคลเอ็นต์อาจตั้งค่าหรือดึงเนื้อหาของหน่วยความจำอย่างชัดเจนโดยใช้ ANeuralNetworksMemory_copy()
คุณสามารถสร้างความทรงจำแบบทึบที่มีบทบาทของมิติข้อมูลหรือลําดับที่ไม่ระบุ
ในกรณีนี้ การสร้างหน่วยความจำอาจล้มเหลวโดยแสดงสถานะ ANEURALNETWORKS_OP_FAILED
หากไดรเวอร์ที่เกี่ยวข้องไม่รองรับ เราขอแนะนำให้ไคลเอ็นต์ใช้ตรรกะสำรองโดยจัดสรรบัฟเฟอร์ที่ใหญ่พอซึ่งรองรับโดย Ashmem หรือโหมด BLOB AHardwareBuffer
เมื่อ NNAPI ไม่ต้องเข้าถึงออบเจ็กต์หน่วยความจำแบบทึบอีกต่อไป ให้ยกเลิกการจองอินสแตนซ์ ANeuralNetworksMemory
ที่เกี่ยวข้อง ดังนี้
ANeuralNetworksMemory_free(opaqueMem);
วัดประสิทธิภาพ
คุณสามารถประเมินประสิทธิภาพของแอปได้โดยการวัดเวลาดำเนินการหรือโดยการโปรไฟล์
เวลาดำเนินการ
เมื่อต้องการระบุเวลาดำเนินการทั้งหมดผ่านรันไทม์ ให้ใช้ API การดำเนินการแบบซิงโครนัสและวัดเวลาที่เรียกใช้ เมื่อต้องการระบุเวลาดำเนินการทั้งหมดผ่านสแต็กซอฟต์แวร์ระดับล่าง คุณสามารถใช้ ANeuralNetworksExecution_setMeasureTiming
และ ANeuralNetworksExecution_getDuration
เพื่อรับข้อมูลต่อไปนี้
- เวลาในการดำเนินการบน Accelerator (ไม่ใช่ในไดรเวอร์ ซึ่งทำงานบนตัวประมวลผลโฮสต์)
- เวลาดำเนินการในไดรเวอร์ ซึ่งรวมถึงเวลาใน Accelerator
เวลาดำเนินการในไดรเวอร์จะไม่รวมค่าใช้จ่ายเพิ่มเติม เช่น รันไทม์เองและ IPC ที่จําเป็นสําหรับรันไทม์ในการสื่อสารกับไดรเวอร์
API เหล่านี้จะวัดระยะเวลาระหว่างเหตุการณ์งานที่ส่งและงานที่เสร็จสมบูรณ์ ไม่ใช่เวลาที่ไดรเวอร์หรือตัวเร่งดำเนินการใช้เพื่อทำการอนุมาน ซึ่งอาจถูกขัดจังหวะด้วยการสลับบริบท
ตัวอย่างเช่น หากการอนุมาน 1 เริ่มขึ้น จากนั้นไดรเวอร์จะหยุดทํางานเพื่อดําเนินการอนุมาน 2 จากนั้นจึงดําเนินการต่อและทําการอนุมาน 1 ให้เสร็จสมบูรณ์ เวลาในการดําเนินการสําหรับการอนุมาน 1 จะรวมเวลาที่หยุดทํางานเพื่อดําเนินการอนุมาน 2
ข้อมูลการกําหนดเวลานี้อาจมีประโยชน์สําหรับการติดตั้งใช้งานเวอร์ชันที่ใช้งานจริงของแอปพลิเคชันเพื่อรวบรวมข้อมูลการวัดและส่งข้อมูลทางไกลสําหรับการใช้งานแบบออฟไลน์ คุณสามารถใช้ข้อมูลเวลาเพื่อแก้ไขแอปให้มีประสิทธิภาพสูงขึ้นได้
โปรดคำนึงถึงสิ่งต่อไปนี้เมื่อใช้ฟังก์ชันการทำงานนี้
- การเก็บรวบรวมข้อมูลการวัดเวลาอาจส่งผลเสียต่อประสิทธิภาพ
- มีเพียงไดรเวอร์เท่านั้นที่คำนวณเวลาที่ใช้กับตนเองหรือในโปรแกรมเร่งความเร็วได้ โดยไม่รวมเวลาที่ใช้ในรันไทม์ NNAPI และใน IPC
- คุณใช้ API เหล่านี้ได้กับ
ANeuralNetworksExecution
ที่สร้างขึ้นด้วยANeuralNetworksCompilation_createForDevices
ที่มีnumDevices = 1
เท่านั้น - ไม่จำเป็นต้องมีไดรเวอร์เพื่อรายงานข้อมูลการจับเวลา
สร้างโปรไฟล์แอปพลิเคชันของคุณด้วย Android Systrace
ใน Android 10 เป็นต้นไป NNAPI จะสร้างเหตุการณ์ systrace โดยอัตโนมัติที่คุณใช้สร้างโปรไฟล์แอปพลิเคชันได้
แหล่งที่มาของ NNAPI มาพร้อมกับยูทิลิตี parse_systrace
เพื่อประมวลผลเหตุการณ์ systrace ที่แอปพลิเคชันสร้างขึ้น และสร้างมุมมองตารางที่แสดงเวลาที่ใช้ในแต่ละระยะของวงจรโมเดล (การสร้างอินสแตนซ์ การเตรียม การคอมไพล์ การดำเนินการ และการสิ้นสุด) และเลเยอร์ต่างๆ ของแอปพลิเคชัน เลเยอร์ที่แอปพลิเคชันของคุณแยกออกเป็นดังนี้
Application
: รหัสแอปพลิเคชันหลักRuntime
: รันไทม์ NNAPIIPC
: การสื่อสารระหว่างกระบวนการระหว่างรันไทม์ NNAPI กับโค้ดไดรเวอร์Driver
: กระบวนการของโปรแกรมเร่ง
สร้างข้อมูลการวิเคราะห์โปรไฟล์
สมมติว่าคุณตรวจสอบซอร์สทรี AOSP ที่ $ANDROID_BUILD_TOP และใช้ตัวอย่างการแยกประเภทรูปภาพด้วย TFLite เป็นแอปพลิเคชันเป้าหมาย คุณสามารถสร้างข้อมูลการโปรไฟล์ NNAPI ได้โดยทำตามขั้นตอนต่อไปนี้
- เริ่ม Android systrace ด้วยคำสั่งต่อไปนี้
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py -o trace.html -a org.tensorflow.lite.examples.classification nnapi hal freq sched idle load binder_driver
พารามิเตอร์ -o trace.html
ระบุว่าการติดตามจะเขียนใน trace.html
เมื่อทำโปรไฟล์แอปพลิเคชันของคุณเอง คุณจะต้องแทนที่ org.tensorflow.lite.examples.classification
ด้วยชื่อกระบวนการที่ระบุไว้ในไฟล์ Manifest ของแอป
ซึ่งจะทำให้คอนโซลเชลล์หนึ่งไม่ว่าง อย่าเรียกใช้คำสั่งในเบื้องหลังเนื่องจากจะรอให้ enter
สิ้นสุดการทำงานแบบอินเทอร์แอกทีฟ
- หลังจากเริ่มเครื่องมือรวบรวม Systrace แล้ว ให้เปิดแอปและทำการทดสอบประสิทธิภาพ
ในกรณีของเรา คุณสามารถเริ่มแอปการจัดประเภทรูปภาพจาก Android Studio หรือจาก UI ของโทรศัพท์ทดสอบโดยตรงหากติดตั้งแอปไว้แล้ว หากต้องการสร้างข้อมูล NNAPI บางส่วน คุณต้องกำหนดค่าแอปให้ใช้ NNAPI โดยเลือก NNAPI เป็นอุปกรณ์เป้าหมายในกล่องโต้ตอบการกำหนดค่าแอป
เมื่อการทดสอบเสร็จสิ้นแล้ว ให้สิ้นสุดการติดตามระบบโดยกด
enter
ในเทอร์มินัลคอนโซลที่ทำงานมาตั้งแต่ขั้นตอนที่ 1เรียกใช้ยูทิลิตี
systrace_parser
เพื่อสร้างสถิติแบบสะสม
$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html
โปรแกรมแยกวิเคราะห์ยอมรับพารามิเตอร์ต่อไปนี้
- --total-times
: แสดงเวลาทั้งหมดที่ใช้ในเลเยอร์ ซึ่งรวมถึงเวลาที่ใช้ในการรอการดําเนินการเมื่อมีการเรียกเลเยอร์ที่อยู่เบื้องล่าง
- --print-detail
: พิมพ์เหตุการณ์ทั้งหมดที่รวบรวมจาก systrace
- --per-execution
: พิมพ์เฉพาะการดําเนินการและระยะย่อยของการดําเนินการ (ตามเวลาการดําเนินการ) แทนสถิติสําหรับทุกระยะ
- --json
: สร้างเอาต์พุตในรูปแบบ JSON
ตัวอย่างเอาต์พุตแสดงอยู่ด้านล่าง
===========================================================================================================================================
NNAPI timing summary (total time, ms wall-clock) Execution
----------------------------------------------------
Initialization Preparation Compilation I/O Compute Results Ex. total Termination Total
-------------- ----------- ----------- ----------- ------------ ----------- ----------- ----------- ----------
Application n/a 19.06 1789.25 n/a n/a 6.70 21.37 n/a 1831.17*
Runtime - 18.60 1787.48 2.93 11.37 0.12 14.42 1.32 1821.81
IPC 1.77 - 1781.36 0.02 8.86 - 8.88 - 1792.01
Driver 1.04 - 1779.21 n/a n/a n/a 7.70 - 1787.95
Total 1.77* 19.06* 1789.25* 2.93* 11.74* 6.70* 21.37* 1.32* 1831.17*
===========================================================================================================================================
* This total ignores missing (n/a) values and thus is not necessarily consistent with the rest of the numbers
โปรแกรมแยกวิเคราะห์อาจไม่สําเร็จหากเหตุการณ์ที่รวบรวมไม่ได้แสดงการติดตามแอปพลิเคชันอย่างสมบูรณ์ โดยเฉพาะอย่างยิ่ง อาจไม่สําเร็จหากมีเหตุการณ์ systrace ที่สร้างขึ้นเพื่อระบุจุดสิ้นสุดของส่วนอยู่ในการติดตามโดยไม่มีเหตุการณ์เริ่มต้นของส่วนที่เกี่ยวข้อง กรณีนี้มักเกิดขึ้นหากมีการสร้างเหตุการณ์บางอย่างจากเซสชันการโปรไฟล์ก่อนหน้าเมื่อคุณเริ่มเครื่องมือรวบรวม Systrace ในกรณีนี้ คุณจะต้องเรียกใช้การโปรไฟล์อีกครั้ง
เพิ่มสถิติสําหรับโค้ดแอปพลิเคชันของคุณไปยังเอาต์พุต systrace_parser
แอปพลิเคชัน parse_systrace ทำงานตามฟังก์ชันการทำงานของ systrace ในตัวของ Android คุณสามารถเพิ่มการติดตามสําหรับการดำเนินการที่เฉพาะเจาะจงในแอปได้โดยใช้ systrace API (สําหรับ Java , สําหรับแอปพลิเคชันเนทีฟ ) ที่มีชื่อเหตุการณ์ที่กําหนดเอง
หากต้องการเชื่อมโยงเหตุการณ์ที่กําหนดเองกับระยะต่างๆ ของวงจรการทํางานของแอปพลิเคชัน ให้ใส่สตริงต่อไปนี้ไว้หน้าชื่อเหตุการณ์
[NN_LA_PI]
: เหตุการณ์ระดับแอปสําหรับการเริ่มต้น[NN_LA_PP]
: เหตุการณ์ระดับแอปพลิเคชันสําหรับการเตรียม[NN_LA_PC]
: เหตุการณ์ระดับแอปสําหรับการคอมไพล์[NN_LA_PE]
: เหตุการณ์ระดับแอปพลิเคชันสำหรับการดำเนินการ
ต่อไปนี้คือตัวอย่างวิธีแก้ไขโค้ดตัวอย่างการจัดประเภทรูปภาพของ TFLite โดยเพิ่มส่วน runInferenceModel
สำหรับระยะ Execution
และเลเยอร์ Application
ที่มีส่วนอื่นๆ อีก preprocessBitmap
ซึ่งจะไม่ได้รับการพิจารณาในการติดตาม NNAPI ส่วน runInferenceModel
จะเป็นส่วนหนึ่งของเหตุการณ์ systrace ที่ประมวลผลโดยโปรแกรมแยกวิเคราะห์ systrace ของ nnapi
Kotlin
/** Runs inference and returns the classification results. */ fun recognizeImage(bitmap: Bitmap): List{ // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap") convertBitmapToByteBuffer(bitmap) Trace.endSection() // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel") long startTime = SystemClock.uptimeMillis() runInference() long endTime = SystemClock.uptimeMillis() Trace.endSection() ... return recognitions }
Java
/** Runs inference and returns the classification results. */ public ListrecognizeImage(final Bitmap bitmap) { // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap"); convertBitmapToByteBuffer(bitmap); Trace.endSection(); // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel"); long startTime = SystemClock.uptimeMillis(); runInference(); long endTime = SystemClock.uptimeMillis(); Trace.endSection(); ... Trace.endSection(); return recognitions; }
คุณภาพของบริการ
ใน Android 11 ขึ้นไป NNAPI ทำให้คุณภาพการบริการ (QoS) ดีขึ้นโดยอนุญาตให้แอปพลิเคชันระบุลำดับความสำคัญของโมเดลที่เกี่ยวข้อง ระยะเวลาสูงสุดที่คาดว่าจะเตรียมโมเดลหนึ่งๆ และระยะเวลาสูงสุดที่คาดว่าจะเสร็จสิ้นการคำนวณหนึ่งๆ Android 11 ยังเปิดตัวรหัสผลลัพธ์ NNAPI เพิ่มเติมที่ช่วยให้แอปพลิเคชันเข้าใจความล้มเหลว เช่น กำหนดเวลาการเรียกใช้ที่พลาดไป
กำหนดลำดับความสำคัญของภาระงาน
หากต้องการตั้งค่าลำดับความสำคัญของภาระงาน NNAPI ให้เรียกใช้ ANeuralNetworksCompilation_setPriority()
ก่อนเรียกใช้ ANeuralNetworksCompilation_finish()
กำหนดกำหนดเวลา
แอปพลิเคชันสามารถกำหนดกำหนดเวลาทั้งสำหรับการคอมไพล์และการอนุมานโมเดล
- หากต้องการตั้งค่าระยะหมดเวลาของการรวบรวม ให้เรียกใช้
ANeuralNetworksCompilation_setTimeout()
ก่อนเรียกใช้ANeuralNetworksCompilation_finish()
- หากต้องการตั้งค่าการหมดเวลาการอนุมาน ให้เรียกใช้
ANeuralNetworksExecution_setTimeout()
ก่อนเริ่มการคอมไพล์
ข้อมูลเพิ่มเติมเกี่ยวกับโอเปอเรนด์
ส่วนต่อไปนี้ครอบคลุมหัวข้อขั้นสูงเกี่ยวกับการใช้ตัวถูกดำเนินการ
Tensor ที่แปลงค่าเป็นจำนวนเต็ม
เทนเซอร์ที่แปลงเป็นจำนวนเต็มเป็นวิธีที่กะทัดรัดในการแสดงอาร์เรย์ n มิติของค่าทศนิยม
NNAPI รองรับ Tensor แบบ 8 บิตที่แปลงค่าแบบไม่สมมาตร สำหรับ Tensor เหล่านี้ ค่าของแต่ละเซลล์จะแสดงด้วยจำนวนเต็ม 8 บิต เทนเซอร์จะเชื่อมโยงกับค่าสเกลและค่าจุดศูนย์ ซึ่งจะใช้แปลงจำนวนเต็ม 8 บิตเป็นค่าทศนิยมที่แสดง
สูตรคือ
(cellValue - zeroPoint) * scale
โดยที่ค่า zeroPoint เป็นจำนวนเต็ม 32 บิต และ scale เป็นค่าตัวเลขทศนิยม 32 บิต
เมื่อเปรียบเทียบกับ tensor ของค่าทศนิยม 32 บิตแล้ว Tensor ควอนเซอร์ 8 บิตมีข้อดี 2 ข้อดังนี้
- แอปพลิเคชันมีขนาดเล็กลงเนื่องจากน้ำหนักที่ฝึกใช้ไปแล้ว 1 ใน 4 ของ Tensor แบบ 32 บิต
- การคํานวณมักจะทําได้เร็วขึ้น ทั้งนี้เนื่องจากมีข้อมูลที่ต้องดึงจากหน่วยความจำจำนวนน้อย และประสิทธิภาพของหน่วยประมวลผล เช่น DSP ในการคำนวณจำนวนเต็ม
แม้ว่าระบบจะแปลงโมเดลจุดลอยตัวเป็นโมเดลที่เล็กลงได้ แต่ประสบการณ์ของเราแสดงให้เห็นว่าผลลัพธ์ที่ดีขึ้นได้จากการฝึกโมเดลที่เล็กลงโดยตรง ยิ่งไปกว่านั้น โครงข่ายประสาทจะเรียนรู้ที่จะชดเชยรายละเอียดที่เพิ่มขึ้นของแต่ละค่า สำหรับ Tensor แต่ละรายการที่ปรับขนาด ค่ามาตราส่วนและค่าเป็นศูนย์จะกำหนดระหว่างกระบวนการฝึก
ใน NNAPI คุณจะกําหนดประเภท Tensor ที่แปลงค่าเป็นจำนวนเต็มได้โดยตั้งค่าช่องประเภทของโครงสร้างข้อมูล ANeuralNetworksOperandType
เป็น ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
นอกจากนี้ คุณยังระบุค่าสเกลและ zeroPoint ของเทมพอร์ในโครงสร้างข้อมูลดังกล่าวได้ด้วย
นอกจากเทนเซอร์ที่แปลงให้เล็กลงแบบไม่สมมาตร 8 บิตแล้ว NNAPI ยังรองรับรายการต่อไปนี้ด้วย
ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
ซึ่งใช้สำหรับการแสดงน้ำหนักต่อการดำเนินการCONV/DEPTHWISE_CONV/TRANSPOSED_CONV
ได้ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
ซึ่งคุณใช้สำหรับสถานะภายในของQUANTIZED_16BIT_LSTM
ได้ANEURALNETWORKS_TENSOR_QUANT8_SYMM
ซึ่งอาจเป็นอินพุตสำหรับANEURALNETWORKS_DEQUANTIZE
ตัวถูกดำเนินการที่ไม่บังคับ
การดำเนินการบางอย่าง เช่น ANEURALNETWORKS_LSH_PROJECTION
ใช้โอเปอเรนด์ที่ไม่บังคับ หากต้องการระบุว่าในโมเดลไม่มีตัวดำเนินการที่ไม่บังคับ ให้เรียกใช้ฟังก์ชัน ANeuralNetworksModel_setOperandValue()
โดยส่ง NULL
สำหรับบัฟเฟอร์และ 0 สำหรับความยาว
หากการตัดสินใจว่าออบเจ็กต์ใดมีหรือไม่มีแตกต่างกันไปในแต่ละการเรียกใช้ คุณระบุได้ว่าออบเจ็กต์นั้นไม่มีอยู่โดยใช้ฟังก์ชัน ANeuralNetworksExecution_setInput()
หรือ ANeuralNetworksExecution_setOutput()
โดยส่ง NULL
สำหรับบัฟเฟอร์และ 0 สำหรับความยาว
Tensor ของอันดับที่ไม่รู้จัก
Android 9 (API ระดับ 28) เปิดตัวตัวดำเนินการโมเดลของมิติข้อมูลที่ไม่ทราบ แต่ทราบลําดับ (จํานวนมิติข้อมูล) Android 10 (API ระดับ 29) ได้เปิดตัวเทนเซอร์ที่มีลําดับที่ไม่รู้จัก ดังที่แสดงใน ANeuralNetworksOperandType
การเปรียบเทียบ NNAPI
การเปรียบเทียบ NNAPI มีอยู่ใน AOSP ใน platform/test/mlts/benchmark
(แอปการเปรียบเทียบ) และ platform/test/mlts/models
(โมเดลและชุดข้อมูล)
การเปรียบเทียบนี้จะประเมินเวลาในการตอบสนองและความแม่นยำ แล้วเปรียบเทียบไดรเวอร์กับงานชิ้นเดียวกันที่ใช้ Tensorflow Lite ที่ทำงานบน CPU สำหรับโมเดลและชุดข้อมูลเดียวกัน
ในการใช้การเปรียบเทียบ ให้ทำดังนี้
เชื่อมต่ออุปกรณ์ Android เป้าหมายกับคอมพิวเตอร์ เปิดหน้าต่างเทอร์มินัล และตรวจสอบว่าอุปกรณ์เข้าถึงได้ผ่าน adb
หากมีอุปกรณ์ Android เชื่อมต่ออยู่มากกว่า 1 เครื่อง ให้ส่งออกตัวแปรสภาพแวดล้อมของอุปกรณ์เป้าหมาย
ANDROID_SERIAL
ไปที่ไดเรกทอรีแหล่งที่มาระดับบนสุดของ Android
เรียกใช้คำสั่งต่อไปนี้
lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available ./test/mlts/benchmark/build_and_run_benchmark.sh
เมื่อการเรียกใช้การทดสอบประสิทธิภาพสิ้นสุดลง ผลลัพธ์จะแสดงเป็นหน้า HTML ที่ส่งไปยัง
xdg-open
บันทึกของ NNAPI
NNAPI จะสร้างข้อมูลการวินิจฉัยที่เป็นประโยชน์ในบันทึกของระบบ หากต้องการวิเคราะห์บันทึก ให้ใช้ยูทิลิตี logcat
เปิดใช้การบันทึก NNAPI แบบละเอียดสำหรับเฟสหรือคอมโพเนนต์ที่เฉพาะเจาะจงโดยการตั้งค่าพร็อพเพอร์ตี้ debug.nn.vlog
(โดยใช้ adb shell
) เป็นรายการค่าต่อไปนี้ โดยคั่นด้วยการเว้นวรรค เครื่องหมายโคลอน หรือคอมมา
model
: การสร้างโมเดลcompilation
: การสร้างแผนการดำเนินการโมเดลและคอมไพล์execution
: การดำเนินการของโมเดลcpuexe
: การดำเนินการโดยใช้การใช้งาน CPU ของ NNAPImanager
: ส่วนขยาย NNAPI, อินเทอร์เฟซและข้อมูลเกี่ยวกับความสามารถที่ใช้ได้all
หรือ1
: องค์ประกอบทั้งหมดข้างต้น
เช่น หากต้องการเปิดใช้การบันทึกแบบละเอียดทั้งหมด ให้ใช้คําสั่ง
adb shell setprop debug.nn.vlog all
หากต้องการปิดใช้การบันทึกแบบละเอียด ให้ใช้คำสั่ง
adb shell setprop debug.nn.vlog '""'
เมื่อเปิดใช้แล้ว การบันทึกแบบละเอียดจะสร้างรายการบันทึกที่ระดับ INFO โดยมีการตั้งค่าแท็กเป็นชื่อระยะหรือคอมโพเนนต์
นอกจากdebug.nn.vlog
ข้อความที่มีการควบคุมแล้ว คอมโพเนนต์ NNAPI API ยังมีรายการบันทึกอื่นๆ ในระดับต่างๆ โดยแต่ละรายการใช้แท็กบันทึกที่เฉพาะเจาะจง
หากต้องการดูรายการคอมโพเนนต์ ให้ค้นหาในลําดับชั้นซอร์สโค้ดโดยใช้นิพจน์ต่อไปนี้
grep -R 'define LOG_TAG' | awk -F '"' '{print $2}' | sort -u | egrep -v "Sample|FileTag|test"
ปัจจุบันนิพจน์นี้จะแสดงแท็กต่อไปนี้
- สร้างภาพถ่ายอัจฉริยะ
- การติดต่อกลับ
- CompilationBuilder
- CpuExecutor
- ExecutionBuilder
- ExecutionBurstController
- ExecutionBurstServer
- แผนดำเนินการ
- FibonacciDriver
- กราฟ Dump
- IndexedShapeWrapper
- IonWatcher
- ผู้จัดการ
- หน่วยความจำ
- MemoryUtils
- โมเดลเมตา
- ModelArgumentInfo
- ModelBuilder
- NeuralNetworks
- โอเปอเรเตอร์การแก้ไข
- การทำงาน
- OperationsUtils
- PackageInfo
- TokenHasher
- เครื่องมือจัดการประเภท
- Utils
- ValidateHal
- VersionedInterfaces
หากต้องการควบคุมระดับของข้อความบันทึกที่แสดงโดย logcat
ให้ใช้ตัวแปรสภาพแวดล้อม ANDROID_LOG_TAGS
หากต้องการแสดงข้อความบันทึก NNAPI ชุดสมบูรณ์และปิดใช้ข้อความอื่นๆ ให้ตั้งค่า ANDROID_LOG_TAGS
เป็นค่าต่อไปนี้
BurstBuilder:V Callbacks:V CompilationBuilder:V CpuExecutor:V ExecutionBuilder:V ExecutionBurstController:V ExecutionBurstServer:V ExecutionPlan:V FibonacciDriver:V GraphDump:V IndexedShapeWrapper:V IonWatcher:V Manager:V MemoryUtils:V Memory:V MetaModel:V ModelArgumentInfo:V ModelBuilder:V NeuralNetworks:V OperationResolver:V OperationsUtils:V Operations:V PackageInfo:V TokenHasher:V TypeManager:V Utils:V ValidateHal:V VersionedInterfaces:V *:S.
คุณตั้งค่า ANDROID_LOG_TAGS
ได้โดยใช้คำสั่งต่อไปนี้
export ANDROID_LOG_TAGS=$(grep -R 'define LOG_TAG' | awk -F '"' '{ print $2 ":V" }' | sort -u | egrep -v "Sample|FileTag|test" | xargs echo -n; echo ' *:S')
โปรดทราบว่านี่เป็นเพียงตัวกรองที่ใช้กับ logcat
เท่านั้น คุณยังคงต้องตั้งค่าพร็อพเพอร์ตี้ debug.nn.vlog
เป็น all
เพื่อสร้างข้อมูลบันทึกแบบละเอียด