API โครงข่ายระบบประสาทเทียม

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

รูปที่ 1 สถาปัตยกรรมระบบสําหรับ Android Neural Networks API

รูปแบบการเขียนโปรแกรม Neural Networks API

หากต้องการทําการคํานวณโดยใช้ NNAPI ก่อนอื่นคุณต้องสร้างกราฟที่มีทิศทางซึ่งกําหนดการคํานวณที่จะทํา กราฟการคำนวณนี้รวมกับข้อมูลที่ป้อน (เช่น น้ำหนักและความลำเอียงที่ส่งต่อมาจากเฟรมเวิร์กแมชชีนเลิร์นนิง) จะเป็นโมเดลสำหรับการประเมินรันไทม์ NNAPI

NNAPI ใช้การแยกแยะหลัก 4 รายการ ได้แก่

  • โมเดล: กราฟการคํานวณของการดำเนินการทางคณิตศาสตร์และค่าคงที่ที่เรียนรู้ผ่านกระบวนการฝึก การดำเนินการเหล่านี้มีไว้สำหรับเครือข่ายประสาทโดยเฉพาะ ซึ่งรวมถึงการฟัซชัน 2 มิติ (2D) การกระตุ้นแบบโลจิสติก (sigmoid) การกระตุ้นแบบเชิงเส้นที่แก้ไขแล้ว (ReLU) และอื่นๆ การสร้างโมเดลเป็นการดำเนินการแบบซิงค์ เมื่อสร้างเสร็จแล้ว คุณจะนํารายงานไปใช้ซ้ำในชุดข้อความและการคอมไพล์ได้ ใน NNAPI โมเดลจะแสดงเป็นANeuralNetworksModel อินสแตนซ์
  • การคอมไพล์: แสดงการกำหนดค่าสำหรับการคอมไพล์โมเดล NNAPI เป็นโค้ดระดับล่าง การสร้างการคอมไพล์เป็นการดำเนินการแบบซิงค์ เมื่อสร้างสำเร็จแล้ว คุณจะนําไปใช้งานซ้ำในเธรดและการดำเนินการต่างๆ ได้ ใน NNAPI การคอมไพล์แต่ละรายการจะแสดงเป็นอินสแตนซ์ ANeuralNetworksCompilation
  • หน่วยความจำ: แสดงหน่วยความจำที่ใช้ร่วมกัน ไฟล์ที่แมปหน่วยความจำ และบัฟเฟอร์หน่วยความจำที่คล้ายกัน การใช้บัฟเฟอร์หน่วยความจำทำให้รันไทม์ของ NNAPI โอนข้อมูลไปยังไดรเวอร์ได้อย่างมีประสิทธิภาพมากขึ้น โดยปกติแล้ว แอปจะสร้างบัฟเฟอร์หน่วยความจำที่แชร์ 1 รายการซึ่งมีเทนเซอร์ทั้งหมดที่จําเป็นในการกําหนดโมเดล คุณยังใช้บัฟเฟอร์หน่วยความจำเพื่อจัดเก็บอินพุตและเอาต์พุตสำหรับอินสแตนซ์การดำเนินการได้ด้วย ใน NNAPI บัฟเฟอร์หน่วยความจําแต่ละรายการจะแสดงเป็นANeuralNetworksMemory อินสแตนซ์
  • การดำเนินการ: อินเทอร์เฟซสำหรับการใช้โมเดล NNAPI กับชุดอินพุตและรวบรวมผลลัพธ์ การดำเนินการจะดำเนินการแบบซิงค์หรือไม่ซิงค์ก็ได้

    สําหรับการดําเนินการแบบไม่พร้อมกัน หลายเธรดจะรอการดําเนินการเดียวกันได้ เมื่อการดําเนินการนี้เสร็จสมบูรณ์ ระบบจะปล่อยเธรดทั้งหมด

    ใน NNAPI การเรียกใช้แต่ละครั้งจะแสดงเป็นANeuralNetworksExecution อินสแตนซ์

