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 ไม่มีฟังก์ชันการทำงานสำหรับการเรียกใช้โมเดลในระบบคลาวด์

ดูตัวอย่างวิธีใช้ NNAPI ได้จากตัวอย่าง Android Neural Networks API

ทําความเข้าใจรันไทม์ Neural Networks API

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

รันไทม์ของเครือข่ายประสาทของ Android สามารถกระจายภาระงานการคำนวณไปยังโปรเซสเซอร์ในอุปกรณ์ที่มีอยู่ได้อย่างมีประสิทธิภาพ โดยอิงตามข้อกำหนดของแอปและความสามารถของฮาร์ดแวร์ในอุปกรณ์ Android ซึ่งรวมถึงฮาร์ดแวร์เครือข่ายประสาทเฉพาะ หน่วยประมวลผลกราฟิก (GPU) และโปรเซสเซอร์สัญญาณดิจิทัล (DSP)

สำหรับอุปกรณ์ 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 ขั้นตอนการเขียนโปรแกรมสําหรับ Android Neural Networks API

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

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

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

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

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

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

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

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

การทำงาน

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

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

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

คุณต้องเพิ่มออบเจ็กต์ที่การดำเนินการใช้หรือสร้างลงในโมเดลก่อนเพิ่มการดำเนินการ

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

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

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

ปัญหาที่ทราบใน API ระดับ 28: เมื่อส่ง 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 ที่ได้ตามปกติ ฟังก์ชันจะแสดงข้อผิดพลาดหากโมเดลที่ระบุมีการดำเนินการที่อุปกรณ์ที่เลือกไม่รองรับ

หากระบุอุปกรณ์หลายเครื่อง รันไทม์จะมีหน้าที่กระจายงานไปยังอุปกรณ์ต่างๆ

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

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

  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. คุณสามารถใช้ชุดอินพุตอื่นกับโมเดลที่คอมไพล์แล้วได้หากต้องการ โดยการใช้อินสแตนซ์การคอมไพล์เดียวกันเพื่อสร้างANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecutionANeuralNetworksExecution

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

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

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

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

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

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

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

สูตรคือ

(cellValue - zeroPoint) * scale

โดยที่ค่า zeroPoint เป็นจำนวนเต็ม 32 บิต และ scale เป็นค่าตัวเลขทศนิยม 32 บิต

เมื่อเทียบกับเทนเซอร์ของค่าทศนิยม 32 บิต เทนเซอร์ที่แปลงค่าเป็นจำนวนเต็ม 8 บิตมีข้อดี 2 ข้อดังนี้

  • แอปพลิเคชันของคุณจะมีขนาดเล็กลง เนื่องจากน้ำหนักที่ผ่านการฝึกมีขนาดเล็กกว่าเทนเซอร์ 32 บิตเพียง 1 ใน 4
  • การคํานวณมักจะทําได้เร็วขึ้น สาเหตุคือมีปริมาณข้อมูลที่จำเป็นต้องดึงมาจากหน่วยความจำน้อยลงและประสิทธิภาพของโปรเซสเซอร์ เช่น DSP ในการทําคณิตศาสตร์แบบจำนวนเต็ม

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

ใน NNAPI คุณจะกําหนดประเภท Tensor ที่แปลงค่าเป็นจำนวนเต็มได้โดยการตั้งค่าช่องประเภทของรูปแบบข้อมูล ANeuralNetworksOperandType เป็น ANEURALNETWORKS_TENSOR_QUANT8_ASYMM นอกจากนี้ คุณยังระบุค่าสเกลและ zeroPoint ของเทมพอร์ในโครงสร้างข้อมูลดังกล่าวได้ด้วย

นอกจากเทนเซอร์ที่แปลงให้เล็กลงแบบไม่สมมาตร 8 บิตแล้ว NNAPI ยังรองรับรายการต่อไปนี้ด้วย

ออบเจ็กต์ที่ไม่บังคับ

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

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

  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: การดำเนินการโดยใช้การติดตั้งใช้งาน NNAPI CPU
  • 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"

ปัจจุบันนิพจน์นี้จะแสดงแท็กต่อไปนี้

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