واجهة برمجة التطبيقات Android Neure Networks API (NNAPI) هي واجهة برمجة تطبيقات تعمل بنظام التشغيل Android C تم تصميمها لتشغيل عمليات حسابية مكثفة لتعلُّم الآلة على أجهزة Android. تم تصميم NNAPI لتوفير طبقة أساسية من الوظائف لأُطر عمل تعلُّم الآلة ذات المستوى الأعلى، مثل TensorFlow Lite وCaffe2 اللتين تنشئان الشبكات العصبية وتدريبها. تتوفر واجهة برمجة التطبيقات على جميع أجهزة Android التي تعمل بالإصدار 8.1 (المستوى 27 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث من نظام التشغيل Android.
وتدعم NNAPI التدخل من خلال تطبيق البيانات من أجهزة Android على النماذج المُدرَّبة سابقًا والتي حدّدها المطوِّرون. ومن أمثلة أساليب الحماية تصنيف الصور والتنبؤ بسلوك المستخدم واختيار الردود المناسبة على طلب البحث.
هناك العديد من المزايا التي توفّرها تقنية الإبلاغ على الجهاز فقط:
- وقت الاستجابة: لست بحاجة إلى إرسال طلب عبر الاتصال بالشبكة وانتظار الرد. على سبيل المثال، قد يكون هذا أمرًا بالغ الأهمية لتطبيقات الفيديو التي تعالج الإطارات المتتالية الواردة من الكاميرا.
- مدى التوفّر: يتم تشغيل التطبيق حتى عندما يكون خارج تغطية الشبكة.
- السرعة: توفِّر الأجهزة الجديدة المخصّصة لمعالجة الشبكة العصبونية عمليات حسابية أسرع بكثير من وحدة المعالجة المركزية للأغراض العامة وحدها.
- الخصوصية: لا يتم نقل البيانات خارج جهاز Android.
- التكلفة: لا حاجة إلى استخدام مزرعة الخادم عند إجراء جميع العمليات الحسابية على جهاز Android.
هناك أيضًا حلول وسط يجب أن يأخذها المطوّر في الاعتبار:
- استخدام النظام: يتضمّن تقييم الشبكات العصبونية الكثير من العمليات الحسابية، ما قد يؤدي إلى زيادة استخدام طاقة البطارية. يجب مراعاة مراقبة حالة البطارية إذا كان هذا مصدر قلق لتطبيقك، خاصةً في عمليات الحوسبة طويلة الأمد.
- حجم التطبيق: انتبه إلى حجم نماذجك. قد تستهلك النماذج عدة ميغابايت من المساحة. إذا كان تجميع النماذج الكبيرة في حزمة APK سيؤثر في المستخدمين بشكل غير ملائم، يمكنك تنزيل هذه النماذج بعد تثبيت التطبيق أو استخدام نماذج أصغر أو تشغيل عمليات الحوسبة في السحابة. لا توفر NNAPI وظائف لتشغيل النماذج في السحابة.
يمكنك الاطّلاع على نموذج لواجهة برمجة تطبيقات الشبكات العصبية في Android للاطّلاع على مثال حول كيفية استخدام واجهة برمجة التطبيقات NNAPI.
التعرّف على وقت تشغيل واجهة برمجة تطبيقات الشبكات العصبونية
ومن المفترض أن يتم طلب واجهة NNAPI من خلال مكتبات وأُطر العمل وأدوات تعلُّم الآلة التي تتيح للمطوّرين تدريب نماذجهم خارج الأجهزة ونشرها على أجهزة Android. ولن تستخدم التطبيقات عادةً NNAPI مباشرةً، ولكنها ستستخدم بدلاً من ذلك أطر عمل ذات مستوى أعلى لتعلُّم الآلة. ويمكن لأُطر العمل هذه بدورها استخدام واجهة NNAPI لتنفيذ عمليات استنتاج مسرَّعة على الأجهزة على الأجهزة المتوافقة.
استنادًا إلى متطلبات التطبيق وإمكانيات الأجهزة على جهاز Android، يمكن لبيئة تشغيل الشبكة العصبونية في Android توزيع عبء العمل الحاسوبي بكفاءة على مستوى معالِجات البيانات المتاحة على الجهاز، بما في ذلك أجهزة الشبكة العصبونية المخصّصة ووحدات معالجة الرسومات (GPU) ومعالِجات الإشارات الرقمية (DSP).
بالنسبة إلى أجهزة Android التي تفتقر إلى برنامج تشغيل متخصّص، ينفِّذ وقت تشغيل NNAPI الطلبات على وحدة المعالجة المركزية (CPU).
يوضح الشكل 1 بنية النظام عالية المستوى لـ NNAPI.
![](https://developer.android.google.cn/static/ndk/images/nnapi/nnapi_architecture.png?authuser=9&hl=ar)
نموذج برمجة واجهة برمجة التطبيقات للشبكات العصبية
لإجراء العمليات الحسابية باستخدام NNAPI، تحتاج أولاً إلى إنشاء رسم بياني موجَّه يحدد العمليات الحسابية المراد تنفيذها. يشكِّل هذا الرسم البياني الحاسوبي، إلى جانب بيانات المدخلات (على سبيل المثال، الأوزان والانحيازات التي تم تمريرها من إطار عمل التعلم الآلي)، نموذجًا لتقييم وقت تشغيل NNAPI.
تستخدم واجهة NNAPI أربعة تجريدات رئيسية:
- النموذج: رسم بياني لحسابي للعمليات الرياضية والقيم الثابتة التي يتم تعلّمها من خلال عملية تدريب. ترتبط هذه العمليات بالشبكات العصبونية. وتشمل هذه النماذج الالتفاف الثنائي الأبعاد (الثنائي الأبعاد)، والتفعيل اللوجستي (السيني)، والتفعيل الخطي المعدّل (ReLU)، وغير ذلك. إن إنشاء نموذج هو عملية متزامنة.
وبعد إنشائها بنجاح، يمكن إعادة استخدامها في سلاسل المحادثات وعمليات التجميع.
في NNAPI، يتم تمثيل النموذج على أنّه مثيل
ANeuralNetworksModel
. - التجميع: يمثل إعدادًا لتجميع نموذج NNAPI في رمز بمستوى أقل. يعد إنشاء التحويل البرمجي عملية متزامنة. وبعد إنشائها بنجاح، يمكن إعادة استخدامها على مستوى سلاسل المحادثات وعمليات التنفيذ. في NNAPI، يتم تمثيل كل عملية تجميع كمثيل
ANeuralNetworksCompilation
. - الذاكرة: تمثل الذاكرة المشتركة، والملفات التي تم ربطها بالذاكرة، والمخازن المؤقتة المشابهة للذاكرة. ويتيح استخدام المخزن المؤقت للذاكرة نقل بيانات وقت تشغيل NNAPI إلى برامج التشغيل بشكل أكثر كفاءة. ينشئ التطبيق عادةً مخزنًا مؤقتًا واحدًا للذاكرة يحتوي على كل أداة تينر مطلوبة لتحديد نموذج. يمكنك أيضًا استخدام مخازن الذاكرة
لتخزين المدخلات والمخرجات لمثيل التنفيذ. في NNAPI، يتم تمثيل كل مخزن مؤقت للذاكرة كمثيل
ANeuralNetworksMemory
. التنفيذ: واجهة لتطبيق نموذج NNAPI على مجموعة من الإدخالات وجمع النتائج. يمكن تنفيذ التنفيذ بشكلٍ متزامن أو غير متزامن.
بالنسبة إلى التنفيذ غير المتزامن، يمكن أن تنتظر سلاسل محادثات متعددة أثناء التنفيذ نفسه. عند اكتمال عملية التنفيذ هذه، يتم تحرير جميع سلاسل المحادثات.
في NNAPI، يتم تمثيل كل عملية تنفيذ على أنّها مثيل
ANeuralNetworksExecution
.
يوضح الشكل 2 تدفق البرمجة الأساسي.
![](https://developer.android.google.cn/static/ndk/images/nnapi/nnapi_flow.png?authuser=9&hl=ar)
يصف الجزء المتبقي من هذا القسم خطوات إعداد نموذج NNAPI لإجراء العمليات الحسابية وتجميع النموذج وتنفيذ النموذج المجمّع.
توفير الوصول إلى بيانات التدريب
من المحتمل أن يتم تخزين بيانات الأوزان والتحيزات المدرَّبة في ملف. لتوفير
وقت تشغيل NNAPI مع إمكانية الوصول الفعّال إلى هذه البيانات، أنشِئ مثيل ANeuralNetworksMemory
من خلال استدعاء الدالة
ANeuralNetworksMemory_createFromFd()
وتمرير واصف ملف البيانات المفتوحة. يمكنك أيضًا تحديد علامات حماية الذاكرة والإزاحة التي تبدأ فيها منطقة الذاكرة المشتركة في الملف.
// 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. يتم تعريف كل نموذج من خلال عامل أو أكثر من المعاملات والعمليات.
الجهات المعنيّة
المعاملات هي كائنات بيانات تستخدم في تعريف الرسم البياني. وهي تشمل مدخلات ومخرجات النموذج، والعقد الوسطى التي تحتوي على البيانات التي تتدفق من عملية إلى أخرى، والثوابت التي يتم تمريرها إلى هذه العمليات.
هناك نوعان من المعاملات التي يمكن إضافتها إلى نماذج NNAPI: الكميات القياسية والعوالم.
يمثل العدد القياسي قيمة واحدة. تتوافق NNAPI مع القيم العددية في تنسيقات الأعداد المنطقية 32 بت، و النقطة العائمة 16 بت، والنقطة العائمة 32 بت، والأعداد الصحيحة 32 بت، والتنسيقات الصحيحة غير الموقَّعة.
تشتمل معظم العمليات في NNAPI على متسللات. المتغيرات عبارة عن صفائف مكوّنة من n. يتوافق NNAPI مع أدوات وحدة قياس بتنسيق النقاط العائمة 16 بت، والنقطة العائمة 32 بت، والكمّية بنظام 8 بت، وعدد 16 بت الصحيح، والقيم المنطقية 8 بت.
على سبيل المثال، يمثل الشكل 3 نموذجًا به عمليتين: إضافة متبوعة بالضرب. يستخدم النموذج متسّع إدخال وينتج موترًا واحدًا للمخرجات.
![](https://developer.android.google.cn/static/ndk/images/nnapi/nnapi_operands.png?authuser=9&hl=ar)
يتضمن النموذج أعلاه سبعة معاملات. ويتم تحديد هذه المعاملات ضمنيًا عن طريق فهرس ترتيب إضافتها إلى النموذج. يحتوي المعامل الأول الذي تمت إضافته على فهرس 0، والعامل الثاني فهرسًا 1، وهكذا. العناصر 1 و2 و3 و5 هي معامل ثابتة.
لا يهم الترتيب الذي تضيف به المعاملات. على سبيل المثال، يمكن أن يكون معامل إخراج النموذج هو أول معامل تتم إضافته. الجزء المهم هو استخدام قيمة الفهرس الصحيحة عند الإشارة إلى المعامل.
وللمعاملين أنواع. ويتم تحديدها عند إضافتها إلى النموذج.
لا يمكن استخدام المعامل كإدخال وإخراج لنموذج.
يجب أن يكون كل معامل إدخال نموذج أو ثابتًا أو معامل إخراج لعملية واحدة بالضبط.
للحصول على معلومات إضافية عن استخدام المعاملات، اطّلِع على مزيد من المعلومات عن المعاملات.
العمليات
تحدد العملية العمليات الحسابية المراد تنفيذها. تتكون كل عملية من هذه العناصر:
- نوع عملية (على سبيل المثال، الجمع والضرب والالتفاف)،
- قائمة فهارس المعاملات التي تستخدمها العملية للإدخال،
- قائمة فهارس المعاملات التي تستخدمها العملية للمخرج.
الترتيب في هذه القوائم مهمّ، لذا راجِع مرجع واجهة برمجة التطبيقات NNAPI للاطّلاع على المدخلات والمخرجات المتوقّعة لكل نوع عملية.
يجب إضافة المعاملات التي تستهلكها العملية أو تنتجها إلى النموذج قبل إضافة العملية.
لا يهم ترتيب إضافة العمليات. تعتمد واجهة NNAPI على التبعيات التي يحددها الرسم البياني للعمليات والعمليات لتحديد ترتيب تنفيذ العمليات.
يتم تلخيص العمليات التي تتوافق مع NNAPI في الجدول أدناه:
مشكلة معروفة في المستوى 28 من واجهة برمجة التطبيقات: عند نقل
عشرات
ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
إلى العملية
ANEURALNETWORKS_PAD
،
المتوفّرة على نظام التشغيل Android 9 (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأحدث، قد لا تتطابق النتائج من NNAPI مع الناتج من أطر عمل تعلُّم الآلة
ذات المستوى الأعلى، مثل
TensorFlow Lite. وبدلاً من ذلك، عليك
اجتياز
ANEURALNETWORKS_TENSOR_FLOAT32
فقط.
تم حلّ المشكلة في نظام التشغيل Android 10 (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأحدث.
إنشاء نماذج
في المثال التالي، ننشئ نموذج عمليتين كما في الشكل 3.
لإنشاء النموذج، يُرجى اتّباع الخطوات التالية:
يمكنك استدعاء الدالة
ANeuralNetworksModel_create()
لتحديد نموذج فارغ.ANeuralNetworksModel* model = NULL; ANeuralNetworksModel_create(&model);
أضِف المعامل إلى نموذجك من خلال استدعاء
ANeuralNetworks_addOperand()
. ويتم تحديد أنواع البيانات الخاصة بها باستخدام بنية بياناتANeuralNetworksOperandType
.// In our example, all our tensors are matrices of dimension [3][4] ANeuralNetworksOperandType tensor3x4Type; tensor3x4Type.type = ANEURALNETWORKS_TENSOR_FLOAT32; tensor3x4Type.scale = 0.f; // These fields are used for quantized tensors tensor3x4Type.zeroPoint = 0; // These fields are used for quantized tensors tensor3x4Type.dimensionCount = 2; uint32_t dims[2] = {3, 4}; tensor3x4Type.dimensions = dims;
// We also specify operands that are activation function specifiers ANeuralNetworksOperandType activationType; activationType.type = ANEURALNETWORKS_INT32; activationType.scale = 0.f; activationType.zeroPoint = 0; activationType.dimensionCount = 0; activationType.dimensions = NULL;
// Now we add the seven operands, in the same order defined in the diagram ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 0 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 1 ANeuralNetworksModel_addOperand(model, &activationType); // operand 2 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 3 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 4 ANeuralNetworksModel_addOperand(model, &activationType); // operand 5 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 6بالنسبة إلى المعاملات التي لها قيم ثابتة، مثل الترجيحات والانحيازات التي يحصل عليها تطبيقك من عملية تدريب، يمكنك استخدام الدالتين
ANeuralNetworksModel_setOperandValue()
وANeuralNetworksModel_setOperandValueFromMemory()
.في المثال التالي، نضبط قيمًا ثابتة من ملف بيانات التدريب تتوافق مع المخزن المؤقت للذاكرة الذي أنشأناه في إتاحة الوصول إلى بيانات التدريب.
// In our example, operands 1 and 3 are constant tensors whose values were // established during the training process const int sizeOfTensor = 3 * 4 * 4; // The formula for size calculation is dim0 * dim1 * elementSize ANeuralNetworksModel_setOperandValueFromMemory(model, 1, mem1, 0, sizeOfTensor); ANeuralNetworksModel_setOperandValueFromMemory(model, 3, mem1, sizeOfTensor, sizeOfTensor);
// We set the values of the activation operands, in our example operands 2 and 5 int32_t noneValue = ANEURALNETWORKS_FUSED_NONE; ANeuralNetworksModel_setOperandValue(model, 2, &noneValue, sizeof(noneValue)); ANeuralNetworksModel_setOperandValue(model, 5, &noneValue, sizeof(noneValue));لكل عملية في الرسم البياني الموجَّه الذي تريد احتسابه، أضِف العملية إلى النموذج من خلال استدعاء الدالة
ANeuralNetworksModel_addOperation()
.كمَعلمات لهذه المكالمة، يجب أن يوفّر تطبيقك ما يلي:
- نوع العملية
- عدد قيم الإدخال
- مصفوفة الفهارس لمعاملات الإدخال
- عدد قيم المخرجات
- مصفوفة الفهارس لمعاملات الإخراج
تجدر الإشارة إلى أنّه لا يمكن استخدام المعامل لكل من الإدخال والإخراج لنفس العملية.
// We have two operations in our example // The first consumes operands 1, 0, 2, and produces operand 4 uint32_t addInputIndexes[3] = {1, 0, 2}; uint32_t addOutputIndexes[1] = {4}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, addInputIndexes, 1, addOutputIndexes);
// The second consumes operands 3, 4, 5, and produces operand 6 uint32_t multInputIndexes[3] = {3, 4, 5}; uint32_t multOutputIndexes[1] = {6}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_MUL, 3, multInputIndexes, 1, multOutputIndexes);حدِّد المعاملات التي يجب أن يتعامل معها النموذج كمدخلات ومخرجات من خلال استدعاء الدالة
ANeuralNetworksModel_identifyInputsAndOutputs()
.// Our model has one input (0) and one output (6) uint32_t modelInputIndexes[1] = {0}; uint32_t modelOutputIndexes[1] = {6}; ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, modelInputIndexes, 1 modelOutputIndexes);
يمكنك اختياريًا تحديد ما إذا كان مسموحًا احتساب
ANEURALNETWORKS_TENSOR_FLOAT32
بنطاق أو دقة منخفضة على نحو أقل من نطاق تنسيق النقطة العائمة بتنسيق IEEE 754 16 بت من خلال استدعاءANeuralNetworksModel_relaxComputationFloat32toFloat16()
.يمكنك طلب الرقم
ANeuralNetworksModel_finish()
لوضع اللمسات الأخيرة على تعريف النموذج. إذا لم تكن هناك أخطاء، تعرض هذه الدالة رمز النتيجةANEURALNETWORKS_NO_ERROR
.ANeuralNetworksModel_finish(model);
بعد إنشاء نموذج، يمكنك تجميعه لأي عدد من المرات وتنفيذ كل تجميع بأي عدد من المرات.
مسار التحكّم
لدمج تدفق التحكم في نموذج NNAPI، قم بما يلي:
يمكنك إنشاء الرسوم البيانية الفرعية الخاصة بالتنفيذ (الفرعان الفرعيان
then
وelse
لجملةIF
، والرسومتين الفرعيتينcondition
وbody
لتكرارWHILE
) كنماذجANeuralNetworksModel*
مستقلة:ANeuralNetworksModel* thenModel = makeThenModel(); ANeuralNetworksModel* elseModel = makeElseModel();
أنشئ معاملات تشير إلى تلك النماذج داخل النموذج الذي يحتوي على تدفق التحكم:
ANeuralNetworksOperandType modelType = { .type = ANEURALNETWORKS_MODEL, }; ANeuralNetworksModel_addOperand(model, &modelType); // kThenOperandIndex ANeuralNetworksModel_addOperand(model, &modelType); // kElseOperandIndex ANeuralNetworksModel_setOperandValueFromModel(model, kThenOperandIndex, &thenModel); ANeuralNetworksModel_setOperandValueFromModel(model, kElseOperandIndex, &elseModel);
إضافة عملية مسار التحكّم:
uint32_t inputs[] = {kConditionOperandIndex, kThenOperandIndex, kElseOperandIndex, kInput1, kInput2, kInput3}; uint32_t outputs[] = {kOutput1, kOutput2}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_IF, std::size(inputs), inputs, std::size(output), outputs);
موسيقى مجمّعة
تحدد خطوة التجميع المعالجات التي سيتم تنفيذ نموذجك عليها وتطلب من برامج التشغيل المقابلة الاستعداد لتنفيذه. قد يشمل ذلك إنشاء رمز آلة خاص بالمعالجات التي سيعمل نموذجك عليها.
لتجميع نموذج، اتبع الخطوات التالية:
يمكنك استدعاء الدالة
ANeuralNetworksCompilation_create()
لإنشاء مثيل تجميع جديد.// Compile the model ANeuralNetworksCompilation* compilation; ANeuralNetworksCompilation_create(model, &compilation);
يمكنك استخدام تخصيص الجهاز لاختيار الأجهزة التي تريد تنفيذ الطلبات عليها بشكل واضح.
يمكنك اختياريًا التأثير في كيفية تبديل وقت التشغيل بين استخدام طاقة البطارية وسرعة التنفيذ. يمكنك إجراء ذلك من خلال الاتصال بالرقم
ANeuralNetworksCompilation_setPreference()
.// Ask to optimize for low power consumption ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);
تشمل الإعدادات المفضَّلة التي يمكنك تحديدها ما يلي:
ANEURALNETWORKS_PREFER_LOW_POWER
: يُفضَّل التنفيذ بطريقة تقلل من استهلاك البطارية. وهذا المحتوى مطلوب في الفيديوهات المجمّعة التي يتمّ تنفيذها كثيرًا.ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER
: من الأفضل عرض إجابة واحدة في أسرع وقت ممكن، حتى إذا أدى ذلك إلى استهلاك أكبر للطاقة. هذا هو الخيار التلقائي.ANEURALNETWORKS_PREFER_SUSTAINED_SPEED
: يُفضَّل زيادة سرعة معالجة الإطارات المتتالية، كما هو الحال عند معالجة اللقطات المتتالية الواردة من الكاميرا.
يمكنك إعداد ميزة التخزين المؤقت للتجميع من خلال استدعاء
ANeuralNetworksCompilation_setCaching
.// Set up compilation caching ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);
استخدِم
getCodeCacheDir()
معcacheDir
. يجب أن يكونtoken
المحدد فريدًا لكل نموذج داخل التطبيق.يمكنك إنهاء تعريف التجميع من خلال استدعاء الرمز
ANeuralNetworksCompilation_finish()
. إذا لم تكن هناك أخطاء، تعرض هذه الدالة رمز النتيجةANEURALNETWORKS_NO_ERROR
.ANeuralNetworksCompilation_finish(compilation);
اكتشاف الأجهزة وتعيينها
على أجهزة Android التي تعمل بالإصدار 10 من نظام التشغيل Android (المستوى 29 لواجهة برمجة التطبيقات) والإصدارات الأحدث، توفّر واجهة برمجة التطبيقات NNAPI وظائف تتيح للمكتبات والتطبيقات المستندة إلى أُطر عمل تعلُّم الآلة الحصول على معلومات حول الأجهزة المتاحة وتحديد الأجهزة التي سيتم استخدامها في التنفيذ. إنّ توفير معلومات عن الأجهزة المتاحة يسمح للتطبيقات بالحصول على الإصدار الدقيق من برامج التشغيل المتوفّرة على الجهاز لتجنّب حالات عدم التوافق المعروفة. ويمكن تحسين التطبيقات لتلائم جهاز Android الذي يتم نشر التطبيقات عليه، وذلك من خلال منح التطبيقات إمكانية تحديد الأجهزة التي ستُنفذ أقسامًا مختلفة من أحد النماذج.
رصد الأجهزة
استخدِم
ANeuralNetworks_getDeviceCount
لمعرفة عدد الأجهزة المتاحة. لكل جهاز، استخدِم ANeuralNetworks_getDevice
لضبط مثيل ANeuralNetworksDevice
على مرجع إلى ذلك الجهاز.
بمجرد حصولك على مرجع جهاز، يمكنك معرفة معلومات إضافية حول هذا الجهاز باستخدام الدوال التالية:
ANeuralNetworksDevice_getFeatureLevel
ANeuralNetworksDevice_getName
ANeuralNetworksDevice_getType
ANeuralNetworksDevice_getVersion
تخصيص الجهاز
استخدِم
ANeuralNetworksModel_getSupportedOperationsForDevices
لتحديد عمليات النموذج التي يمكن تنفيذها على أجهزة معيّنة.
لتحديد مسرِّعات الأعمال التي تريد استخدامها في التنفيذ، يمكنك طلب ANeuralNetworksCompilation_createForDevices
بدلاً من ANeuralNetworksCompilation_create
.
استخدِم الكائن ANeuralNetworksCompilation
الناتج كالمعتاد.
تعرض الدالة خطأ إذا كان النموذج المقدم يحتوي على عمليات غير متوافقة مع الأجهزة المحددة.
وإذا تم تحديد عدة أجهزة، تكون بيئة التشغيل مسؤولة عن توزيع العمل على الأجهزة.
على غرار الأجهزة الأخرى، يتم تمثيل تنفيذ وحدة المعالجة المركزية (CPU) NNAPI بواسطة ANeuralNetworksDevice
بالاسم nnapi-reference
والنوع ANEURALNETWORKS_DEVICE_TYPE_CPU
. عند استدعاء الدالة ANeuralNetworksCompilation_createForDevices
، لا يتم استخدام عملية تنفيذ وحدة المعالجة المركزية (CPU) لمعالجة حالات تعذُّر تجميع النماذج وتنفيذها.
تقع على عاتق التطبيق مسؤولية تقسيم نموذج إلى نماذج فرعية يمكن
تشغيلها على الأجهزة المحددة. ومن المفترَض أن تستمرّ التطبيقات التي لا تحتاج إلى التقسيم اليدوي في استدعاء ANeuralNetworksCompilation_create
الأبسط لاستخدام جميع الأجهزة المتاحة (بما في ذلك وحدة المعالجة المركزية (CPU) لتسريع
النموذج. إذا تعذّر توفير النموذج بالكامل على الأجهزة التي حدّدتها باستخدام ANeuralNetworksCompilation_createForDevices
، سيتم عرض ANEURALNETWORKS_BAD_DATA
.
تقسيم النموذج
عند توفّر أجهزة متعدّدة للنموذج، يوزِّع وقت تشغيل NNAPI العمل على جميع الأجهزة. على سبيل المثال، إذا تم توفير أكثر من جهاز واحد لـ "ANeuralNetworksCompilation_createForDevices
"، سيتم أخذ جميع الأجهزة المحدّدة في الاعتبار عند تخصيص العمل. تجدر الإشارة إلى أنّه إذا لم يكن جهاز وحدة المعالجة المركزية (CPU) مدرجًا في القائمة، سيتم إيقاف تنفيذ وحدة المعالجة المركزية (CPU). عند استخدام "ANeuralNetworksCompilation_create
"، سيتم أخذ جميع الأجهزة المتاحة في الاعتبار، بما في ذلك وحدة المعالجة المركزية (CPU).
يتم التوزيع من خلال الاختيار من بين قائمة الأجهزة المتاحة لكل عملية من العمليات في النموذج، ثم تحديد الجهاز الذي يتوافق مع العملية للإعلان عن أفضل أداء، أي في أسرع وقت للتنفيذ أو أقل استهلاك للطاقة، بناءً على خيار التنفيذ المفضّل الذي حدّده العميل. لا تأخذ خوارزمية التقسيم هذه في الحسبان أوجه القصور المحتملة التي يتسبب فيها طلب الإدراج بين المعالجات المختلفة، لذلك عند تحديد معالِجات متعددة (إما بشكل صريح عند استخدام 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
.
الرسالة (على مستوى المعلومات مع العلامة 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 ميكرو ثانية بين وقت إرسال إشعار إلى سلسلة التعليمات أو تشغيلها ووقت ربطها بوحدة معالجة مركزية (CPU) في النهاية.
لتحسين وقت الاستجابة، يمكنك بدلاً من ذلك توجيه أحد التطبيقات لإجراء استدعاء
استنتاج متزامن إلى بيئة التشغيل. سيعود هذا الاستدعاء فقط عند اكتمال الاستنتاج بدلاً من العودة بمجرد بدء الاستنتاج. بدلاً من
استدعاء
ANeuralNetworksExecution_startCompute
لطلب استنتاج غير متزامن لبيئة التشغيل، يطلب التطبيق
ANeuralNetworksExecution_compute
لإجراء استدعاء متزامن لبيئة التشغيل. لن تؤدي المكالمة إلى
"ANeuralNetworksExecution_compute
" إلى إجراء ANeuralNetworksEvent
ولا يتم إقرانها بمكالمة إلى "ANeuralNetworksEvent_wait
".
عمليات تنفيذ الصور المتسلسلة
على أجهزة Android التي تعمل بالإصدار 10 من نظام التشغيل Android (المستوى 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()
. عند استخدام هذه الطريقة، ينتظر التنفيذ الإشارة إلى جميع الأحداث المرتبطة قبل بدء التقييم. بمجرد اكتمال التنفيذ وتكون النتائج جاهزة للاستخدام، تتم الإشارة إلى الحدث الذي تم عرضه.
استنادًا إلى الأجهزة التي ستتولى عملية التنفيذ، قد يتم دعم الحدث من خلال
سياج مزامنة. يجب
استدعاء
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 إلى تنفيذ وحدة المعالجة المركزية الخاصة به للعمليات أو أكثر.
إذا كان عميل NNAPI يحتوي على إصدارات محسَّنة من العملية (مثل TFLite)، قد يكون من المفيد إيقاف الإجراء الاحتياطي لوحدة المعالجة المركزية (CPU) ومعالجة الإخفاقات من خلال تنفيذ العملية المُحسَّنة للعميل.
في Android 10، إذا تم إجراء التحويل البرمجي باستخدام ANeuralNetworksCompilation_createForDevices
، سيتم إيقاف وحدة المعالجة المركزية (CPU) الاحتياطية.
في Android P، يعود تنفيذ NNAPI إلى وحدة المعالجة المركزية في حالة إخفاق التنفيذ على برنامج التشغيل.
وينطبق ذلك أيضًا على Android 10 عند استخدام ANeuralNetworksCompilation_create
بدلاً من ANeuralNetworksCompilation_createForDevices
.
يتم التراجع عن عملية التنفيذ الأولى لهذا القسم الفردي، وإذا استمرت المشكلة، تتم إعادة محاولة النموذج بأكمله على وحدة المعالجة المركزية (CPU).
إذا تعذّر التقسيم أو التجميع، ستتم تجربة النموذج بأكمله على وحدة المعالجة المركزية.
هناك حالات لا تتوفر فيها بعض العمليات على وحدة المعالجة المركزية (CPU)، وفي مثل هذه الحالات، يتعذّر التجميع أو التنفيذ بدلاً من التراجع عن العملية.
حتى بعد تعطيل وحدة المعالجة المركزية الاحتياطية، قد يكون هناك عمليات في النموذج تمت جدولتها على وحدة المعالجة المركزية. إذا كانت وحدة المعالجة المركزية (CPU) مُدرَجة في قائمة معالِجات البيانات المُقدَّمة إلى ANeuralNetworksCompilation_createForDevices
، وكانت هي المعالج الوحيد الذي يتوافق مع هذه العمليات أو المعالِج الذي يدّعي أفضل أداء لتلك العمليات، سيتم اختياره كمعالج أساسي (غير احتياطي).
لضمان عدم توفُّر عمليات تنفيذ لوحدة المعالجة المركزية (CPU)، استخدِم ANeuralNetworksCompilation_createForDevices
مع استبعاد nnapi-reference
من قائمة الأجهزة.
بدءًا من الإصدار Android P، يمكن إيقاف الإجراء الاحتياطي في وقت التنفيذ على
إصدارات "تصحيح الأخطاء" من خلال ضبط السمة debug.nn.partition
على 2.
نطاقات الذاكرة
في نظام التشغيل Android 11 والإصدارات الأحدث، تتوافق واجهة NNAPI مع نطاقات الذاكرة التي توفّر واجهات مُخصّصة للذكريات المبهَمة. ويتيح ذلك للتطبيقات تمرير ذاكرات تستند إلى الجهاز في جميع عمليات التنفيذ، كي لا تنسخ NNAPI البيانات أو تحوِّلها بدون داعٍ عند تنفيذ عمليات تنفيذ متتابعة على برنامج التشغيل نفسه.
تم تصميم ميزة مجال الذاكرة لأجهزة Tensor التي غالبًا ما تكون داخلية لبرنامج التشغيل ولا تحتاج إلى وصول متكرر إلى جانب العميل. وتشمل أمثلة هذه المضاعفات موترات الحالة في نماذج التسلسل. بالنسبة إلى وحدات التوليف التي تحتاج إلى الدخول المتكرر إلى وحدة المعالجة المركزية من جانب العميل، استخدم وحدات تجمع الذاكرة المشتركة بدلاً من ذلك.
لتخصيص ذاكرة معتمة، نفِّذ الخطوات التالية:
يمكنك استدعاء الدالة
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
إذا لم يكن برنامج التشغيل الأساسي متوافقًا معها. يتم تشجيع العميل على تنفيذ المنطق الاحتياطي من خلال تخصيص مورد احتياطي كبير بما يكفي يدعمه Ashmem أو وضع BLOB AHardwareBuffer
.
عندما لا تحتاج NNAPI إلى الوصول إلى كائن الذاكرة المعتمة، يمكنك تحرير مثيل ANeuralNetworksMemory
المقابل:
ANeuralNetworksMemory_free(opaqueMem);
قياس الأداء
يمكنك تقييم أداء تطبيقك عن طريق قياس وقت التنفيذ أو من خلال إنشاء ملفات تعريفية.
وقت التنفيذ
عندما تريد تحديد إجمالي وقت التنفيذ من خلال بيئة التشغيل، يمكنك استخدام واجهة برمجة التطبيقات الخاصة بالتنفيذ المتزامن وقياس الوقت الذي يستغرقه الاستدعاء. عندما تريد تحديد إجمالي وقت التنفيذ من خلال مستوى أدنى في حزمة البرامج، يمكنك استخدام ANeuralNetworksExecution_setMeasureTiming
وANeuralNetworksExecution_getDuration
للحصول على ما يلي:
- وقت التنفيذ على مسرِّع (وليس في برنامج التشغيل الذي يتم تشغيله على معالج المضيف).
- مدة التنفيذ في برنامج التشغيل، بما في ذلك الوقت المستغرَق في مسرِّعة الأعمال.
يستثني وقت التنفيذ في برنامج التشغيل النفقات العامة مثل وقت التشغيل نفسه وIPC اللازم لاتصال بيئة التشغيل ببرنامج التشغيل.
تقيس واجهات برمجة التطبيقات هذه المدة بين العمل المُرسَل وأحداث العمل المكتملة، بدلاً من الوقت الذي يخصّصه السائق أو مسرِّعة البيانات لتنفيذ الاستنتاج، وقد تتم مقاطعتها بتبديل السياق.
على سبيل المثال، إذا بدأ الاستنتاج 1، ثم يتوقف السائق عن العمل لتنفيذ الاستنتاج 2، ثم يستأنف العمل ويُكمل الاستنتاج 1، وسيشمل وقت تنفيذ الاستنتاج 1 الوقت الذي توقف فيه العمل لتنفيذ الاستنتاج 2.
قد تكون معلومات التوقيت هذه مفيدة في عملية نشر تطبيق من أجل جمع بيانات القياس عن بُعد للاستخدام بلا اتصال بالإنترنت. يمكنك استخدام بيانات التوقيت لتعديل التطبيق من أجل أداء أفضل.
عند استخدام هذه الوظيفة، يجب مراعاة ما يلي:
- قد تكون هناك تكلفة مرتبطة بجمع معلومات التوقيت.
- يستطيع السائق فقط حساب الوقت المستغرَق في نفسه أو في برنامج المسرّع، باستثناء الوقت المستغرَق في وقت تشغيل NNAPI وفي IPC.
- لا يمكنك استخدام واجهات برمجة التطبيقات هذه إلا مع
ANeuralNetworksExecution
الذي تم إنشاؤه باستخدامANeuralNetworksCompilation_createForDevices
باستخدامnumDevices = 1
. - لا يُطلب من السائقين الإبلاغ عن معلومات التوقيت.
إنشاء ملف شخصي على التطبيق باستخدام Android Systrace
بدءًا من نظام التشغيل Android 10، تنشئ NNAPI تلقائيًا أحداث systrace التي يمكنك استخدامها لتحليل تطبيقك.
يأتي مصدر NNAPI مزودًا بأداة parse_systrace
لمعالجة أحداث تتبُّع النظام التي ينشئها التطبيق وإنشاء عرض جدول يعرض الوقت المستغرَق في المراحل المختلفة من دورة حياة النموذج (الإنشاء الفوري
والإعداد
وتنفيذ عملية التجميع وإنهائه) وطبقات مختلفة من
التطبيقات. الطبقات التي ينقسم التطبيق فيها هي:
Application
: رمز التطبيق الرئيسيRuntime
: وقت تشغيل NNAPIIPC
: الاتصال البيني للعمليات بين "وقت تشغيل NNAPI" ورمز برنامج التشغيلDriver
: عملية تشغيل مسرِّعة الأعمال
إنشاء بيانات تحليل التوصيف
بافتراض أنك راجعت شجرة مصدر AOSP على $ANDROID_BUILD_TOP، وباستخدام مثال تصنيف الصور TFLite كتطبيق مستهدف، يمكنك إنشاء بيانات تحليل NNAPI باتّباع الخطوات التالية:
- يمكنك بدء تتبُّع النظام في 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
باسم العملية المحدّد في بيان التطبيق.
سيؤدي ذلك إلى إبقاء إحدى وحدات تحكم واجهة الأوامر مشغولة، ولا تُشغل الأمر في الخلفية لأنها تنتظر انتهاء enter
بشكل تفاعلي.
- بعد بدء أداة تجميع سجلات النظام، ابدأ تطبيقك وأجرِ اختبار قياس الأداء.
في هذه الحالة، يمكنك بدء تطبيق Image Classification من "استوديو Android" مباشرةً من واجهة مستخدم الهاتف التجريبي إذا كان التطبيق مُثبّتًا مسبقًا. لإنشاء بعض بيانات NNAPI، يجب إعداد التطبيق لاستخدام NNAPI من خلال اختيار NNAPI كجهاز مستهدف في مربّع حوار إعداد التطبيق.
عند اكتمال الاختبار، يمكنك إنهاء سجلّ النظام من خلال الضغط على
enter
في الوحدة الطرفية لوحدة التحكّم النشطة منذ الخطوة 1.شغّل الأداة المساعدة
systrace_parser
لإنشاء إحصاءات تراكمية:
$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html
يقبل المحلل اللغوي المعلَمات التالية:
- --total-times
: يعرض إجمالي الوقت المستغرَق في إحدى الطبقات بما في ذلك الوقت
المستغرق في انتظار تنفيذ استدعاء لطبقة أساسية
- --print-detail
: يطبع جميع الأحداث التي تمّ جمعها من النظام
- --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 API (بالنسبة إلى Java وللتطبيقات الأصلية ) مع أسماء الأحداث المخصّصة.
لربط الأحداث المخصّصة بمراحل نشاط التطبيق، أضِف اسم الحدث بإحدى السلاسل التالية:
[NN_LA_PI]
: حدث على مستوى التطبيق للإعداد[NN_LA_PP]
: حدث على مستوى التطبيق للاستعداد[NN_LA_PC]
: حدث على مستوى التطبيق في ميزة التجميع[NN_LA_PE]
: الحدث على مستوى التطبيق للتنفيذ
إليك مثال على كيفية تغيير رمز نموذج تصنيف صور TFLite من خلال إضافة قسم runInferenceModel
لمرحلة Execution
والطبقة Application
التي تحتوي على أقسام preprocessBitmap
أخرى لن يتم تضمينها في عمليات تتبُّع NNAPI. سيكون القسم runInferenceModel
جزءًا من أحداث تتبُّع النظام التي يعالجها محلّل تتبُّع النظام nnapi:
Kotlin
/** Runs inference and returns the classification results. */ fun recognizeImage(bitmap: Bitmap): List{ // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap") convertBitmapToByteBuffer(bitmap) Trace.endSection() // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel") long startTime = SystemClock.uptimeMillis() runInference() long endTime = SystemClock.uptimeMillis() Trace.endSection() ... return recognitions }
Java
/** Runs inference and returns the classification results. */ public ListrecognizeImage(final Bitmap bitmap) { // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap"); convertBitmapToByteBuffer(bitmap); Trace.endSection(); // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel"); long startTime = SystemClock.uptimeMillis(); runInference(); long endTime = SystemClock.uptimeMillis(); Trace.endSection(); ... Trace.endSection(); return recognitions; }
جودة الخدمة
في نظام التشغيل Android 11 والإصدارات الأحدث، توفّر NNAPI جودة أفضل للخدمة (QoS) من خلال السماح للتطبيق بالإشارة إلى الأولويات النسبية لنماذجه، والحد الأقصى للوقت المتوقع لإعداد نموذج معيّن، والحد الأقصى للوقت المتوقع لإكمال عملية حسابية معيّنة. يقدِّم Android 11 أيضًا رموز نتائج إضافية من NNAPI تتيح للتطبيقات إمكانية التعرّف على حالات الإخفاق، مثل الفائتة في المواعيد النهائية.
تحديد أولوية عبء العمل
لضبط أولوية أعباء عمل NNAPI، يمكنك طلب ANeuralNetworksCompilation_setPriority()
قبل الاتصال ANeuralNetworksCompilation_finish()
.
تحديد المواعيد النهائية
يمكن للتطبيقات تحديد مواعيد نهائية لكلٍ من تجميع النماذج والاستنتاج.
- لضبط مهلة التجميع، يمكنك استدعاء
ANeuralNetworksCompilation_setTimeout()
قبل استدعاءANeuralNetworksCompilation_finish()
. - لضبط مهلة الاستنتاج، يمكنك استدعاء
ANeuralNetworksExecution_setTimeout()
قبل بدء التجميع.
مزيد من المعلومات عن المعاملات
يتناول القسم التالي مواضيع متقدمة حول استخدام المعاملات.
نقاط التوتر الكمّية
يعتبر المتنسور الكمي طريقة مضغوطة لتمثيل صفيفة الأبعاد النائية لقيم النقاط العائمة.
يدعم NNAPI عوامل العشرات الكميّة غير المتماثلة 8 بت. بالنسبة لهذه المؤشرات، يتم تمثيل قيمة كل خلية بعدد صحيح 8 بت. المرتبط بالمتسلل هو مقياس وقيمة نقطة صفرية. وتُستخدم لتحويل الأعداد الصحيحة 8 بت إلى قيم النقطة العائمة التي يتم تمثيلها.
المعادلة هي:
(cellValue - zeroPoint) * scale
حيث تكون قيمة zeroPoint هي عدد صحيح 32 بت والمقياس كقيمة نقطة عائمة 32 بت.
بالمقارنة مع متسابقات قيم النقاط العائمة 32 بت، فإن للعشرات الكمي 8 بت ميزتان:
- تطبيقك أصغر، حيث أن الأوزان المدرَّبة تستغرق ربع حجم موتّرات 32 بت.
- غالبًا ما يتم تنفيذ العمليات الحسابية بشكلٍ أسرع. ويرجع ذلك إلى صغر حجم البيانات التي يجب جلبها من الذاكرة وكفاءة المعالِجات، مثل الأنظمة الأساسية الرقمية (DSP)، في إجراء العمليات الحسابية للأعداد الصحيحة.
على الرغم من إمكانية تحويل نموذج النقطة العائمة إلى نموذج كَمي، أثبتت تجربتنا أنّه يمكن تحقيق نتائج أفضل من خلال تدريب نموذج كمي مباشرةً. وفي الواقع، تتعلم الشبكة العصبية التعويض عن الدقة المتزايدة لكل قيمة. لكل متوتر كمي، يتم تحديد المقياس وقيم الصفر أثناء عملية التطبيق.
في NNAPI، يمكنك تحديد أنواع موتّرات كمية عبر ضبط حقل النوع لبنية بيانات ANeuralNetworksOperandType
على ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
.
يمكنك أيضًا تحديد المقياس وقيمة نقطة الصفر للمتسابق في هيكل البيانات هذا.
بالإضافة إلى مأخذ القوة غير المتماثلة 8 بت، تدعم NNAPI ما يلي:
ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
الذي يمكنك استخدامه لتمثيل الترجيحات في عملياتCONV/DEPTHWISE_CONV/TRANSPOSED_CONV
.ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
الذي يمكنك استخدامه لحالةQUANTIZED_16BIT_LSTM
الداخلية.ANEURALNETWORKS_TENSOR_QUANT8_SYMM
الذي يمكن أن يكون إدخالاً فيANEURALNETWORKS_DEQUANTIZE
.
المعاملات الاختيارية
تتخذ بعض العمليات، مثل
ANEURALNETWORKS_LSH_PROJECTION
،
معاملات اختيارية. للإشارة في النموذج إلى أنّه تم حذف المعامل الاختياري،
يمكنك استدعاء الدالة
ANeuralNetworksModel_setOperandValue()
مع ضبط NULL
للمخزن المؤقت و0 للطول.
إذا كان القرار بشأن ما إذا كان المعامل موجودًا أم لا يختلف حسب كل
تنفيذ، تشير إلى أنه تم حذف المعامل باستخدام
الدالتين
ANeuralNetworksExecution_setInput()
أو
ANeuralNetworksExecution_setOutput()
، مع تمرير NULL
للمخزن المؤقت و0 للطول.
قيم ترتيب غير معروف
قدّم Android 9 (مستوى واجهة برمجة التطبيقات 28) معاملات النماذج ذات الأبعاد غير المعروفة، ولكنّها ترتيب معروف (عدد الأبعاد). يقدّم Android 10 (المستوى 29 من واجهة برمجة التطبيقات) عوامل تشغيل ذات ترتيب غير معروف، كما هو موضَّح في ANeral NetworksOperandType.
مقياس أداء NNAPI
يتوفّر مقياس أداء NNAPI على AOSP في platform/test/mlts/benchmark
(تطبيق قياس الأداء) وplatform/test/mlts/models
(النماذج ومجموعات البيانات).
يقيّم مقياس الأداء وقت الاستجابة والدقة ويقارن برامج التشغيل بالعمل نفسه الذي تم إجراؤه باستخدام Tensorflow Lite التي تعمل على وحدة المعالجة المركزية (CPU) للنماذج ومجموعات البيانات نفسها.
لاستخدام مقياس الأداء، عليك اتّباع الخطوات التالية:
وصِّل جهاز Android مستهدَف بجهاز الكمبيوتر وافتح نافذة طرفية وتأكَّد من إمكانية الوصول إلى الجهاز من خلال adb.
في حال توصيل أكثر من جهاز Android، يمكنك تصدير متغيّر بيئة
ANDROID_SERIAL
للجهاز المستهدَف.انتقِل إلى دليل المصدر العالي المستوى على Android.
شغِّل الأوامر التالية:
lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available ./test/mlts/benchmark/build_and_run_benchmark.sh
في نهاية عملية قياس الأداء، سيتم عرض نتائجها على شكل صفحة HTML تم تمريرها إلى
xdg-open
.
سجلات NNAPI
تنشئ واجهة NNAPI معلومات تشخيصية مفيدة في سجلات النظام. لتحليل السجلات، استخدِم الأداة logcat.
يمكنك تفعيل التسجيل المطوَّل لـ NNAPI لمراحل أو مكونات معيّنة من خلال ضبط السمة debug.nn.vlog
(باستخدام adb shell
) على قائمة القيم التالية، مفصولة بمسافة أو نقطتين أو فاصلة:
model
: إنشاء نموذجcompilation
: إنشاء خطة تنفيذ النموذج وتجميعهexecution
: تنفيذ النموذجcpuexe
: تنفيذ العمليات باستخدام تنفيذ وحدة المعالجة المركزية (CPU) فئة NNAPImanager
: إضافات NNAPI والواجهات المتاحة ومعلومات حول الإمكانات ذات الصلةall
أو1
: كل العناصر المذكورة أعلاه
على سبيل المثال، لتفعيل التسجيل المطوَّل بالكامل، استخدِم الأمر
adb shell setprop debug.nn.vlog all
. لإيقاف التسجيل المطوَّل، استخدِم الأمر
adb shell setprop debug.nn.vlog '""'
.
وبعد تفعيل التسجيل المطوَّل، يُنشِئ التسجيل المطوَّل إدخالات السجلّ على مستوى "معلومات" (INFO) مع ضبط علامة على اسم المرحلة أو المكوِّن.
إلى جانب الرسائل التي يتم التحكّم فيها من خلال debug.nn.vlog
، توفّر مكونات واجهة برمجة التطبيقات NNAPI إدخالات أخرى للسجلّ على مستويات مختلفة، يستخدم كل منها علامة سجلّ محددة.
للحصول على قائمة بالمكوّنات، ابحث في شجرة المصدر باستخدام التعبير التالي:
grep -R 'define LOG_TAG' | awk -F '"' '{print $2}' | sort -u | egrep -v "Sample|FileTag|test"
يعرض هذا التعبير حاليًا العلامات التالية:
- أداة إنشاء الصور المصغّرة
- طلبات معاودة الاتصال
- أداة إنشاء التحويل البرمجي
- وحدة المعالجة المركزية (CPU)
- أداة إنشاء التنفيذ
- وحدة التحكّم التنفيذية
- خادم ExecutionBurstServer
- خطة التنفيذ
- سائق فيبوناتشي
- تفريغ الرسم البياني
- ملف IndexedShapeWrapper
- ساعة IonWatcher
- مدير
- الذاكرة
- أدوات الذاكرة
- نموذج وصفي
- معلومات النموذج
- مصمم النماذج
- الشبكات العصبية
- أداة حلّ العمليات
- العمليات
- عمليات التشغيل
- معلومات الحزمة
- رمز TokenHasher
- مدير الأنواع
- أدوات الاستخدام
- التحقق من الصحة
- واجهات ذات إصدارات
للتحكّم في مستوى رسائل السجلّ التي يعرضها 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
لإنشاء معلومات سجلّ مطوَّل.