รูปที่ 2 แสดงขั้นตอนการเขียนโปรแกรมพื้นฐาน

รูปที่ 2 ขั้นตอนการเขียนโปรแกรมสำหรับ API โครงข่ายระบบประสาทเทียมของ Android

ส่วนที่เหลือของส่วนนี้จะอธิบายขั้นตอนในการตั้งค่าโมเดล 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 รายการ

รูปที่ 3 ตัวอย่างออบเจ็กต์การดำเนินการสำหรับโมเดล NNAPI

โมเดลด้านบนมีโอเปอเรนด์ 7 รายการ ระบบจะระบุตัวดำเนินการเหล่านี้โดยนัยจากดัชนีของลําดับที่เพิ่มลงในโมเดล อOperand แรกที่มีการเพิ่มจะมีดัชนี 0 อOperand ที่ 2 จะมีดัชนี 1 และอื่นๆ ตามลำดับ ตัวถูกดำเนินการ 1, 2, 3 และ 5 คือตัวถูกดำเนินการคงที่

ลำดับที่คุณเพิ่มตัวดำเนินการไม่มีผล เช่น ตัวดำเนินการเอาต์พุตของโมเดลอาจเป็นรายการแรกที่เพิ่ม ส่วนสําคัญคือการใช้ค่าดัชนีที่ถูกต้องเมื่ออ้างอิงไปยังออบเปอเรนด

ออบเจ็กต์การดำเนินการมีประเภท ซึ่งจะระบุเมื่อเพิ่มลงในโมเดล

ออบเจ็กต์จะไม่สามารถใช้เป็นทั้งอินพุตและเอาต์พุตของโมเดล

ออบเจ็กต์การดำเนินการทั้งหมดต้องเป็นอินพุตของโมเดล ค่าคงที่ หรือออบเจ็กต์การดำเนินการเอาต์พุตของการดำเนินการเพียง 1 รายการ

ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้โอเปอเรนด์ได้ที่ข้อมูลเพิ่มเติมเกี่ยวกับโอเปอเรนด์

การทำงาน

การดำเนินการจะระบุการคำนวณที่จะดำเนินการ การดำเนินการแต่ละรายการประกอบด้วยองค์ประกอบต่อไปนี้

  • ประเภทการดำเนินการ (เช่น การบวก การคูณ การฟัซซิออน)
  • รายการดัชนีของตัวถูกดำเนินการที่การดำเนินการใช้สำหรับอินพุต และ
  • รายการดัชนีของออบเจ็กต์การดำเนินการที่การดำเนินการใช้สำหรับเอาต์พุต

ลำดับในรายการเหล่านี้มีความสำคัญ โปรดดูข้อมูลอ้างอิง NNAPI API สำหรับอินพุตและเอาต์พุตที่คาดไว้ของการดำเนินการแต่ละประเภท

คุณต้องเพิ่มตัวถูกดำเนินการที่การดำเนินการใช้หรือสร้างในโมเดลก่อนเพิ่มการดำเนินการ

ลำดับที่คุณเพิ่มการดำเนินการไม่มีผล NNAPI อาศัยความสัมพันธ์ที่สร้างขึ้นโดยกราฟการคํานวณของออพเพอร์แลนด์และการดำเนินการเพื่อกําหนดลําดับการดําเนินการ

การดำเนินการที่ NNAPI รองรับจะสรุปไว้ในตารางด้านล่าง

หมวดหมู่ การทำงาน
การดำเนินการทางคณิตศาสตร์แบบองค์ประกอบ
การจัดการ Tensor
การดำเนินการกับอิมเมจ
การดำเนินการค้นหา
การดำเนินการตามมาตรฐาน
การดำเนินการ Conv
การดำเนินการกับพูล
การดำเนินการเปิดใช้งาน
การดำเนินการอื่นๆ

ปัญหาที่ทราบใน 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

