Android Neural Networks API (NNAPI), Android C API है. इसे Android डिवाइसों पर मशीन लर्निंग के लिए, कंप्यूटेशनल तौर पर ज़्यादा काम करने वाले ऑपरेशन चलाने के लिए डिज़ाइन किया गया है. NNAPI को, बेहतर लेवल के मशीन लर्निंग फ़्रेमवर्क के लिए, फ़ंक्शन की बुनियादी लेयर उपलब्ध कराने के मकसद से डिज़ाइन किया गया है. जैसे, TensorFlow Lite और Caffe2, जो न्यूरल नेटवर्क बनाते हैं और उन्हें ट्रेन करते हैं. यह एपीआई, Android 8.1 (एपीआई लेवल 27) या इसके बाद के वर्शन वाले सभी Android डिवाइसों पर उपलब्ध है.
NNAPI, Android डिवाइसों के डेटा को पहले से ट्रेन किए गए, डेवलपर के तय किए गए मॉडल पर लागू करके, अनुमान लगाने की सुविधा देता है. अनुमान के उदाहरणों में इमेज की कैटगरी तय करना, उपयोगकर्ता के व्यवहार का अनुमान लगाना, और खोज क्वेरी के लिए सही जवाब चुनना शामिल है.
डिवाइस पर अनुमान लगाने की सुविधा के कई फ़ायदे हैं:
- देरी: आपको इंटरनेट कनेक्शन के ज़रिए अनुरोध भेजने और जवाब का इंतज़ार करने की ज़रूरत नहीं है. उदाहरण के लिए, यह उन वीडियो ऐप्लिकेशन के लिए अहम हो सकता है जो कैमरे से लगातार आने वाले फ़्रेम को प्रोसेस करते हैं.
- उपलब्धता: ऐप्लिकेशन, नेटवर्क कवरेज से बाहर होने पर भी चलता है.
- स्पीड: न्यूरल नेटवर्क प्रोसेसिंग के लिए खास तौर पर बनाया गया नया हार्डवेयर, सामान्य सीपीयू की तुलना में काफ़ी तेज़ी से कैलकुलेशन करता है.
- निजता: यह डेटा, Android डिवाइस में ही सेव रहता है. इसे कहीं और शेयर नहीं किया जाता.
- लागत: जब सभी कैलकुलेशन Android डिवाइस पर किए जाते हैं, तो किसी सर्वर फ़ार्म की ज़रूरत नहीं होती.
डेवलपर को इन बातों का भी ध्यान रखना चाहिए:
- सिस्टम का इस्तेमाल: न्यूरल नेटवर्क का आकलन करने के लिए, बहुत सारे कैलकुलेशन किए जाते हैं. इससे बैटरी की खपत बढ़ सकती है. अगर यह समस्या आपके ऐप्लिकेशन से जुड़ी है, तो आपको बैटरी की परफ़ॉर्मेंस पर नज़र रखनी चाहिए. खास तौर पर, लंबे समय तक चलने वाले कंप्यूटेशन के मामले में.
- ऐप्लिकेशन का साइज़: अपने मॉडल के साइज़ पर ध्यान दें. मॉडल कई मेगाबाइट का स्पेस ले सकते हैं. अगर आपके APK में बड़े मॉडल को बंडल करने से, आपके उपयोगकर्ताओं पर बुरा असर पड़ता है, तो ऐप्लिकेशन इंस्टॉल होने के बाद मॉडल डाउनलोड करने, छोटे मॉडल इस्तेमाल करने या क्लाउड में कैलकुलेशन चलाने पर विचार करें. NNAPI, क्लाउड में मॉडल चलाने की सुविधा नहीं देता.
NNAPI का इस्तेमाल करने का एक उदाहरण देखने के लिए, Android Neural Networks API का सैंपल देखें.
Neural Networks API के रनटाइम के बारे में जानकारी
NNAPI को मशीन लर्निंग लाइब्रेरी, फ़्रेमवर्क, और टूल के ज़रिए कॉल किया जाता है. इनकी मदद से, डेवलपर अपने मॉडल को डिवाइस से बाहर ट्रेन कर सकते हैं और उन्हें Android डिवाइसों पर डिप्लॉय कर सकते हैं. आम तौर पर, ऐप्लिकेशन सीधे तौर पर NNAPI का इस्तेमाल नहीं करते. इसके बजाय, वे बेहतर लेवल के मशीन लर्निंग फ़्रेमवर्क का इस्तेमाल करते हैं. ये फ़्रेमवर्क, काम करने वाले डिवाइसों पर, हार्डवेयर से तेज़ी लाने वाले अनुमान लगाने के ऑपरेशन करने के लिए, NNAPI का इस्तेमाल कर सकते हैं.
Android डिवाइस पर मौजूद ऐप्लिकेशन की ज़रूरतों और हार्डवेयर की क्षमताओं के आधार पर, Android का न्यूरल नेटवर्क रनटाइम, डिवाइस पर मौजूद प्रोसेसर के बीच, कैलकुलेशन का वर्कलोड बेहतर तरीके से बांट सकता है. इनमें खास तौर पर, न्यूरल नेटवर्क हार्डवेयर, ग्राफ़िक प्रोसेसिंग यूनिट (GPU), और डिजिटल सिग्नल प्रोसेसर (DSP) शामिल हैं.
जिन Android डिवाइसों में खास वेंडर ड्राइवर नहीं होता है उनके लिए, NNAPI रनटाइम, सीपीयू पर अनुरोधों को लागू करता है.
पहली इमेज में, एनएनएपीआई का हाई-लेवल सिस्टम आर्किटेक्चर दिखाया गया है.
न्यूरल नेटवर्क एपीआई प्रोग्रामिंग मॉडल
एनएनएपीआई की मदद से कंप्यूटेशन (हिसाब लगाना) करने के लिए, आपको सबसे पहले एक डायरेक्ट ग्राफ़ बनाना होगा, जो कंप्यूटेशन की जानकारी देगा. आपके इनपुट डेटा (उदाहरण के लिए, किसी मशीन लर्निंग फ़्रेमवर्क से पास होने वाले वेट और बायस) के साथ मिलकर काम करने वाला यह कंप्यूटेशन ग्राफ़, एनएनएपीआई रनटाइम के आकलन का मॉडल बनाता है.
NNAPI, चार मुख्य एब्स्ट्रैक्शन का इस्तेमाल करता है:
- मॉडल: यह गणितीय ऑपरेशन और ट्रेनिंग प्रोसेस से सीखी गई स्थिर वैल्यू का कंप्यूटेशन ग्राफ़ होता है. ये ऑपरेशन, न्यूरल नेटवर्क के लिए खास तौर पर होते हैं. इनमें दो डाइमेंशन (2D) वाला कंवोल्यूशन, लॉजिस्टिक (सिग्मॉइड) ऐक्टिवेशन, रेक्टिफ़ाइड लीनियर (ReLU) ऐक्टिवेशन वगैरह शामिल हैं. मॉडल बनाना एक सिंक्रोनस कार्रवाई है.
टैग बनाने के बाद, उसका इस्तेमाल सभी थ्रेड और कलेक्शन में किया जा सकता है.
NNAPI में, मॉडल को
ANeuralNetworksModel
के तौर पर दिखाया जाता है. - कंपाइलेशन: यह NNAPI मॉडल को लोवर-लेवल कोड में कंपाइल करने के लिए कॉन्फ़िगरेशन दिखाता है. कंपाइलेशन एक सिंक्रोनस कार्रवाई है. इसे बनाने के बाद, इसे सभी थ्रेड और एक्सीक्यूशन में फिर से इस्तेमाल किया जा सकता है. NNAPI में, हर कलेक्शन को
ANeuralNetworksCompilation
के तौर पर दिखाया जाता है. - मेमोरी: शेयर की गई मेमोरी, मैप की गई मेमोरी की गई फ़ाइलों, और मिलते-जुलते मेमोरी बफ़र को दिखाता है. मेमोरी बफ़र का इस्तेमाल करने से, NNAPI रनटाइम, ड्राइवरों को डेटा को ज़्यादा बेहतर तरीके से ट्रांसफ़र कर पाता है. आम तौर पर, कोई ऐप्लिकेशन एक शेयर की गई मेमोरी बफ़र बनाता है. इसमें मॉडल को तय करने के लिए ज़रूरी हर टेंसर होता है. एक्ज़ीक्यूशन के किसी इंस्टेंस के इनपुट और आउटपुट सेव करने के लिए, मेमोरी बफ़र का इस्तेमाल भी किया जा सकता है. NNAPI में, हर मेमोरी बफ़र को
ANeuralNetworksMemory
इंस्टेंस के तौर पर दिखाया जाता है. लागू करना: इनपुट के सेट पर NNAPI मॉडल लागू करने और नतीजे इकट्ठा करने के लिए इंटरफ़ेस. एक्ज़ीक्यूशन सिंक्रोनस या एसिंक्रोनस तरीके से किया जा सकता है.
असाइनमेंट को एक साथ लागू करने के लिए, एक ही असाइनमेंट पर कई थ्रेड इंतज़ार कर सकती हैं. यह प्रोसेस पूरी होने के बाद, सभी थ्रेड रिलीज़ हो जाती हैं.
NNAPI में, हर एक्सीक्यूशन को
ANeuralNetworksExecution
के तौर पर दिखाया जाता है.
दूसरी इमेज में, प्रोग्रामिंग का बुनियादी फ़्लो दिखाया गया है.
इस सेक्शन के बाकी हिस्से में, NNAPI मॉडल को सेट अप करने का तरीका बताया गया है, ताकि कैलकुलेशन की जा सके, मॉडल को कॉम्पाइल किया जा सके, और कॉम्पाइल किए गए मॉडल को चलाया जा सके.
ट्रेनिंग डेटा का ऐक्सेस देना
ट्रेन किए गए वेट और बायस का डेटा, किसी फ़ाइल में सेव हो सकता है. NNAPI रनटाइम को इस डेटा का बेहतर तरीके से ऐक्सेस देने के लिए, ANeuralNetworksMemory_createFromFd()
फ़ंक्शन को कॉल करके ANeuralNetworksMemory
इंस्टेंस बनाएं और खोली गई डेटा फ़ाइल का फ़ाइल डिस्क्रिप्टर पास करें. साथ ही, आपको मेमोरी सुरक्षा फ़्लैग और फ़ाइल में शेयर की गई मेमोरी का वह हिस्सा भी बताना होगा जहां से मेमोरी का शेयर किया गया हिस्सा शुरू होता है.
// 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 accelerator, AHardwareBuffer
ऑब्जेक्ट को ऐक्सेस कर सकता है. AHardwareBuffer
के कई अलग-अलग कॉन्फ़िगरेशन होते हैं. ऐसा हो सकता है कि हर NNAPI ऐक्सेलरेटर, इन सभी कॉन्फ़िगरेशन के साथ काम न करे. इस सीमा की वजह से, ANeuralNetworksMemory_createFromAHardwareBuffer
रेफ़रंस दस्तावेज़ में बताई गई पाबंदियों को देखें. साथ ही, टारगेट डिवाइसों पर पहले से जांच करें, ताकि यह पक्का किया जा सके कि AHardwareBuffer
का इस्तेमाल करने वाले कंपाइलेशन और एक्सीक्यूशन, उम्मीद के मुताबिक काम करें. इसके लिए, ऐक्सेलरेटर की जानकारी देने के लिए, डिवाइस असाइनमेंट का इस्तेमाल करें.
NNAPI रनटाइम को AHardwareBuffer
ऑब्जेक्ट ऐक्सेस करने की अनुमति देने के लिए, ANeuralNetworksMemory_createFromAHardwareBuffer
फ़ंक्शन को कॉल करके और AHardwareBuffer
ऑब्जेक्ट को पास करके, ANeuralNetworksMemory
इंस्टेंस बनाएं. इसका उदाहरण नीचे दिए गए कोड सैंपल में दिया गया है:
// 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 में कंप्यूटेशन की बुनियादी इकाई है. हर मॉडल को एक या उससे ज़्यादा ऑपरेंड और ऑपरेशन के आधार पर तय किया जाता है.
ओपेरैंड्स
ओपेरैंड, ऐसे डेटा ऑब्जेक्ट होते हैं जिनका इस्तेमाल ग्राफ़ को परिभाषित करने के लिए किया जाता है. इनमें मॉडल के इनपुट और आउटपुट, इंटरमीडिएट नोड, और इन ऑपरेशन में इस्तेमाल होने वाले कॉन्स्टेंट शामिल होते हैं. इंटरमीडिएट नोड में, एक ऑपरेशन से दूसरे ऑपरेशन में जाने वाला डेटा होता है.
NNAPI मॉडल में दो तरह के ऑपरेंड जोड़े जा सकते हैं: स्केलर और टेंसर.
स्केलर, एक वैल्यू दिखाता है. NNAPI, स्केलर वैल्यू को बूलियन, 16-बिट फ़्लोटिंग पॉइंट, 32-बिट फ़्लोटिंग पॉइंट, 32-बिट पूर्णांक, और बिना साइन वाले 32-बिट पूर्णांक फ़ॉर्मैट में इस्तेमाल करता है.
NNAPI में ज़्यादातर ऑपरेशन में टेंसर शामिल होते हैं. टेंसर, n-डाइमेंशन वाले ऐरे होते हैं. NNAPI, 16-बिट फ़्लोटिंग पॉइंट, 32-बिट फ़्लोटिंग पॉइंट, 8-बिट क्वंटाइज़ की गई, 16-बिट क्वंटाइज़ की गई, 32-बिट पूर्णांक, और 8-बिट बुलियन वैल्यू वाले टेंसर के साथ काम करता है.
उदाहरण के लिए, तीसरे चित्र में दो ऑपरेशन वाला मॉडल दिखाया गया है: पहले जोड़ और फिर गुणा. यह मॉडल, इनपुट टेंसर लेता है और एक आउटपुट टेंसर जनरेट करता है.
ऊपर दिए गए मॉडल में सात ऑपरेंड हैं. इन ऑपरेंड की पहचान, मॉडल में जोड़े जाने के क्रम के इंडेक्स से होती है. जोड़े गए पहले ऑपरेंड का इंडेक्स 0 होता है, दूसरे का इंडेक्स 1 होता है और इसी तरह बाकी के इंडेक्स होते हैं. ऑपरेंड 1, 2, 3, और 5, कॉन्स्टेंट ऑपरेंड हैं.
ऑपरेंड को जोड़ने के क्रम से कोई फ़र्क़ नहीं पड़ता. उदाहरण के लिए, मॉडल के आउटपुट ऑपरेंड को पहले जोड़ा जा सकता है. ऑपरेंड का रेफ़रंस देते समय, सही इंडेक्स वैल्यू का इस्तेमाल करना ज़रूरी है.
ऑपरेंड के टाइप होते हैं. इनकी जानकारी, मॉडल में जोड़ने के दौरान दी जाती है.
ऑपरेंड का इस्तेमाल, मॉडल के इनपुट और आउटपुट, दोनों के तौर पर नहीं किया जा सकता.
हर ऑपरेंड, मॉडल इनपुट, कॉन्सटेंट या ठीक एक ऑपरेशन का आउटपुट ऑपरेंड होना चाहिए.
ऑपरेंड का इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए, ऑपरेंड के बारे में ज़्यादा जानकारी देखें.
ऑपरेशंस
ऑपरेशन से पता चलता है कि कौनसे कैलकुलेशन करने हैं. हर ऑपरेशन में ये एलिमेंट होते हैं:
- ऑपरेशन का टाइप (उदाहरण के लिए, जोड़ना, गुणा करना, कन्वोल्यूशन),
- उन ऑपरेंड के इंडेक्स की सूची जिनका इस्तेमाल ऑपरेशन, इनपुट के लिए करता है, और
- ऑपरेंड के इंडेक्स की सूची, जिसका इस्तेमाल ऑपरेशन, आउटपुट के लिए करता है.
इन सूचियों में ऑर्डर का फ़र्क़ पड़ता है. हर तरह के ऑपरेशन के अनुमानित इनपुट और आउटपुट के लिए, NNAPI API का रेफ़रंस देखें.
किसी कार्रवाई को जोड़ने से पहले आपको मॉडल में वे ऑपरेंड जोड़ने होंगे जो मॉडल में इस्तेमाल होते हैं या बनाए जाते हैं.
ऑपरेशन जोड़ने के क्रम से कोई फ़र्क़ नहीं पड़ता. एनएनएपीआई, ऑपरेंड और ऑपरेशन के कंप्यूटेशन ग्राफ़ की मदद से तय की गई डिपेंडेंसी का इस्तेमाल करके, यह तय करता है कि कार्रवाइयां किस क्रम में की जाएं.
NNAPI के साथ काम करने वाली कार्रवाइयों की खास जानकारी नीचे टेबल में दी गई है:
एपीआई लेवल 28 में एक समस्या: Android 9 (एपीआई लेवल 28) और उसके बाद के वर्शन पर उपलब्ध ANEURALNETWORKS_PAD
ऑपरेशन में ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
टेन्सर पास करने पर, हो सकता है कि NNAPI का आउटपुट, TensorFlow Lite जैसे बेहतर मशीन लर्निंग फ़्रेमवर्क के आउटपुट से मेल न खाए. इसके बजाय, आपको सिर्फ़
ANEURALNETWORKS_TENSOR_FLOAT32
को पास करना चाहिए.
यह समस्या, Android 10 (एपीआई लेवल 29) और उसके बाद के वर्शन में हल हो गई है.
मॉडल बनाना
नीचे दिए गए उदाहरण में, हम तीसरे चित्र में मौजूद दो ऑपरेशन वाला मॉडल बनाते हैं.
मॉडल बनाने के लिए, यह तरीका अपनाएं:
खाली मॉडल तय करने के लिए,
ANeuralNetworksModel_create()
फ़ंक्शन को कॉल करें.ANeuralNetworksModel* model = NULL; ANeuralNetworksModel_create(&model);
ANeuralNetworks_addOperand()
को कॉल करके, अपने मॉडल में ऑपरेंड जोड़ें. उनका डेटा किस तरह का है, यहANeuralNetworksOperandType
डेटा स्ट्रक्चर का इस्तेमाल करके बताया जाता है.// In our example, all our tensors are matrices of dimension [3][4] ANeuralNetworksOperandType tensor3x4Type; tensor3x4Type.type = ANEURALNETWORKS_TENSOR_FLOAT32; tensor3x4Type.scale = 0.f; // These fields are used for quantized tensors tensor3x4Type.zeroPoint = 0; // These fields are used for quantized tensors tensor3x4Type.dimensionCount = 2; uint32_t dims[2] = {3, 4}; tensor3x4Type.dimensions = dims;
// We also specify operands that are activation function specifiers ANeuralNetworksOperandType activationType; activationType.type = ANEURALNETWORKS_INT32; activationType.scale = 0.f; activationType.zeroPoint = 0; activationType.dimensionCount = 0; activationType.dimensions = NULL;
// Now we add the seven operands, in the same order defined in the diagram ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 0 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 1 ANeuralNetworksModel_addOperand(model, &activationType); // operand 2 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 3 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 4 ANeuralNetworksModel_addOperand(model, &activationType); // operand 5 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 6जिन ऑपरेंड की वैल्यू एक जैसी होती है उनके लिए,
ANeuralNetworksModel_setOperandValue()
औरANeuralNetworksModel_setOperandValueFromMemory()
फ़ंक्शन का इस्तेमाल करें. जैसे, वेट और बायस, जो आपके ऐप्लिकेशन को ट्रेनिंग प्रोसेस से मिलते हैं.नीचे दिए गए उदाहरण में, हमने ट्रेनिंग डेटा का ऐक्सेस दें में बनाए गए मेमोरी बफ़र से जुड़ी ट्रेनिंग डेटा फ़ाइल की लगातार वैल्यू सेट की हैं.
// In our example, operands 1 and 3 are constant tensors whose values were // established during the training process const int sizeOfTensor = 3 * 4 * 4; // The formula for size calculation is dim0 * dim1 * elementSize ANeuralNetworksModel_setOperandValueFromMemory(model, 1, mem1, 0, sizeOfTensor); ANeuralNetworksModel_setOperandValueFromMemory(model, 3, mem1, sizeOfTensor, sizeOfTensor);
// We set the values of the activation operands, in our example operands 2 and 5 int32_t noneValue = ANEURALNETWORKS_FUSED_NONE; ANeuralNetworksModel_setOperandValue(model, 2, &noneValue, sizeof(noneValue)); ANeuralNetworksModel_setOperandValue(model, 5, &noneValue, sizeof(noneValue));डायरेक्टेड ग्राफ़ में हर उस ऑपरेशन के लिए जिसका आपको हिसाब लगाना है,
ANeuralNetworksModel_addOperation()
फ़ंक्शन को कॉल करके, अपने मॉडल में ऑपरेशन जोड़ें.इस कॉल के पैरामीटर के तौर पर, आपके ऐप्लिकेशन को ये चीज़ें उपलब्ध करानी होंगी:
- कार्रवाई का टाइप
- इनपुट वैल्यू की संख्या
- इनपुट ऑपरेंड के इंडेक्स का कलेक्शन
- आउटपुट वैल्यू की संख्या
- आउटपुट ऑपरेंड के इंडेक्स का कलेक्शन
ध्यान दें कि किसी ऑपरेंड का इस्तेमाल, एक ही ऑपरेशन के इनपुट और आउटपुट, दोनों के लिए नहीं किया जा सकता.
// We have two operations in our example // The first consumes operands 1, 0, 2, and produces operand 4 uint32_t addInputIndexes[3] = {1, 0, 2}; uint32_t addOutputIndexes[1] = {4}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, addInputIndexes, 1, addOutputIndexes);
// The second consumes operands 3, 4, 5, and produces operand 6 uint32_t multInputIndexes[3] = {3, 4, 5}; uint32_t multOutputIndexes[1] = {6}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_MUL, 3, multInputIndexes, 1, multOutputIndexes);ANeuralNetworksModel_identifyInputsAndOutputs()
फ़ंक्शन को कॉल करके, यह पता लगाएं कि मॉडल को किन ऑपरेंड को इनपुट और आउटपुट के तौर पर इस्तेमाल करना चाहिए.// Our model has one input (0) and one output (6) uint32_t modelInputIndexes[1] = {0}; uint32_t modelOutputIndexes[1] = {6}; ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, modelInputIndexes, 1 modelOutputIndexes);
इसके अलावा,
ANeuralNetworksModel_relaxComputationFloat32toFloat16()
को कॉल करके यह भी तय किया जा सकता है किANEURALNETWORKS_TENSOR_FLOAT32
की गिनती, रेंज या सटीक वैल्यू के साथ की जाए या नहीं. हालांकि, यह ज़रूरी नहीं है.अपने मॉडल की परिभाषा को फ़ाइनल करने के लिए,
ANeuralNetworksModel_finish()
पर कॉल करें. अगर कोई गड़बड़ी नहीं है, तो यह फ़ंक्शनANEURALNETWORKS_NO_ERROR
वाला नतीजा कोड दिखाता है.ANeuralNetworksModel_finish(model);
मॉडल बनाने के बाद, उसे जितनी बार चाहे उतनी बार कंपाइल किया जा सकता है. साथ ही, हर कंपाइलेशन को जितनी बार चाहे उतनी बार चलाया जा सकता है.
कंट्रोल फ़्लो
NNAPI मॉडल में कंट्रोल फ़्लो शामिल करने के लिए, यह तरीका अपनाएं:
इसके एक्ज़ीक्यूशन सबग्राफ़ (
IF
स्टेटमेंट के लिएthen
औरelse
सबग्राफ़,WHILE
लूप के लिएcondition
औरbody
सबग्राफ़) को स्टैंडअलोनANeuralNetworksModel*
मॉडल के तौर पर बनाएं:ANeuralNetworksModel* thenModel = makeThenModel(); ANeuralNetworksModel* elseModel = makeElseModel();
ऐसे ऑपरेंड बनाएं जो कंट्रोल फ़्लो वाले मॉडल में उन मॉडल का रेफ़रंस देते हों:
ANeuralNetworksOperandType modelType = { .type = ANEURALNETWORKS_MODEL, }; ANeuralNetworksModel_addOperand(model, &modelType); // kThenOperandIndex ANeuralNetworksModel_addOperand(model, &modelType); // kElseOperandIndex ANeuralNetworksModel_setOperandValueFromModel(model, kThenOperandIndex, &thenModel); ANeuralNetworksModel_setOperandValueFromModel(model, kElseOperandIndex, &elseModel);
कंट्रोल फ़्लो ऑपरेशन जोड़ें:
uint32_t inputs[] = {kConditionOperandIndex, kThenOperandIndex, kElseOperandIndex, kInput1, kInput2, kInput3}; uint32_t outputs[] = {kOutput1, kOutput2}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_IF, std::size(inputs), inputs, std::size(output), outputs);
कंपाइलेशन
कंपाइल करने के चरण से यह तय होता है कि आपका मॉडल किन प्रोसेसर पर चलाया जाएगा और उनसे इसके लिए तैयार होने के लिए कहा जाता है. इसमें, उन प्रोसेसर के हिसाब से खास तौर पर मशीन कोड जनरेट करना शामिल हो सकता है जिन पर आपका मॉडल चलेगा.
मॉडल को कंपाइल करने के लिए, यह तरीका अपनाएं:
नया कंपाइलेशन इंस्टेंस बनाने के लिए,
ANeuralNetworksCompilation_create()
फ़ंक्शन को कॉल करें.// Compile the model ANeuralNetworksCompilation* compilation; ANeuralNetworksCompilation_create(model, &compilation);
इसके अलावा, डिवाइस असाइनमेंट का इस्तेमाल करके, यह भी चुना जा सकता है कि किस डिवाइस पर टास्क लागू करना है.
आपके पास यह तय करने का विकल्प होता है कि रनटाइम, बैटरी की खपत और प्रोग्राम को लागू करने की स्पीड के बीच किस तरह का समझौता करे. इसके लिए,
ANeuralNetworksCompilation_setPreference()
पर कॉल करें.// Ask to optimize for low power consumption ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);
आपके पास ये प्राथमिकताएं तय करने का विकल्प होता है:
ANEURALNETWORKS_PREFER_LOW_POWER
: ऐसे तरीके से प्रोसेस को पूरा करें जिससे बैटरी कम से कम खर्च हो. यह उन कंपाइलेशन के लिए ज़रूरी है जिन्हें अक्सर चलाया जाता है.ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER
: ज्यादा से ज़्यादा तेज़ी से एक जवाब दें. भले ही, इससे ज़्यादा बिजली खर्च हो. यह डिफ़ॉल्ट विकल्प है.ANEURALNETWORKS_PREFER_SUSTAINED_SPEED
: एक के बाद एक फ़्रेम के थ्रूपुट को बढ़ाने को प्राथमिकता दें, जैसे कि कैमरे से लगातार आने वाले फ़्रेम को प्रोसेस करते समय.
आपके पास
ANeuralNetworksCompilation_setCaching
को कॉल करके, कंपाइलेशन कैश मेमोरी सेट अप करने का विकल्प है.// Set up compilation caching ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);
cacheDir
के लिए,getCodeCacheDir()
का इस्तेमाल करें. ऐप्लिकेशन में मौजूद हर मॉडल के लिए,token
यूनीक होना चाहिए.ANeuralNetworksCompilation_finish()
को कॉल करके, कंपाइलेशन डेफ़िनिशन को फ़ाइनल करें. अगर कोई गड़बड़ी नहीं है, तो यह फ़ंक्शनANEURALNETWORKS_NO_ERROR
वाला नतीजा कोड दिखाता है.ANeuralNetworksCompilation_finish(compilation);
डिवाइस खोजने और असाइन करने की सुविधा
Android 10 (एपीआई लेवल 29) और उसके बाद के वर्शन वाले Android डिवाइसों पर, NNAPI ऐसे फ़ंक्शन उपलब्ध कराता है जिनकी मदद से मशीन लर्निंग फ़्रेमवर्क लाइब्रेरी और ऐप्लिकेशन, उपलब्ध डिवाइसों के बारे में जानकारी पा सकते हैं. साथ ही, ये फ़ंक्शन यह भी तय कर सकते हैं कि कौनसे डिवाइसों पर ऐप्लिकेशन चलाया जाए. उपलब्ध डिवाइसों के बारे में जानकारी देने से, ऐप्लिकेशन को किसी डिवाइस पर मौजूद ड्राइवर का सटीक वर्शन मिल जाता है. इससे, ऐप्लिकेशन के काम न करने की समस्या से बचा जा सकता है. ऐप्लिकेशन को यह तय करने की सुविधा देकर कि किन डिवाइसों पर मॉडल के अलग-अलग सेक्शन को चलाना है, ऐप्लिकेशन को उस Android डिवाइस के लिए ऑप्टिमाइज़ किया जा सकता है जिस पर उन्हें डिप्लॉय किया गया है.
कास्ट की सुविधा वाले डिवाइस खोजना
उपलब्ध डिवाइसों की संख्या जानने के लिए, ANeuralNetworks_getDeviceCount
का इस्तेमाल करें. हर डिवाइस के लिए, ANeuralNetworksDevice
इंस्टेंस को उस डिवाइस के रेफ़रंस पर सेट करने के लिए, ANeuralNetworks_getDevice
का इस्तेमाल करें.
डिवाइस का रेफ़रंस मिलने के बाद, इन फ़ंक्शन का इस्तेमाल करके उस डिवाइस के बारे में ज़्यादा जानकारी पाई जा सकती है:
ANeuralNetworksDevice_getFeatureLevel
ANeuralNetworksDevice_getName
ANeuralNetworksDevice_getType
ANeuralNetworksDevice_getVersion
डिवाइस असाइनमेंट
ANeuralNetworksModel_getSupportedOperationsForDevices
का इस्तेमाल करके, यह पता लगाएं कि किसी मॉडल के कौनसे ऑपरेशन, खास डिवाइसों पर चलाए जा सकते हैं.
यह कंट्रोल करने के लिए कि प्रोग्राम चलाने के लिए कौनसे एक्सेलरेटर का इस्तेमाल किया जाए, ANeuralNetworksCompilation_create
की जगह ANeuralNetworksCompilation_createForDevices
को कॉल करें.
इसके बाद, ANeuralNetworksCompilation
ऑब्जेक्ट का सामान्य तरीके से इस्तेमाल करें.
अगर दिए गए मॉडल में ऐसे ऑपरेशन हैं जो चुने गए डिवाइसों पर काम नहीं करते हैं, तो फ़ंक्शन गड़बड़ी वाला मैसेज दिखाता है.
अगर एक से ज़्यादा डिवाइसों के बारे में बताया गया है, तो रनटाइम का ज़िम्मेदारी उन डिवाइसों पर काम को डिस्ट्रिब्यूट करना है.
अन्य डिवाइसों की तरह ही, NNAPI सीपीयू को लागू करने के लिए, nnapi-reference
नाम और ANEURALNETWORKS_DEVICE_TYPE_CPU
टाइप वाले ANeuralNetworksDevice
का इस्तेमाल किया जाता है. ANeuralNetworksCompilation_createForDevices
को कॉल करते समय, मॉडल को कंपाइल और लागू करने में होने वाली गड़बड़ियों को ठीक करने के लिए, सीपीयू लागू करने का इस्तेमाल नहीं किया जाता.
किसी मॉडल को ऐसे सब-मॉडल में बांटना, ऐप्लिकेशन की ज़िम्मेदारी है जो तय किए गए डिवाइसों पर चल सकते हैं. जिन ऐप्लिकेशन को मैन्युअल रूप से डिवाइस के स्टोरेज को अलग-अलग हिस्सों में बांटने की ज़रूरत नहीं होती उन्हें आसान ANeuralNetworksCompilation_create
को कॉल करना जारी रखना चाहिए. इससे, मॉडल को तेज़ करने के लिए, सभी उपलब्ध डिवाइसों (सीपीयू के साथ) का इस्तेमाल किया जा सकता है. अगर ANeuralNetworksCompilation_createForDevices
का इस्तेमाल करके बताए गए डिवाइसों पर, मॉडल पूरी तरह से काम नहीं करता है, तो ANEURALNETWORKS_BAD_DATA
दिखाया जाता है.
मॉडल का बंटवारा
जब मॉडल के लिए एक से ज़्यादा डिवाइस उपलब्ध होते हैं, तो NNAPI रनटाइम, काम को सभी डिवाइसों पर बांट देता है. उदाहरण के लिए, अगर ANeuralNetworksCompilation_createForDevices
को एक से ज़्यादा डिवाइस दिए गए थे, तो काम का बंटवारा करते समय, बताए गए सभी डिवाइस को ध्यान में रखा जाएगा. ध्यान दें कि अगर सीपीयू डिवाइस सूची में नहीं है, तो सीपीयू एक्सीक्यूशन बंद हो जाएगा. ANeuralNetworksCompilation_create
का इस्तेमाल करते समय, सभी उपलब्ध डिवाइसों को ध्यान में रखा जाएगा. इनमें सीपीयू भी शामिल है.
डिस्ट्रिब्यूशन, उपलब्ध डिवाइसों की सूची से चुने गए डिवाइसों के आधार पर किया जाता है. यह डिवाइस, मॉडल में मौजूद हर ऑपरेशन के लिए चुना जाता है. साथ ही, सबसे अच्छी परफ़ॉर्मेंस का एलान किया जाता है. इसका मतलब है कि ऑपरेशन को पूरा करने में लगने वाला कम से कम समय या कम से कम बिजली की खपत. यह क्लाइंट की ओर से तय की गई, ऑपरेशन को पूरा करने की प्राथमिकता के आधार पर तय होता है. यह पार्टीशनिंग एल्गोरिदम, अलग-अलग प्रोसेसर के बीच आईओ की वजह से होने वाली संभावित गड़बड़ियों को ध्यान में नहीं रखता. इसलिए, कई प्रोसेसर (ANeuralNetworksCompilation_createForDevices
का इस्तेमाल करके साफ़ तौर पर या ANeuralNetworksCompilation_create
का इस्तेमाल करके गुप्त रूप से) तय करते समय, उससे बने ऐप्लिकेशन की प्रोफ़ाइल बनाना ज़रूरी है.
यह समझने के लिए कि NNAPI ने आपके मॉडल को कैसे बांटा है, मैसेज के लिए Android लॉग देखें (ExecutionPlan
टैग के साथ INFO लेवल पर):
ModelBuilder::findBestDeviceForEachOperation(op-name): device-index
op-name
, ग्राफ़ में ऑपरेशन का ब्यौरा देने वाला नाम है और
device-index
, डिवाइसों की सूची में चुने गए डिवाइस का इंडेक्स है.
यह सूची ANeuralNetworksCompilation_createForDevices
को दिया गया इनपुट है. अगर ANeuralNetworksCompilation_createForDevices
का इस्तेमाल किया जा रहा है, तो ANeuralNetworks_getDeviceCount
और ANeuralNetworks_getDevice
का इस्तेमाल करके सभी डिवाइसों पर बार-बार इस्तेमाल किए जाने पर, डिवाइसों की सूची दिखाई जाती है.
मैसेज (ExecutionPlan
टैग के साथ जानकारी के स्तर पर):
ModelBuilder::partitionTheWork: only one best device: device-name
इस मैसेज से पता चलता है कि डिवाइस device-name
पर पूरे ग्राफ़ को तेज़ किया गया है.
प्लान लागू करना
लागू करने का चरण, इनपुट के सेट पर मॉडल लागू करता है और कैलकुलेशन के आउटपुट को एक या एक से ज़्यादा उपयोगकर्ता बफ़र या मेमोरी स्पेस में सेव करता है. ये स्पेस आपके ऐप्लिकेशन ने ऐलोकेट किए होते हैं.
कंपाइल किए गए मॉडल को चलाने के लिए, यह तरीका अपनाएं:
नया एक्सीक्यूशन इंस्टेंस बनाने के लिए,
ANeuralNetworksExecution_create()
फ़ंक्शन को कॉल करें.// Run the compiled model against a set of inputs ANeuralNetworksExecution* run1 = NULL; ANeuralNetworksExecution_create(compilation, &run1);
बताएं कि आपका ऐप्लिकेशन, कैलकुलेशन के लिए इनपुट वैल्यू कहां से पढ़ता है. आपका ऐप्लिकेशन, उपयोगकर्ता बफ़र या ऐलोकेट किए गए मेमोरी स्पेस से इनपुट वैल्यू पढ़ सकता है. इसके लिए, उसे
ANeuralNetworksExecution_setInput()
याANeuralNetworksExecution_setInputFromMemory()
को कॉल करना होगा.// Set the single input to our sample model. Since it is small, we won't use a memory buffer float32 myInput[3][4] = { ...the data... }; ANeuralNetworksExecution_setInput(run1, 0, NULL, myInput, sizeof(myInput));
बताएं कि आपका ऐप्लिकेशन आउटपुट वैल्यू कहां लिखता है. आपका ऐप्लिकेशन, आउटपुट वैल्यू को उपयोगकर्ता बफ़र या ऐलोकेट किए गए मेमोरी स्पेस में लिख सकता है. इसके लिए,
ANeuralNetworksExecution_setOutput()
याANeuralNetworksExecution_setOutputFromMemory()
को कॉल करना होगा.// Set the output float32 myOutput[3][4]; ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
ANeuralNetworksExecution_startCompute()
फ़ंक्शन को कॉल करके, प्रोसेस शुरू होने का समय शेड्यूल करें. अगर कोई गड़बड़ी नहीं है, तो यह फ़ंक्शनANEURALNETWORKS_NO_ERROR
वाला नतीजा कोड दिखाता है.// Starts the work. The work proceeds asynchronously ANeuralNetworksEvent* run1_end = NULL; ANeuralNetworksExecution_startCompute(run1, &run1_end);
एक्ज़ीक्यूशन पूरा होने का इंतज़ार करने के लिए,
ANeuralNetworksEvent_wait()
फ़ंक्शन को कॉल करें. अगर स्क्रिप्ट लागू हो जाती है, तो यह फ़ंक्शनANEURALNETWORKS_NO_ERROR
का नतीजा कोड दिखाता है. इंतज़ार करने की कार्रवाई, उस थ्रेड से अलग थ्रेड पर की जा सकती है जिससे एक्ज़ीक्यूशन शुरू किया गया है.// For our example, we have no other work to do and will just wait for the completion ANeuralNetworksEvent_wait(run1_end); ANeuralNetworksEvent_free(run1_end); ANeuralNetworksExecution_free(run1);
इसके अलावा, इकट्ठा किए गए मॉडल में इनपुट का कोई दूसरा सेट लागू किया जा सकता है. इसके लिए, इकट्ठा करने के उसी इंस्टेंस का इस्तेमाल करके, नया
ANeuralNetworksExecution
इंस्टेंस बनाएं.// 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 माइक्रोसेकंड तक लग सकते हैं.
इंतज़ार का समय कम करने के लिए, किसी ऐप्लिकेशन को रनटाइम पर सिंक किए गए अनुमान के कॉल करने के लिए निर्देश दिया जा सकता है. यह कॉल, अनुमान शुरू होने के बाद नहीं, बल्कि अनुमान पूरा होने के बाद ही वापस आएगा. ऐप्लिकेशन, रनटाइम पर एसिंक्रोनस अनुमान कॉल के लिए, ANeuralNetworksExecution_startCompute
को कॉल करने के बजाय, रनटाइम पर सिंक्रोनस कॉल करने के लिए, ANeuralNetworksExecution_compute
को कॉल करता है. ANeuralNetworksExecution_compute
को कॉल करने पर, ANeuralNetworksEvent
नहीं दिखता और इसे ANeuralNetworksEvent_wait
को कॉल करने के साथ जोड़ा नहीं जाता.
बर्स्ट निष्पादन
NNAPI, Android 10 (एपीआई लेवल 29) और इसके बाद के वर्शन वाले Android डिवाइसों पर, 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_burstCompute
के कॉल में, अलग-अलग ANeuralNetworksExecution
ऑब्जेक्ट को एक ही ANeuralNetworksBurst
के साथ जोड़ा जाता है.
// 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()
के ज़रिए, एक साथ कई टास्क को शेड्यूल करने के लिए एक और तरीके का इस्तेमाल करता है. इस तरीके का इस्तेमाल करने पर, आकलन शुरू करने से पहले, एक्ज़ीक्यूशन उन सभी इवेंट के सिग्नल मिलने का इंतज़ार करता है जिन पर यह निर्भर करता है. एक्सीक्यूशन पूरा होने और आउटपुट इस्तेमाल के लिए तैयार होने के बाद, रिटर्न किए गए इवेंट को सिग्नल दिया जाता है.
इवेंट का बैक अप, सिंक फ़ेंस की मदद से हो सकता है. यह इस बात पर निर्भर करता है कि एक्ज़ीक्यूशन को कौनसे डिवाइस मैनेज करते हैं. इवेंट के पूरा होने का इंतज़ार करने और एक्ज़ीक्यूशन के लिए इस्तेमाल किए गए संसाधनों को वापस पाने के लिए, आपको 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);
गड़बड़ी को मैनेज करना और सीपीयू फ़ॉलबैक
अगर पार्टिशन करने के दौरान कोई गड़बड़ी होती है, कोई ड्राइवर किसी मॉडल को (उसके किसी हिस्से को) कंपाइल नहीं कर पाता है या कोई ड्राइवर कंपाइल किए गए (उसके किसी हिस्से को) मॉडल को लागू नहीं कर पाता है, तो NNAPI एक या उससे ज़्यादा ऑपरेशन के लिए, सीपीयू पर काम कर सकता है.
अगर NNAPI क्लाइंट में ऑपरेशन के ऑप्टिमाइज़ किए गए वर्शन शामिल हैं (जैसे, TFLite), तो सीपीयू फ़ॉलबैक को बंद करना और क्लाइंट के ऑप्टिमाइज़ किए गए ऑपरेशन को लागू करने में हुई गड़बड़ियों को ठीक करना फ़ायदेमंद हो सकता है.
Android 10 में, अगर ANeuralNetworksCompilation_createForDevices
का इस्तेमाल करके कंपाइलेशन किया जाता है, तो सीपीयू फ़ॉलबैक बंद कर दिया जाएगा.
Android P में, अगर ड्राइवर पर NNAPI को लागू करने में समस्या आती है, तो इसे सीपीयू पर लागू किया जाता है.
यह Android 10 पर भी लागू होता है, जब ANeuralNetworksCompilation_createForDevices
के बजाय ANeuralNetworksCompilation_create
का इस्तेमाल किया जाता है.
पहला एक्ज़ीक्यूशन उसी एक पार्टीशन पर वापस चला जाता है और अगर वह अब भी फ़ेल हो जाता है, तो वह पूरे मॉडल को सीपीयू पर फिर से आज़माता है.
अगर पार्टिशनिंग या कंपाइलेशन पूरा नहीं हो पाता है, तो पूरे मॉडल को सीपीयू पर चलाने की कोशिश की जाएगी.
कुछ मामलों में, सीपीयू पर कुछ कार्रवाइयां काम नहीं करतीं. ऐसे में, फ़ॉलबैक के बजाय, कंपाइलेशन या एक्सीक्यूशन पूरा नहीं हो पाएगा.
सीपीयू फ़ॉलबैक की सुविधा बंद करने के बाद भी, मॉडल में ऐसे ऑपरेशन हो सकते हैं जिन्हें सीपीयू पर शेड्यूल किया गया हो. अगर सीपीयू, ANeuralNetworksCompilation_createForDevices
को दिए गए प्रोसेसर की सूची में शामिल है और वह उन ऑपरेशन के साथ काम करने वाला एकमात्र प्रोसेसर है या वह उन ऑपरेशन के लिए सबसे अच्छी परफ़ॉर्मेंस का दावा करने वाला प्रोसेसर है, तो उसे मुख्य (नॉन-फ़ॉलबैक) एक्ज़ीक्यूटर के तौर पर चुना जाएगा.
यह पक्का करने के लिए कि कोई सीपीयू न एक्ज़ीक्यूशन हो, डिवाइसों की सूची में nnapi-reference
को शामिल न करते समय ANeuralNetworksCompilation_createForDevices
का इस्तेमाल करें.
Android P में, debug.nn.partition
प्रॉपर्टी को 2 पर सेट करके, डीबग वर्शन पर फ़ॉलबैक को बंद किया जा सकता है.
मेमोरी डोमेन
Android 11 और उसके बाद के वर्शन में, NNAPI ऐसे मेमोरी डोमेन के साथ काम करता है जो ओपेक मेमोरी के लिए ऐलोकेटर इंटरफ़ेस उपलब्ध कराते हैं. यह ऐप्लिकेशन को सभी निष्पादनों में डिवाइस-नेटिव मेमोरी पास करने की अनुमति देता है, ताकि NNAPI एक ही ड्राइवर पर लगातार एक्ज़िक्यूशन करते समय डेटा को बिना ज़रूरत के कॉपी न करे या उसमें बदलाव न करे.
मेमोरी डोमेन की सुविधा, उन टेंसर के लिए है जो ज़्यादातर ड्राइवर के अंदर होते हैं और जिन्हें क्लाइंट साइड से बार-बार ऐक्सेस करने की ज़रूरत नहीं होती. ऐसे टेंसर के उदाहरणों में, क्रम के मॉडल में स्टेटस टेंसर शामिल हैं. जिन टेंसर को क्लाइंट साइड पर अक्सर सीपीयू ऐक्सेस करने की ज़रूरत होती है उनके लिए, शेयर किए गए मेमोरी पूल का इस्तेमाल करें.
ऑपैक मेमोरी को ऐलोकेट करने के लिए, यह तरीका अपनाएं:
नई यादें सेव करने के लिए,
ANeuralNetworksMemoryDesc_create()
फ़ंक्शन को कॉल करें:// Create a memory descriptor ANeuralNetworksMemoryDesc* desc; ANeuralNetworksMemoryDesc_create(&desc);
ANeuralNetworksMemoryDesc_addInputRole()
औरANeuralNetworksMemoryDesc_addOutputRole()
को कॉल करके, इनपुट और आउटपुट में काम के सभी रोल बताएं.// Specify that the memory may be used as the first input and the first output // of the compilation ANeuralNetworksMemoryDesc_addInputRole(desc, compilation, 0, 1.0f); ANeuralNetworksMemoryDesc_addOutputRole(desc, compilation, 0, 1.0f);
इसके अलावा,
ANeuralNetworksMemoryDesc_setDimensions()
को कॉल करके मेमोरी डाइमेंशन तय करें.// Specify the memory dimensions uint32_t dims[] = {3, 4}; ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
ANeuralNetworksMemoryDesc_finish()
को कॉल करके, डिस्क्रिप्टर की परिभाषा तय करें.ANeuralNetworksMemoryDesc_finish(desc);
ANeuralNetworksMemory_createFromDesc()
को डिस्क्रिप्टर पास करें और जितनी चाहें उतनी यादों को तय करें.// Allocate two opaque memories with the descriptor ANeuralNetworksMemory* opaqueMem; ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
जब आपको मेमोरी डिस्क्रिप्टर की ज़रूरत न हो, तो उसे खाली कर दें.
ANeuralNetworksMemoryDesc_free(desc);
क्लाइंट, बनाए गए ANeuralNetworksMemory
ऑब्जेक्ट का इस्तेमाल सिर्फ़ ANeuralNetworksExecution_setInputFromMemory()
या ANeuralNetworksExecution_setOutputFromMemory()
के साथ कर सकता है. ऐसा, ANeuralNetworksMemoryDesc
ऑब्जेक्ट में बताई गई भूमिकाओं के हिसाब से किया जाता है. ऑफ़सेट और लंबाई के आर्ग्युमेंट को 0 पर सेट किया जाना चाहिए. इससे पता चलता है कि पूरी मेमोरी का इस्तेमाल किया गया है. क्लाइंट, ANeuralNetworksMemory_copy()
का इस्तेमाल करके, मेमोरी में मौजूद कॉन्टेंट को साफ़ तौर पर सेट या निकाल सकता है.
बिना डाइमेंशन या रैंक वाली भूमिकाओं के साथ, अपारदर्शी यादें बनाई जा सकती हैं.
ऐसे में, हो सकता है कि ANEURALNETWORKS_OP_FAILED
स्थिति होने पर मेमोरी नहीं बन पाएगी. ऐसा तब होगा, जब डिवाइस के ड्राइवर के साथ यह काम न करे. क्लाइंट को फ़ॉलबैक लॉजिक लागू करने के लिए बढ़ावा दिया जाता है. इसके लिए, वह एक ऐसा बड़ा बफ़र असाइन करता है जो Asmem या BLOB-mode AHardwareBuffer
के साथ काम करता है.
जब NNAPI को अब ऑपैक मेमोरी ऑब्जेक्ट को ऐक्सेस करने की ज़रूरत न हो, तो उससे जुड़े ANeuralNetworksMemory
इंस्टेंस को खाली करें:
ANeuralNetworksMemory_free(opaqueMem);
परफ़ॉर्मेंस का आकलन करना
ऐप्लिकेशन के काम करने में लगने वाले समय को मेज़र करके या प्रोफ़ाइलिंग करके, ऐप्लिकेशन की परफ़ॉर्मेंस का आकलन किया जा सकता है.
लागू होने में लगने वाला समय
अगर आपको रनटाइम के ज़रिए, एक्ज़ीक्यूशन का कुल समय तय करना है, तो सिंक्रोनस एक्ज़ीक्यूशन एपीआई का इस्तेमाल करें और कॉल में लगने वाले समय को मेज़र करें. अगर आपको सॉफ़्टवेयर स्टैक के निचले लेवल की मदद से, प्रोग्राम के कुल रन टाइम का पता लगाना है, तो ANeuralNetworksExecution_setMeasureTiming
और ANeuralNetworksExecution_getDuration
का इस्तेमाल करें. इससे आपको ये जानकारी मिलेगी:
- ऐक्सेलरेटर पर एक्ज़ीक्यूशन का समय (ड्राइवर में नहीं, जो होस्ट प्रोसेसर पर चलता है).
- ड्राइवर में एक्सीक्यूशन का समय, जिसमें ऐक्सेलरेटर पर लगने वाला समय भी शामिल है.
ड्राइवर में प्रोग्राम को लागू करने में लगने वाले समय में, ओवरहेड शामिल नहीं होता. जैसे, रनटाइम का समय और रनटाइम के लिए ड्राइवर के साथ कम्यूनिकेट करने के लिए ज़रूरी आईपीसी.
ये एपीआई, सबमिट किए गए काम और काम पूरा होने के इवेंट के बीच की अवधि को मेज़र करते हैं. ये उस समय को मेज़र नहीं करते जो ड्राइवर या ऐक्सेलरेटर, अनुमान लगाने में लगाता है. इस दौरान, संदर्भ स्विच करने से रुकावट आ सकती है.
उदाहरण के लिए, अगर पहला अनुमान लगाने की प्रोसेस शुरू होती है, तो ड्राइवर दूसरा अनुमान लगाने के लिए काम करना बंद कर देता है. इसके बाद, वह पहला अनुमान लगाने की प्रोसेस फिर से शुरू करता है और उसे पूरा करता है. पहला अनुमान लगाने में लगने वाले समय में, वह समय भी शामिल होगा जब दूसरा अनुमान लगाने के लिए काम रोका गया था.
समय की यह जानकारी, किसी ऐप्लिकेशन को प्रोडक्शन में डिप्लॉय करने के लिए काम की हो सकती है, ताकि ऑफ़लाइन इस्तेमाल के लिए टेलीमेट्री इकट्ठा की जा सके. बेहतर परफ़ॉर्मेंस के लिए, ऐप्लिकेशन में बदलाव करने के लिए, समय के डेटा का इस्तेमाल किया जा सकता है.
इस सुविधा का इस्तेमाल करते समय, इन बातों का ध्यान रखें:
- समय की जानकारी इकट्ठा करने के लिए, परफ़ॉर्मेंस की लागत पर असर पड़ सकता है.
- सिर्फ़ ड्राइवर ही यह हिसाब लगा सकता है कि उस पर या ऐक्सेलरेटर पर कितना समय बीतता है. इसमें NNAPI रनटाइम और IPC पर बीतने वाला समय शामिल नहीं है.
- इन एपीआई का इस्तेमाल सिर्फ़ उस
ANeuralNetworksExecution
के साथ किया जा सकता है जिसेANeuralNetworksCompilation_createForDevices
के साथnumDevices = 1
का इस्तेमाल करके बनाया गया था. - किसी भी ड्राइवर को समय की जानकारी देने की ज़रूरत नहीं है.
Android Systrace की मदद से अपने ऐप्लिकेशन की प्रोफ़ाइल बनाना
Android 10 से, NNAPI अपने-आप systrace इवेंट जनरेट करता है. इनका इस्तेमाल, अपने ऐप्लिकेशन की प्रोफ़ाइल बनाने के लिए किया जा सकता है.
NNAPI सोर्स, आपके ऐप्लिकेशन से जनरेट किए गए सिस्ट्रेस इवेंट को प्रोसेस करने के लिए parse_systrace
यूटिलिटी के साथ आता है और मॉडल लाइफ़साइकल (इंस्टेंसेशन, तैयारी, कंपाइलेशन एक्ज़ीक्यूशन, और खत्म करना) और ऐप्लिकेशन की अलग-अलग लेयर के अलग-अलग चरणों में बिताए गए समय को दिखाता हुआ एक टेबल व्यू जनरेट करता है. आपका ऐप्लिकेशन इन लेयर में बंटा होता है:
Application
: ऐप्लिकेशन का मुख्य कोडRuntime
: NNAPI रनटाइमIPC
: NNAPI रनटाइम और ड्राइवर कोड के बीच इंटर-प्रोसेस कम्यूनिकेशनDriver
: ऐक्सेलरेटर ड्राइवर प्रोसेस.
प्रोफ़ाइलिंग विश्लेषण का डेटा जनरेट करना
मान लें कि आपने $ANDROID_BUILD_TOP पर AOSP सोर्स ट्री को चेक आउट किया है और टारगेट ऐप्लिकेशन के तौर पर TFLite इमेज क्लासिफ़िकेशन के उदाहरण का इस्तेमाल किया है. ऐसे में, यहां दिए गए तरीके से NNAPI प्रोफ़ाइलिंग डेटा जनरेट किया जा सकता है:
- इस निर्देश की मदद से, Android systrace शुरू करें:
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py -o trace.html -a org.tensorflow.lite.examples.classification nnapi hal freq sched idle load binder_driver
-o trace.html
पैरामीटर से पता चलता है कि ट्रेस trace.html
में लिखे जाएंगे. अपने ऐप्लिकेशन की प्रोफ़ाइल बनाते समय, आपको org.tensorflow.lite.examples.classification
की जगह उस प्रोसेस का नाम डालना होगा जो आपके ऐप्लिकेशन मेनिफ़ेस्ट में बताया गया है.
इससे आपके किसी एक शेल कंसोल पर काम चलता रहेगा. इसलिए, कमांड को बैकग्राउंड में न चलाएं, क्योंकि यह enter
के बंद होने का इंतज़ार करता है.
- systrace कलेक्टर शुरू होने के बाद, अपना ऐप्लिकेशन शुरू करें और अपना बेंचमार्क टेस्ट चलाएं.
हमारे मामले में, Android Studio से इमेज का क्लासिफ़िकेशन ऐप्लिकेशन शुरू किया जा सकता है. इसके अलावा, अगर ऐप्लिकेशन पहले से इंस्टॉल है, तो सीधे अपने टेस्ट फ़ोन के यूज़र इंटरफ़ेस (यूआई) से भी ऐप्लिकेशन शुरू किया जा सकता है. NNAPI का कुछ डेटा जनरेट करने के लिए, आपको ऐप्लिकेशन को NNAPI का इस्तेमाल करने के लिए कॉन्फ़िगर करना होगा. इसके लिए, ऐप्लिकेशन कॉन्फ़िगरेशन डायलॉग में, टारगेट डिवाइस के तौर पर NNAPI चुनें.
जांच पूरी होने के बाद, पहले चरण से चालू console टर्मिनल पर
enter
दबाकर, systrace को बंद करें.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_parser के आउटपुट में अपने ऐप्लिकेशन कोड के आंकड़े जोड़ना
parse_systrace ऐप्लिकेशन, Android में पहले से मौजूद systrace की सुविधा पर आधारित है. अपने ऐप्लिकेशन में खास कार्रवाइयों के लिए ट्रेस जोड़े जा सकते हैं. इसके लिए, कस्टम इवेंट के नामों के साथ systrace API (Java के लिए, नेटिव ऐप्लिकेशन के लिए) का इस्तेमाल करें.
अपने कस्टम इवेंट को ऐप्लिकेशन लाइफ़साइकल के चरणों से जोड़ने के लिए, इवेंट के नाम के आगे इनमें से कोई एक स्ट्रिंग जोड़ें:
[NN_LA_PI]
: शुरू करने के लिए, ऐप्लिकेशन लेवल का इवेंट[NN_LA_PP]
: तैयारी के लिए ऐप्लिकेशन लेवल इवेंट[NN_LA_PC]
: कंपाइलेशन के लिए ऐप्लिकेशन लेवल इवेंट[NN_LA_PE]
: ऐप्लिकेशन लेवल पर, एक्सीक्यूशन के लिए इवेंट
यहां एक उदाहरण दिया गया है, जिसमें बताया गया है कि Execution
फ़ेज़ के लिए runInferenceModel
सेक्शन और Application
लेयर जोड़कर, TFLite इमेज क्लासिफ़िकेशन के उदाहरण वाले कोड में कैसे बदलाव किया जा सकता है. इस लेयर में ऐसे अन्य सेक्शन preprocessBitmap
भी शामिल हैं जिन्हें NNAPI ट्रेस में शामिल नहीं किया जाएगा. runInferenceModel
सेक्शन, nnapi systrace पार्सर की मदद से प्रोसेस किए गए systrace इवेंट का हिस्सा होगा:
Kotlin
/** Runs inference and returns the classification results. */ fun recognizeImage(bitmap: Bitmap): List{ // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap") convertBitmapToByteBuffer(bitmap) Trace.endSection() // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel") long startTime = SystemClock.uptimeMillis() runInference() long endTime = SystemClock.uptimeMillis() Trace.endSection() ... return recognitions }
Java
/** Runs inference and returns the classification results. */ public ListrecognizeImage(final Bitmap bitmap) { // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap"); convertBitmapToByteBuffer(bitmap); Trace.endSection(); // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel"); long startTime = SystemClock.uptimeMillis(); runInference(); long endTime = SystemClock.uptimeMillis(); Trace.endSection(); ... Trace.endSection(); return recognitions; }
सेवा की क्वालिटी
Android 11 और उसके बाद के वर्शन में, NNAPI बेहतर सेवा क्वालिटी (QoS) उपलब्ध कराता है. इसके लिए, यह ऐप्लिकेशन को अपने मॉडल की प्राथमिकताएं तय करने की अनुमति देता है. साथ ही, यह किसी मॉडल को तैयार करने में लगने वाले ज़्यादा से ज़्यादा समय और किसी कैलकुलेशन को पूरा करने में लगने वाले ज़्यादा से ज़्यादा समय की जानकारी भी देता है. Android 11 में, NNAPI के लिए कुछ और नतीजे के कोड भी जोड़े गए हैं. इनकी मदद से, ऐप्लिकेशन को गड़बड़ियों के बारे में पता चलता है. जैसे, एक्सीक्यूशन की समयसीमा खत्म हो जाना.
वर्कलोड की प्राथमिकता सेट करना
NNAPI वर्कलोड की प्राथमिकता सेट करने के लिए, ANeuralNetworksCompilation_finish()
को कॉल करने से पहले ANeuralNetworksCompilation_setPriority()
पर कॉल करें.
आखिरी तारीख सेट करें
ऐप्लिकेशन, मॉडल कंपाइल करने और अनुमान लगाने, दोनों के लिए समयसीमा सेट कर सकते हैं.
- कंपाइल करने के लिए टाइम आउट सेट करने के लिए,
ANeuralNetworksCompilation_finish()
को कॉल करने से पहलेANeuralNetworksCompilation_setTimeout()
को कॉल करें. - अनुमान का टाइम आउट सेट करने के लिए, कंपाइलेशन शुरू करने से पहले
ANeuralNetworksExecution_setTimeout()
को कॉल करें.
ऑपरेंड के बारे में ज़्यादा जानकारी
इस सेक्शन में, ऑपरेंड इस्तेमाल करने के बारे में ऐडवांस विषयों के बारे में बताया गया है.
क्वांटाइज़ किए गए टेंसर
क्वांटाइज़ किया गया टेंसर, फ़्लोटिंग पॉइंट वैल्यू के n-डाइमेंशन वाले ऐरे को दिखाने का एक छोटा तरीका है.
NNAPI, 8-बिट असिमेट्रिक क्वांटाइज़्ड टेंसर के साथ काम करता है. इन टेंसर के लिए, हर सेल की वैल्यू को 8-बिट के पूर्णांक से दिखाया जाता है. टेंसर के साथ एक स्केल और शून्य पॉइंट की वैल्यू होती है. इनका इस्तेमाल 8-बिट पूर्णांक को दिखाई जा रही फ़्लोटिंग-पॉइंट वैल्यू में बदलने के लिए किया जाता है.
फ़ॉर्मूला यह है:
(cellValue - zeroPoint) * scale
जहां ज़ीरोपॉइंट वैल्यू 32-बिट पूर्णांक और 32-बिट फ़्लोटिंग पॉइंट वैल्यू होती है.
32-बिट फ़्लोटिंग पॉइंट वैल्यू के टेंसर की तुलना में, 8-बिट क्वांटाइज़ किए गए टेंसर के दो फ़ायदे हैं:
- आपका ऐप्लिकेशन छोटा होता है, क्योंकि ट्रेन किए गए वेट का साइज़, 32-बिट टेंसर के साइज़ का एक चौथाई होता है.
- कंप्यूटेशन को अक्सर तेज़ी से एक्ज़ीक्यूट किया जा सकता है. ऐसा इसलिए होता है, क्योंकि मेमोरी से फ़ेच किए जाने वाले डेटा की संख्या कम होती है. साथ ही, डीएसपी जैसे प्रोसेसर, पूर्णांक के हिसाब से गणना करने में ज़्यादा असरदार होते हैं.
फ़्लोटिंग पॉइंट मॉडल को क्वांटाइज़ किए गए मॉडल में बदला जा सकता है. हालांकि, हमारे अनुभव से पता चला है कि क्वांटाइज़ किए गए मॉडल को सीधे तौर पर ट्रेनिंग देकर बेहतर नतीजे मिलते हैं. असल में, न्यूरल नेटवर्क हर वैल्यू की ज़्यादा जानकारी को ध्यान में रखकर काम करता है. हर क्वांटाइज़ किए गए टेंसर के लिए, स्केल और ज़ीरो पॉइंट की वैल्यू, ट्रेनिंग की प्रोसेस के दौरान तय की जाती हैं.
एनएनएपीआई में, ANeuralNetworksOperandType
डेटा स्ट्रक्चर के टाइप फ़ील्ड को ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
पर सेट करके, क्वांटाइज़्ड टेंसर टाइप तय किए जाते हैं.
उस डेटा स्ट्रक्चर में, टेंसर के स्केल और zeroPoint की वैल्यू भी तय की जाती है.
आठ-बिट असिमेट्रिक क्वांटाइज़ किए गए टेंसर के अलावा, NNAPI इनके साथ भी काम करता है:
ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
का इस्तेमाल,CONV/DEPTHWISE_CONV/TRANSPOSED_CONV
ऑपरेशन के लिए वैल्यू के वज़न दिखाने के लिए किया जा सकता है.ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
का इस्तेमाल,QUANTIZED_16BIT_LSTM
की इंटरनल स्थिति के लिए किया जा सकता है.ANEURALNETWORKS_TENSOR_QUANT8_SYMM
, जोANEURALNETWORKS_DEQUANTIZE
का इनपुट हो सकता है.
वैकल्पिक ऑपरेंड
ANEURALNETWORKS_LSH_PROJECTION
जैसे कुछ ऑपरेशन में वैकल्पिक ऑपरेंड होते हैं. मॉडल में यह बताने के लिए कि वैकल्पिक ऑपरेंड को छोड़ा गया है, ANeuralNetworksModel_setOperandValue()
फ़ंक्शन को कॉल करें. इसके लिए, बफ़र के लिए NULL
और लंबाई के लिए 0 पास करें.
अगर हर बार फ़ंक्शन के इस्तेमाल पर, ऑपरेंड मौजूद है या नहीं, यह तय करने का तरीका अलग-अलग है, तो ANeuralNetworksExecution_setInput()
या ANeuralNetworksExecution_setOutput()
फ़ंक्शन का इस्तेमाल करके, ऑपरेंड को छोड़ा जा सकता है. इसके लिए, बफ़र के तौर पर NULL
और लंबाई के तौर पर 0 दें.
अज्ञात रैंक के टेंसर
Android 9 (एपीआई लेवल 28) में, ऐसे मॉडल ऑपरेंड पेश किए गए हैं जिनके डाइमेंशन की जानकारी नहीं है, लेकिन रैंक (डाइमेंशन की संख्या) की जानकारी है. Android 10 (एपीआई लेवल 29) में, रैंक के बारे में जानकारी नहीं देने वाले टेंसर का इस्तेमाल शुरू किया गया है. इस बारे में ANeuralNetworksOperandType में बताया गया है.
NNAPI बेंचमार्क
NNAPI मानदंड, platform/test/mlts/benchmark
(मानदंड ऐप्लिकेशन) और platform/test/mlts/models
(मॉडल और डेटासेट) में एओएसपी पर उपलब्ध है.
मानदंड, इंतज़ार के समय और सटीक होने का आकलन करता है. साथ ही, सीपीयू पर चल रहे Tensorflow Lite का इस्तेमाल करके, एक जैसे मॉडल और डेटासेट के लिए ड्राइवर की तुलना करता है.
मानदंड का इस्तेमाल करने के लिए, ये काम करें:
टारगेट किए गए Android डिवाइस को अपने कंप्यूटर से कनेक्ट करें, टर्मिनल विंडो खोलें, और पक्का करें कि डिवाइस को adb से ऐक्सेस किया जा सकता हो.
अगर एक से ज़्यादा Android डिवाइस कनेक्ट हैं, तो टारगेट डिवाइस के
ANDROID_SERIAL
एनवायरमेंट वैरिएबल को एक्सपोर्ट करें.Android की टॉप-लेवल की सोर्स डायरेक्ट्री पर जाएं.
इन कमांड को चलाएं:
lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available ./test/mlts/benchmark/build_and_run_benchmark.sh
बेंचमार्क की प्रोसेस पूरी होने के बाद, उसके नतीजे एचटीएमएल पेज के तौर पर दिखाए जाएंगे.
xdg-open
NNAPI लॉग
NNAPI, सिस्टम लॉग में गड़बड़ी की अहम जानकारी जनरेट करता है. लॉग का विश्लेषण करने के लिए, logcat उपयोगिता का इस्तेमाल करें.
खास फ़ेज़ या कॉम्पोनेंट के लिए, ज़्यादा जानकारी वाली NNAPI लॉगिंग चालू करें. इसके लिए, adb shell
का इस्तेमाल करके प्रॉपर्टी debug.nn.vlog
को वैल्यू की इस सूची पर सेट करें. वैल्यू को स्पेस, कोलन या कॉमा से अलग किया गया है:
model
: मॉडल बनानाcompilation
: मॉडल को लागू करने का प्लान और कंपाइलेशन जनरेट करनाexecution
: मॉडल को लागू करनाcpuexe
: 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"
फ़िलहाल, यह एक्सप्रेशन ये टैग दिखाता है:
- BurstBuilder
- कॉलबैक
- कंपाइलेशनबिल्डर
- CpuExecutor
- ExecutionBuilder
- ExecutionBurstController
- एक्ज़ीक्यूशन बर्स्टसर्वर
- ExecutionPlan
- FibonacciDriver
- GraphDump
- IndexedShapeWrapper
- IonWatcher
- मैनेजर
- मेमोरी
- MemoryUtils
- MetaModel
- मॉडलArgumentInfo
- 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
पर सेट करना होगा.