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 ไม่มีฟังก์ชันการทำงานสำหรับการเรียกใช้โมเดลในระบบคลาวด์
ดูตัวอย่างวิธีใช้ NNAPI ได้จากตัวอย่าง Android Neural Networks API
ทําความเข้าใจรันไทม์ Neural Networks API
NNAPI มีไว้เพื่อให้ไลบรารี เฟรมเวิร์ก และเครื่องมือแมชชีนเลิร์นนิงเรียกใช้ ซึ่งช่วยให้นักพัฒนาแอปฝึกโมเดลนอกอุปกรณ์และนำไปใช้งานได้ในอุปกรณ์ Android โดยทั่วไปแล้ว แอปจะไม่ใช้ NNAPI โดยตรง แต่จะใช้เฟรมเวิร์กแมชชีนเลิร์นนิงในระดับที่สูงขึ้นแทน เฟรมเวิร์กเหล่านี้อาจใช้ NNAPI เพื่อดำเนินการอนุมานที่เร่งด้วยฮาร์ดแวร์ในอุปกรณ์ที่รองรับ
รันไทม์ของโครงข่ายประสาทของ Android สามารถกระจายภาระงานการคำนวณไปยังโปรเซสเซอร์ในอุปกรณ์ที่มีอยู่ได้อย่างมีประสิทธิภาพ โดยอิงตามข้อกำหนดของแอปและความสามารถของฮาร์ดแวร์ในอุปกรณ์ Android ซึ่งรวมถึงฮาร์ดแวร์โครงข่ายประสาทเฉพาะ หน่วยประมวลผลกราฟิก (GPU) และโปรเซสเซอร์สัญญาณดิจิทัล (DSP)
สำหรับอุปกรณ์ 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
หลายอินสแตนซ์สำหรับไฟล์หลายไฟล์ได้
ใช้บัฟเฟอร์ฮาร์ดแวร์เนทีฟ
คุณสามารถใช้บัฟเฟอร์ฮาร์ดแวร์แบบเนทีฟสำหรับอินพุต เอาต์พุต และค่าโอเปอเรนด์แบบคงที่ของโมเดล ในบางกรณี ตัวเร่ง NNAPI สามารถเข้าถึงออบเจ็กต์ AHardwareBuffer
ได้โดยไม่ต้องให้ไดรเวอร์คัดลอกข้อมูล AHardwareBuffer
มีการกําหนดค่าที่หลากหลาย และตัวเร่ง NNAPI บางรุ่นอาจไม่รองรับการกําหนดค่าเหล่านี้ทั้งหมด ข้อจำกัดนี้ทำให้คุณต้องดูข้อจำกัดที่ระบุไว้ในเอกสารอ้างอิง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: เมื่อส่ง 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 จะกระจายงานไปยังอุปกรณ์ต่างๆ เช่น หากมีการจัดเตรียมอุปกรณ์ให้กับ ANeuralNetworksCompilation_createForDevices
มากกว่า 1 เครื่อง ระบบจะพิจารณาอุปกรณ์ที่ระบุทั้งหมดเมื่อจัดสรรงาน โปรดทราบว่าหากอุปกรณ์ 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 คัดลอกหรือเปลี่ยนรูปแบบข้อมูลโดยไม่จำเป็นเมื่อทำการดําเนินการต่อเนื่องในไดรเวอร์เดียวกัน
ฟีเจอร์โดเมนหน่วยความจำมีไว้สำหรับเทนเซอร์ส่วนใหญ่ที่เป็นข้อมูลภายในของไดรเวอร์และไม่จำเป็นต้องเข้าถึงฝั่งไคลเอ็นต์บ่อยครั้ง ตัวอย่างเทนเซอร์ดังกล่าว ได้แก่ เทนเซอร์สถานะในโมเดลลำดับ สำหรับเทนเซอร์ที่ต้องเข้าถึง 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
เท่านั้น ต้องตั้งค่าอาร์กิวเมนต์ "offset" และ "length" เป็น 0 ซึ่งบ่งบอกว่ามีการใช้หน่วยความจําทั้งหมด นอกจากนี้ ไคลเอ็นต์ยังอาจตั้งค่าหรือดึงข้อมูลเนื้อหาของหน่วยความจําอย่างชัดเจนได้โดยใช้ ANeuralNetworksMemory_copy()
คุณสามารถสร้างความทรงจำแบบทึบที่มีบทบาทของมิติข้อมูลหรือลําดับที่ไม่ระบุ
ในกรณีนี้ การสร้างหน่วยความจำอาจล้มเหลวโดยแสดงสถานะ ANEURALNETWORKS_OP_FAILED
หากไดรเวอร์พื้นฐานไม่รองรับ เราขอแนะนำให้ไคลเอ็นต์ใช้ตรรกะสำรองโดยการจัดสรรบัฟเฟอร์ที่ใหญ่พอซึ่งรองรับโดย Ashmem หรือโหมด BLOB AHardwareBuffer
เมื่อ NNAPI ไม่ต้องเข้าถึงออบเจ็กต์หน่วยความจำแบบทึบอีกต่อไป ให้ยกเลิกการจองอินสแตนซ์ ANeuralNetworksMemory
ที่เกี่ยวข้อง ดังนี้
ANeuralNetworksMemory_free(opaqueMem);
วัดประสิทธิภาพ
คุณสามารถประเมินประสิทธิภาพของแอปได้โดยการวัดเวลาดำเนินการหรือโดยการโปรไฟล์
เวลาดำเนินการ
หากต้องการระบุเวลาดำเนินการทั้งหมดผ่านรันไทม์ ให้ใช้ API การเรียกใช้แบบซิงค์และวัดเวลาที่ใช้ในการเรียกใช้ เมื่อต้องการระบุเวลาดำเนินการทั้งหมดผ่านสแต็กซอฟต์แวร์ระดับล่าง คุณสามารถใช้ ANeuralNetworksExecution_setMeasureTiming
และ ANeuralNetworksExecution_getDuration
เพื่อดูข้อมูลต่อไปนี้
- เวลาที่ใช้ในการดำเนินการบนตัวเร่ง (ไม่ใช่ในไดรเวอร์ที่ทำงานบนโปรเซสเซอร์ของโฮสต์)
- เวลาดำเนินการในไดรเวอร์ ซึ่งรวมถึงเวลาใน Accelerator
เวลาดำเนินการในไดรเวอร์จะไม่รวมค่าใช้จ่ายเพิ่มเติม เช่น รันไทม์เองและ IPC ที่จําเป็นสําหรับรันไทม์ในการสื่อสารกับไดรเวอร์
API เหล่านี้จะวัดระยะเวลาระหว่างเหตุการณ์งานที่ส่งและงานที่เสร็จสมบูรณ์ ไม่ใช่เวลาที่ไดรเวอร์หรือตัวเร่งใช้เพื่อทำการอนุมาน ซึ่งอาจถูกขัดจังหวะด้วยการสลับบริบท
ตัวอย่างเช่น หากการอนุมาน 1 เริ่มขึ้น จากนั้นไดรเวอร์จะหยุดทํางานเพื่อดําเนินการอนุมาน 2 จากนั้นจึงดําเนินการต่อและทําการอนุมาน 1 ให้เสร็จสมบูรณ์ เวลาในการดําเนินการสําหรับการอนุมาน 1 จะรวมเวลาที่หยุดทํางานเพื่อดําเนินการอนุมาน 2 ด้วย
ข้อมูลการกําหนดเวลานี้อาจมีประโยชน์สําหรับการติดตั้งใช้งานแอปพลิเคชันเวอร์ชันที่ใช้งานจริงเพื่อรวบรวมข้อมูลการวัดและส่งข้อมูลทางไกลสําหรับการใช้งานแบบออฟไลน์ คุณสามารถใช้ข้อมูลเวลาเพื่อแก้ไขแอปให้มีประสิทธิภาพสูงขึ้นได้
โปรดคำนึงถึงสิ่งต่อไปนี้เมื่อใช้ฟังก์ชันการทำงานนี้
- การเก็บรวบรวมข้อมูลการวัดเวลาอาจส่งผลเสียต่อประสิทธิภาพ
- มีเพียงไดรเวอร์เท่านั้นที่คำนวณเวลาที่ใช้กับตนเองหรือในโปรแกรมเร่งความเร็วได้ โดยไม่รวมเวลาที่ใช้ในรันไทม์ NNAPI และใน IPC
- คุณใช้ API เหล่านี้ได้กับ
ANeuralNetworksExecution
ที่สร้างขึ้นด้วยANeuralNetworksCompilation_createForDevices
ที่มีnumDevices = 1
เท่านั้น - ไม่จำเป็นต้องมีไดรเวอร์เพื่อรายงานข้อมูลการจับเวลา
ทำโปรไฟล์แอปพลิเคชันด้วย Systrace ของ Android
ตั้งแต่ Android 10 เป็นต้นไป NNAPI จะสร้างเหตุการณ์ systrace โดยอัตโนมัติซึ่งคุณใช้เพื่อโปรไฟล์แอปพลิเคชันได้
แหล่งที่มาของ NNAPI มาพร้อมกับยูทิลิตี parse_systrace
เพื่อประมวลผลเหตุการณ์ systrace ที่แอปพลิเคชันสร้างขึ้น และสร้างมุมมองตารางที่แสดงเวลาที่ใช้ในแต่ละระยะของวงจรโมเดล (การสร้างอินสแตนซ์ การเตรียม การคอมไพล์ การดำเนินการ และการสิ้นสุด) และเลเยอร์ต่างๆ ของแอปพลิเคชัน เลเยอร์ที่แอปพลิเคชันของคุณแยกออกเป็นดังนี้
Application
: รหัสแอปพลิเคชันหลักRuntime
: รันไทม์ NNAPIIPC
: การสื่อสารระหว่างกระบวนการระหว่างรันไทม์ NNAPI กับโค้ดไดรเวอร์Driver
: กระบวนการของโปรแกรมเร่ง
สร้างข้อมูลการวิเคราะห์โปรไฟล์
สมมติว่าคุณตรวจสอบซอร์สทรี AOSP ที่ $ANDROID_BUILD_TOP และใช้ตัวอย่างการแยกประเภทรูปภาพด้วย TFLite เป็นแอปพลิเคชันเป้าหมาย คุณสามารถสร้างข้อมูลการโปรไฟล์ NNAPI ได้โดยทำตามขั้นตอนต่อไปนี้
- เริ่ม Systrace ของ Android ด้วยคำสั่งต่อไปนี้
$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 บิตที่แปลงค่าแบบไม่สมมาตร สำหรับเทนเซอร์เหล่านี้ ค่าของแต่ละเซลล์จะแสดงด้วยจำนวนเต็ม 8 บิต เทนเซอร์จะเชื่อมโยงกับค่าสเกลและค่าจุดศูนย์ ซึ่งจะใช้แปลงจำนวนเต็ม 8 บิตเป็นค่าทศนิยมที่แสดง
สูตรคือ
(cellValue - zeroPoint) * scale
โดยที่ค่า zeroPoint เป็นจำนวนเต็ม 32 บิต และ scale เป็นค่าตัวเลขทศนิยม 32 บิต
เมื่อเทียบกับเทนเซอร์ของค่าทศนิยม 32 บิต เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม 8 บิตมีข้อดี 2 ข้อดังนี้
- แอปพลิเคชันของคุณจะมีขนาดเล็กลง เนื่องจากน้ำหนักที่ผ่านการฝึกมีขนาดเล็กกว่า 1 ใน 4 ของขนาดเทนเซอร์ 32 บิต
- การคํานวณมักจะทําได้เร็วขึ้น สาเหตุคือมีปริมาณข้อมูลที่จำเป็นต้องดึงมาจากหน่วยความจำน้อยลงและประสิทธิภาพของโปรเซสเซอร์ เช่น DSP ในการทําคณิตศาสตร์แบบจำนวนเต็ม
แม้ว่าจะแปลงโมเดลจุดลอยตัวเป็นโมเดลที่เล็กลงได้ แต่ประสบการณ์ของเราแสดงให้เห็นว่าการฝึกโมเดลที่เล็กลงโดยตรงจะให้ผลลัพธ์ที่ดีกว่า ผลที่ได้คือ เครือข่ายประสาทจะเรียนรู้เพื่อชดเชยความละเอียดที่เพิ่มขึ้นของแต่ละค่า ระบบจะกำหนดค่าสเกลและค่าจุดเริ่มต้นของแต่ละเทนเซอร์ที่แปลงค่าเป็นจำนวนเต็มระหว่างกระบวนการฝึก
ใน 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 สำหรับความยาว
เทนเซอร์ที่มีลําดับชั้นที่ไม่รู้จัก
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
: การดำเนินการโดยใช้การติดตั้งใช้งาน NNAPI CPUmanager
: ส่วนขยาย 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"
ปัจจุบันนิพจน์นี้จะแสดงแท็กต่อไปนี้
- BurstBuilder
- การติดต่อกลับ
- CompilationBuilder
- CpuExecutor
- ExecutionBuilder
- ExecutionBurstController
- ExecutionBurstServer
- ExecutionPlan
- FibonacciDriver
- GraphDump
- IndexedShapeWrapper
- IonWatcher
- ผู้จัดการ
- หน่วยความจำ
- MemoryUtils
- MetaModel
- ModelArgumentInfo
- ModelBuilder
- NeuralNetworks
- OperationResolver
- การทำงาน
- OperationsUtils
- PackageInfo
- TokenHasher
- TypeManager
- 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
เพื่อสร้างข้อมูลบันทึกแบบละเอียด