หากต้องการสร้างโมเดล ให้ทำตามขั้นตอนต่อไปนี้

  1. เรียกใช้ฟังก์ชัน ANeuralNetworksModel_create() เพื่อกําหนดโมเดลว่าง

    ANeuralNetworksModel* model = NULL;
    ANeuralNetworksModel_create(&model);
  2. เพิ่มตัวดำเนินการลงในโมเดลโดยเรียกใช้ 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
  3. สำหรับตัวถูกดำเนินการที่มีค่าคงที่ เช่น น้ำหนักและอคติที่แอปได้รับจากกระบวนการฝึก ให้ใช้ฟังก์ชัน 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));
  4. สําหรับการดำเนินการแต่ละรายการในกราฟที่มีทิศทางที่คุณต้องการคํานวณ ให้เพิ่มการดำเนินการนั้นลงในโมเดลโดยเรียกใช้ฟังก์ชัน 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);
  5. ระบุตัวดำเนินการที่โมเดลควรถือว่าเป็นอินพุตและเอาต์พุตโดยเรียกใช้ฟังก์ชัน 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);
  6. คุณสามารถเลือกที่จะระบุว่า ANEURALNETWORKS_TENSOR_FLOAT32 ได้รับอนุญาตให้คำนวณด้วยช่วงหรือความแม่นยำต่ำเท่ากับรูปแบบจุดลอยตัว 16 บิต IEEE 754 ด้วยการเรียก ANeuralNetworksModel_relaxComputationFloat32toFloat16() หรือไม่

  7. โทรหา ANeuralNetworksModel_finish() เพื่อกําหนดค่าโมเดลให้เสร็จสมบูรณ์ หากไม่มีข้อผิดพลาด ฟังก์ชันนี้จะแสดงผลรหัสANEURALNETWORKS_NO_ERROR

    ANeuralNetworksModel_finish(model);

เมื่อสร้างโมเดลแล้ว คุณจะคอมไพล์โมเดลได้เท่าใดก็ได้และเรียกใช้การคอมไพล์แต่ละครั้งกี่ครั้งก็ได้

ควบคุมโฟลว์

หากต้องการรวมการไหลเวียนของการควบคุมในโมเดล NNAPI ให้ทําดังนี้

  1. สร้างกราฟย่อยการดําเนินการที่เกี่ยวข้อง (กราฟย่อย then และ else สำหรับคำสั่ง IF, กราฟย่อย condition และ body สำหรับลูป WHILE) เป็นโมเดล ANeuralNetworksModel* แบบสแตนด์อโลน ดังนี้

    ANeuralNetworksModel* thenModel = makeThenModel();
    ANeuralNetworksModel* elseModel = makeElseModel();
  2. สร้างโอเปอเรนด์ที่อ้างอิงโมเดลเหล่านั้นภายในโมเดลที่มีโฟลว์การควบคุม

    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);
  3. เพิ่มการดำเนินการขั้นตอนการควบคุมดังนี้

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

การรวบรวม

ขั้นตอนการคอมไพล์จะระบุโปรเซสเซอร์ที่จะเรียกใช้โมเดลของคุณและขอให้ไดรเวอร์ที่เกี่ยวข้องเตรียมพร้อมสำหรับการดำเนินการ ซึ่งอาจรวมถึงการสร้างโค้ดเครื่องสำหรับโปรเซสเซอร์ที่โมเดลจะทำงานด้วย

หากต้องการคอมไพล์โมเดล ให้ทำตามขั้นตอนต่อไปนี้

  1. เรียกใช้ฟังก์ชัน ANeuralNetworksCompilation_create() เพื่อสร้างอินสแตนซ์การคอมไพล์ใหม่

    // Compile the model
    ANeuralNetworksCompilation* compilation;
    ANeuralNetworksCompilation_create(model, &compilation);

    คุณใช้การกำหนดอุปกรณ์เพื่อเลือกอุปกรณ์ที่จะเรียกใช้อย่างชัดเจนได้ (ไม่บังคับ)

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

    // Set up compilation caching
    ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);

    ใช้ getCodeCacheDir() สําหรับ cacheDir token ที่ระบุต้องไม่ซ้ำกันสำหรับแต่ละโมเดลภายในแอปพลิเคชัน

  4. ทำให้คำจำกัดความการรวบรวมเสร็จสิ้นโดยการเรียกใช้ ANeuralNetworksCompilation_finish() หากไม่มีข้อผิดพลาด ฟังก์ชันนี้จะแสดงผลรหัสผลลัพธ์เป็น ANEURALNETWORKS_NO_ERROR

    ANeuralNetworksCompilation_finish(compilation);

การค้นหาและการกำหนดอุปกรณ์

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

การค้นหาอุปกรณ์

ใช้ ANeuralNetworks_getDeviceCount เพื่อดูจำนวนอุปกรณ์ที่ใช้ได้ สําหรับอุปกรณ์แต่ละเครื่อง ให้ใช้ ANeuralNetworks_getDevice เพื่อตั้งค่าอินสแตนซ์ ANeuralNetworksDevice เป็นข้อมูลอ้างอิงถึงอุปกรณ์นั้น

เมื่อคุณมีข้อมูลอ้างอิงอุปกรณ์แล้ว คุณสามารถดูข้อมูลเพิ่มเติมเกี่ยวกับอุปกรณ์นั้นโดยใช้ฟังก์ชันต่อไปนี้

การกำหนดอุปกรณ์

ใช้ 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 รายการที่แอปของคุณจัดสรรไว้

หากต้องการเรียกใช้โมเดลที่คอมไพล์แล้ว ให้ทําตามขั้นตอนต่อไปนี้

  1. เรียกใช้ฟังก์ชัน ANeuralNetworksExecution_create() เพื่อสร้างอินสแตนซ์การดำเนินการใหม่

    // Run the compiled model against a set of inputs
    ANeuralNetworksExecution* run1 = NULL;
    ANeuralNetworksExecution_create(compilation, &run1);
  2. ระบุตําแหน่งที่แอปอ่านค่าอินพุตสําหรับการคํานวณ แอปของคุณอ่านค่าอินพุตได้จากบัฟเฟอร์ผู้ใช้หรือพื้นที่หน่วยความจำที่จัดสรรโดยเรียกใช้ 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));
  3. ระบุตำแหน่งที่แอปของคุณเขียนค่าเอาต์พุต แอปสามารถเขียนค่าเอาต์พุตไปยังบัฟเฟอร์ผู้ใช้หรือพื้นที่หน่วยความจำที่จองไว้ได้โดยการเรียกใช้ ANeuralNetworksExecution_setOutput() หรือ ANeuralNetworksExecution_setOutputFromMemory() ตามลำดับ

    // Set the output
    float32 myOutput[3][4];
    ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
  4. กําหนดเวลาการเริ่มการดําเนินการโดยเรียกใช้ฟังก์ชัน ANeuralNetworksExecution_startCompute() หากไม่มีข้อผิดพลาด ฟังก์ชันนี้จะแสดงผลรหัสผลลัพธ์เป็น ANEURALNETWORKS_NO_ERROR

    // Starts the work. The work proceeds asynchronously
    ANeuralNetworksEvent* run1_end = NULL;
    ANeuralNetworksExecution_startCompute(run1, &run1_end);
  5. เรียกใช้ฟังก์ชัน 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);
  6. คุณสามารถใช้ชุดอินพุตอื่นกับโมเดลที่คอมไพล์แล้วได้หากต้องการ โดยการใช้อินสแตนซ์การคอมไพล์เดียวกันเพื่อสร้างANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecution

    // 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 บ่อยครั้งในฝั่งไคลเอ็นต์ ให้ใช้พูลหน่วยความจำที่ใช้ร่วมกันแทน

หากต้องการจัดสรรหน่วยความจำแบบทึบ ให้ทำตามขั้นตอนต่อไปนี้

  1. เรียกใช้ฟังก์ชัน ANeuralNetworksMemoryDesc_create() เพื่อสร้างตัวบ่งชี้หน่วยความจำใหม่

    // Create a memory descriptor
    ANeuralNetworksMemoryDesc* desc;
    ANeuralNetworksMemoryDesc_create(&desc);
  2. ระบุบทบาทอินพุตและเอาต์พุตที่ต้องการทั้งหมดโดยเรียกใช้ 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);
  3. (ไม่บังคับ) ระบุมิติข้อมูลหน่วยความจําโดยเรียกใช้ ANeuralNetworksMemoryDesc_setDimensions()

    // Specify the memory dimensions
    uint32_t dims[] = {3, 4};
    ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
  4. สรุปคําจํากัดความของคําอธิบายโดยเรียกใช้ ANeuralNetworksMemoryDesc_finish()

    ANeuralNetworksMemoryDesc_finish(desc);
  5. 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);
  6. ปล่อยตัวระบุหน่วยความจำเมื่อไม่ต้องการแล้ว

    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: รันไทม์ NNAPI
  • IPC: การสื่อสารระหว่างกระบวนการระหว่างรันไทม์ NNAPI กับโค้ดไดรเวอร์
  • Driver: กระบวนการของโปรแกรมเร่ง

สร้างข้อมูลการวิเคราะห์โปรไฟล์

สมมติว่าคุณตรวจสอบซอร์สทรี AOSP ที่ $ANDROID_BUILD_TOP และใช้ตัวอย่างการแยกประเภทรูปภาพด้วย TFLite เป็นแอปพลิเคชันเป้าหมาย คุณสามารถสร้างข้อมูลการโปรไฟล์ NNAPI ได้โดยทำตามขั้นตอนต่อไปนี้

  1. เริ่ม 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 สิ้นสุดการทำงานแบบอินเทอร์แอกทีฟ

  1. หลังจากเริ่มเครื่องมือรวบรวม Systrace แล้ว ให้เปิดแอปและทำการทดสอบประสิทธิภาพ

ในกรณีของเรา คุณสามารถเริ่มแอปการจัดประเภทรูปภาพจาก Android Studio หรือจาก UI ของโทรศัพท์ทดสอบโดยตรงหากติดตั้งแอปไว้แล้ว หากต้องการสร้างข้อมูล NNAPI บางส่วน คุณต้องกำหนดค่าแอปให้ใช้ NNAPI โดยเลือก NNAPI เป็นอุปกรณ์เป้าหมายในกล่องโต้ตอบการกำหนดค่าแอป

  1. เมื่อการทดสอบเสร็จสิ้นแล้ว ให้สิ้นสุดการติดตามระบบโดยกด enter ในเทอร์มินัลคอนโซลที่ทำงานมาตั้งแต่ขั้นตอนที่ 1

  2. เรียกใช้ยูทิลิตี 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 List recognizeImage(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()

กำหนดกำหนดเวลา

แอปพลิเคชันสามารถกำหนดกำหนดเวลาทั้งสำหรับการคอมไพล์และการอนุมานโมเดล

ข้อมูลเพิ่มเติมเกี่ยวกับโอเปอเรนด์

ส่วนต่อไปนี้ครอบคลุมหัวข้อขั้นสูงเกี่ยวกับการใช้ตัวถูกดำเนินการ

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_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 สำหรับโมเดลและชุดข้อมูลเดียวกัน

ในการใช้การเปรียบเทียบ ให้ทำดังนี้

  1. เชื่อมต่ออุปกรณ์ Android เป้าหมายกับคอมพิวเตอร์ เปิดหน้าต่างเทอร์มินัล และตรวจสอบว่าอุปกรณ์เข้าถึงได้ผ่าน adb

  2. หากมีอุปกรณ์ Android เชื่อมต่ออยู่มากกว่า 1 เครื่อง ให้ส่งออกตัวแปรสภาพแวดล้อมของอุปกรณ์เป้าหมายANDROID_SERIAL

  3. ไปที่ไดเรกทอรีแหล่งที่มาระดับบนสุดของ Android

  4. เรียกใช้คำสั่งต่อไปนี้

    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 ของ NNAPI
  • manager: ส่วนขยาย 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 เพื่อสร้างข้อมูลบันทึกแบบละเอียด