Android Neural Networks API (NNAPI) هي واجهة برمجة تطبيقات C لنظام التشغيل Android مصمّمة لتنفيذ عمليات تتعلّق بالذكاء الاصطناعي على أجهزة Android، وهي عمليات تتطلّب معالجة مكثفة. تم تصميم NNAPI لتوفير طبقة أساسية من الوظائف لأُطر عمل تعلُّم الآلة ذات المستوى الأعلى، مثل TensorFlow Lite وCaffe2، اللتين تُنشئان الشبكات العصبية وتدرّبانها. تتوفّر واجهة برمجة التطبيقات على جميع أجهزة Android التي تعمل بالإصدار 8.1 (المستوى 27 من واجهة برمجة التطبيقات) أو إصدار أحدث.
يتيح NNAPI الاستنتاج من خلال تطبيق البيانات من أجهزة Android على نماذج تم تدريبها سابقًا ويحدّدها المطوّر. تشمل أمثلة الاستنتاج تصنيف الصور وتوقّع سلوك المستخدِم واختيار الردود المناسبة لطلب بحث.
توفّر الاستنتاج على الجهاز العديد من المزايا:
- وقت الاستجابة: لا تحتاج إلى إرسال طلب عبر اتصال بالشبكة ثم انتظار الردّ. على سبيل المثال، يمكن أن يكون ذلك مهمًا لتطبيقات الفيديو التي تعالج اللقطات المتتالية الواردة من الكاميرا.
- التوفّر: يعمل التطبيق حتى في حال عدم توفّر تغطية شبكة.
- السرعة: الأجهزة الجديدة المخصّصة لمعالجة الشبكات العصبية توفّر عمليات حسابية أسرع بكثير من وحدة المعالجة المركزية (CPU) المخصّصة للأغراض العامة وحدها.
- الخصوصية: لا يتم نقل البيانات خارج جهاز Android.
- التكلفة: لا حاجة إلى مزرعة خوادم عند إجراء جميع العمليات الحسابية على جهاز Android.
هناك أيضًا بعض السلبيات التي يجب أن يأخذها المطوّر في الاعتبار:
- استخدام النظام: يتطلّب تقييم الشبكات العصبية إجراء الكثير من العمليات الحسابية، ما قد يؤدي إلى زيادة استهلاك طاقة البطارية. يجب مراعاة مراقبة حالة البطارية إذا كان ذلك يشكّل مصدر قلق لتطبيقك، خاصةً في العمليات الحسابية التي تستغرق وقتًا طويلاً.
- حجم التطبيق: انتبه إلى حجم النماذج. قد تستهلك النماذج مساحة تبلغ عدة ميغابايت. إذا كان تجميع نماذج كبيرة في حِزمة APK سيؤثّر بشكلٍ غير ملائم في المستخدمين، ننصحك بتنزيل نماذج بعد تثبيت التطبيق أو استخدام نماذج أصغر أو تنفيذ العمليات الحسابية في السحابة الإلكترونية. لا يوفّر NNAPI وظيفة لتشغيل نماذج في السحابة الإلكترونية.
اطّلِع على نموذج Android Neural Networks API للاطّلاع على مثال واحد على كيفية استخدام NNAPI.
فهم وقت تشغيل Neural Networks API
تم تصميم NNAPI ليتم استدعاؤه من خلال مكتبات وإطارات عمل وأدوات تعلُّم الآلة التي تتيح للمطوّرين تدريب نماذجهم خارج الجهاز ونشرها على أجهزة Android. لا تستخدم التطبيقات عادةً NNAPI مباشرةً، ولكن بدلاً من ذلك، تستخدم إطارات عمل تعلُّم الآلة ذات المستوى الأعلى. ويمكن أن تستخدم هذه الأطر بدورها NNAPI لتنفيذ عمليات الاستنتاج المستندة إلى الأجهزة على الأجهزة المتوافقة.
استنادًا إلى متطلبات التطبيق وإمكانات الأجهزة على جهاز Android، يمكن لوقت تشغيل الشبكة العصبية في Android توزيع حمولة الحوسبة بكفاءة على المعالجات المتاحة على الجهاز، بما في ذلك الأجهزة المخصّصة للشبكة العصبية ووحدات معالجة الرسومات ومعالجات الإشارات الرقمية.
بالنسبة إلى أجهزة Android التي لا تتضمّن برنامج تشغيل مخصّصًا من المورّد، ينفِّذ وقت تشغيل NNAPI الطلبات على وحدة المعالجة المركزية.
يعرض الشكل 1 بنية النظام العالية المستوى لواجهة برمجة التطبيقات NNAPI.
نموذج برمجة واجهة برمجة التطبيقات للشبكات العصبية
لإجراء العمليات الحسابية باستخدام NNAPI، عليك أولاً إنشاء gráph موجه يحدِّد العمليات الحسابية التي يجب إجراؤها. يشكّل هذا الرسم البياني للحساب، مع بيانات الإدخال (مثل الأوزان والانحيازات التي تم تمريرها من إطار عمل تعلُّم الآلة)، نموذج تقييم وقت التشغيل في NNAPI.
يستخدم NNAPI أربعة عناصر مجردة رئيسية:
- النموذج: رسم بياني للعمليات الحسابية للعمليات الحسابية والقيمة
الثابتة التي تم تعلّمها من خلال عملية تدريب هذه العمليات خاصة بالشبكة
العصبية. وتشمل هذه العمليات التفاف ثنائي الأبعاد
والتفعيل اللوجستي
(sigmoid)
والتفعيل الخطي المصحَّح
(ReLU) وغيرها. إنّ إنشاء نموذج هو عملية متزامنة.
بعد إنشاء النموذج بنجاح، يمكن إعادة استخدامه في جميع المواضيع والمجموعات.
في NNAPI، يتم تمثيل النموذج كمثيل
ANeuralNetworksModel
. - التجميع: يمثّل إعدادًا لتجميع نموذج NNAPI في код
أدنى مستوى. إنّ إنشاء مجموعة اختبارات هو عملية متزامنة. بعد
إنشاءها بنجاح، يمكن إعادة استخدامها في جميع سلاسل المحادثات وعمليات التنفيذ. في
NNAPI، يتم تمثيل كل عملية تجميع كمثيل
ANeuralNetworksCompilation
. - الذاكرة: تمثّل الذاكرة المشتركة والملفات المُعرَّفة بالذاكرة ومستودعات الذاكرة
المشابهة. يتيح استخدام ذاكرة تخزين مؤقت لنقل البيانات إلى برامج التشغيل في وقت تشغيل NNAPI
بكفاءة أكبر. ينشئ التطبيق عادةً وحدة تخزين مؤقتة واحدة للذاكرة المشتركة تتضمن كل مصفوفة تانسور مطلوبة لتحديد نموذج. يمكنك أيضًا استخدام ملف ذاكرة
المخازن المؤقتة لتخزين المدخلات والمخرجات لمثيل التنفيذ. في NNAPI، يتم تمثيل كل وحدة تخزين مؤقت للذاكرة على أنّها مثيل
ANeuralNetworksMemory
. التنفيذ: واجهة لتطبيق نموذج NNAPI على مجموعة من المدخلات وجمع النتائج يمكن تنفيذ الإجراء بشكل متزامن أو غير متزامن.
بالنسبة إلى التنفيذ غير المتزامن، يمكن لعدة سلاسل مهام الانتظار إلى أن يكتمل التنفيذ نفسه. عند اكتمال هذا التنفيذ، يتم إلغاء جميع سلاسل التعليمات.
في NNAPI، يتم تمثيل كل عملية تنفيذ على أنّها مثيل
ANeuralNetworksExecution
.
يعرض الشكل 2 عملية البرمجة الأساسية.
يوضّح الجزء المتبقّي من هذا القسم خطوات إعداد نموذج 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. يتم تعريف كل نموذج بoperand واحد أو أكثر وعمليات واحدة أو أكثر.
المَعلمات
عناصر التشغيل هي عناصر بيانات تُستخدَم في تحديد الرسم البياني. وتشمل هذه المدخلات والمخرجات للنموذج، والعقد الوسيطة التي تحتوي على البيانات التي تتدفق من عملية إلى أخرى، والثوابت التي يتم تمريرها إلى هذه العمليات.
هناك نوعان من المَعلمات التي يمكن إضافتها إلى نماذج NNAPI: المَعلمات السلعية و المَعلمات المصفوفية.
يمثّل المتغيّر السلعي قيمة واحدة. يتوافق NNAPI مع القيم السلاسل في التنسيقات المنطقية والتنسيقات العشرية ذات النقطة العائمة بسعة 16 بت و32 بت والتنسيقات الصحيحة للأعداد الصحيحة بسعة 32 بت.
تتضمن معظم العمليات في NNAPI مصفوفات تانسور. مصفوفات Tensors هي صفائف من n بُعد. يتوافق NNAPI مع مصفوفات السلاسل المتسلسلة ذات النقطة العائمة بسعة 16 بت و32 بت و8 بت المقسَّمة و16 بت مقسَّمة و32 بت عدد صحيح و8 بت قيم منطقية.
على سبيل المثال، يمثّل الشكل 3 نموذجًا يتضمّن عمليتين: عملية جمع followed by a multiplication. يأخذ النموذج مصفوفة إدخال وينتج عنها مصفوفة إخراج واحدة.
يحتوي النموذج أعلاه على سبعة عوامل تشغيل. ويتم تحديد هذه المُعامِلات بشكل ضمني من خلال فهرس الترتيب الذي تتم إضافتها به إلى النموذج. يكون للمَعلمة الأولى التي تتم إضافتها فهرس 0، وللمَعلمة الثانية فهرس 1، وهكذا. المعاملات 1 و2 و3 و5 هي معاملات ثابتة.
ولا يهمّ الترتيب الذي تضيف به المُعامِلات. على سبيل المثال، يمكن أن يكون عامل ناتج النموذج هو العامل الأول الذي تمت إضافته. الجزء المهم هو استخدام قيمة الفهرس الصحيحة عند الإشارة إلى عامل تشغيل.
للمعاملات أنواع. ويتم تحديدها عند إضافتها إلى النموذج.
لا يمكن استخدام عامل تشغيل كإدخال وإخراج نموذج في آنٍ واحد.
يجب أن يكون كلّ عامل تشغيل إمّا إدخال نموذج أو ثابتًا أو عامل تشغيل ناتجًا عن عملية واحدة بالضبط.
لمزيد من المعلومات عن استخدام المَعلمات، اطّلِع على مزيد من المعلومات عن المَعلمات.
العمليات
تحدِّد العملية العمليات الحسابية التي سيتم إجراؤها. تتألّف كل عملية من العناصر التالية:
- نوع العملية (مثل الجمع أو الضرب أو الالتفاف)
- قائمة بمؤشرات المعاملات التي تستخدمها العملية كمدخلات
- قائمة بمؤشرات المعاملات التي تستخدمها العملية للإخراج
يُرجى العِلم أنّ الترتيب في هذه القوائم مهم، ويمكنك الاطّلاع على مرجع واجهة برمجة التطبيقات NNAPI للاطّلاع على الإدخالات والمخرجات المتوقّعة لكل نوع من أنواع العمليات.
يجب إضافة المَعلمات التي تستهلكها العملية أو تنتجها إلى النموذج قبل إضافة العملية.
ولا يهمّ الترتيب الذي تُضيف به العمليات. تعتمد NNAPI على التبعيات التي يحدّدها الرسم البياني الحسابي للمَعلمات والعمليات لتحديد ترتيب تنفيذ العمليات.
في الجدول التالي، تم تلخيص العمليات التي تتوافق مع NNAPI:
مشكلة معروفة في المستوى 28 لواجهة برمجة التطبيقات: عند تمرير مكونات
ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
tensor إلى عملية
ANEURALNETWORKS_PAD
المتوفّرة على Android 9 (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأحدث، قد لا يتطابق المخرج
من NNAPI مع المخرج من إطارات عمل
تعلُّم الآلة ذات المستوى الأعلى، مثل
TensorFlow Lite. وبدلاً من ذلك، يجب تمرير
ANEURALNETWORKS_TENSOR_FLOAT32
فقط.
تم حلّ هذه المشكلة في الإصدار 10 من نظام التشغيل Android (المستوى 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
باستخدام نطاق أو دقة منخفضة مثل التنسيق المتغير النقطتين العشريتين بسعة 16 بت IEEE 754 من خلال استدعاءANeuralNetworksModel_relaxComputationFloat32toFloat16()
.يُرجى الاتصال على
ANeuralNetworksModel_finish()
لإكمال تعريف النموذج. في حال عدم حدوث أي أخطاء، تعرض هذه الدالة رمز النتيجةANEURALNETWORKS_NO_ERROR
.ANeuralNetworksModel_finish(model);
بعد إنشاء نموذج، يمكنك تجميعه أي عدد من المرات وتنفيذ كل عملية compiling أي عدد من المرات.
التحكّم في التدفق
لدمج تدفّق التحكّم في نموذج 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 (المستوى 29 لواجهة برمجة التطبيقات) والإصدارات الأحدث، توفّر واجهة برمجة التطبيقات NNAPI وظائف تسمح لتطبيقات ومكتبات إطار عمل تعلُّم الآلة بالحصول على معلومات عن الأجهزة المتاحة وتحديد الأجهزة التي سيتم استخدامها لتنفيذ العمليات. من خلال تقديم معلومات عن الأجهزة المتاحة، يمكن للتطبيقات الحصول على الإصدار الدقيق لبرامج التشغيل المتوفّرة على الجهاز لتجنُّب عدم التوافق المعروف. من خلال منح التطبيقات إمكانية تحديد الأجهزة التي ستبدأ في تنفيذ أقسام مختلفة من النموذج، يمكن تحسين التطبيقات لجهاز Android الذي يتم نشرها عليه.
رصد الأجهزة
استخدِم رمز
ANeuralNetworks_getDeviceCount
للحصول على عدد الأجهزة المتاحة. بالنسبة إلى كل جهاز، استخدِم
ANeuralNetworks_getDevice
لضبط مثيل ANeuralNetworksDevice
على إشارة إلى هذا الجهاز.
بعد الحصول على مرجع الجهاز، يمكنك الاطّلاع على معلومات إضافية عن هذا الجهاز باستخدام الدوالّ التالية:
ANeuralNetworksDevice_getFeatureLevel
ANeuralNetworksDevice_getName
ANeuralNetworksDevice_getType
ANeuralNetworksDevice_getVersion
تعيين الجهاز
استخدِم رمز
ANeuralNetworksModel_getSupportedOperationsForDevices
لاكتشاف العمليات التي يمكن تنفيذها في نموذج معيّن على أجهزة محدّدة.
للتحكّم في مسرعات التنفيذ التي تريد استخدامها، استخدِم
ANeuralNetworksCompilation_createForDevices
بدلاً من ANeuralNetworksCompilation_create
.
استخدِم عنصر ANeuralNetworksCompilation
الناتج كالمعتاد.
تعرض الدالة خطأ إذا كان النموذج المقدَّم يحتوي على عمليات
غير متوافقة مع الأجهزة المحدّدة.
في حال تحديد أجهزة متعددة، يكون وقت التشغيل مسؤولاً عن توزيع العمل على الأجهزة.
على غرار الأجهزة الأخرى، يتم تمثيل تنفيذ وحدة المعالجة المركزية لـ NNAPI باستخدام ملف
ANeuralNetworksDevice
يحمل الاسم nnapi-reference
والنوع
ANEURALNETWORKS_DEVICE_TYPE_CPU
. عند استدعاء
ANeuralNetworksCompilation_createForDevices
، لا يتم استخدام تنفيذ وحدة المعالجة المركزية
للتعامل مع حالات الفشل في تجميع النموذج وتنفيذه.
تقع على عاتق التطبيق مسؤولية تقسيم النموذج إلى نماذج فرعية يمكن
تشغيلها على الأجهزة المحدّدة. بالنسبة إلى التطبيقات التي لا تحتاج إلى مشاركة ملف التمهيد يدويًا، يجب أن تستمر في طلب ANeuralNetworksCompilation_create
البسيط لاستخدام جميع الأجهزة المتاحة (بما في ذلك وحدة المعالجة المركزية) لتسريع ANeuralNetworksCompilation_create
النموذج. إذا تعذّر توافق النموذج بالكامل مع الأجهزة التي حدّدتها
باستخدام ANeuralNetworksCompilation_createForDevices
،
ANEURALNETWORKS_BAD_DATA
يتم عرض القيمة التالية:
تقسيم النماذج
عندما تتوفّر أجهزة متعددة للنموذج، يوزع وقت تشغيل NNAPI
العمل على الأجهزة. على سبيل المثال، إذا تم توفير أكثر من جهاز واحد
لـ ANeuralNetworksCompilation_createForDevices
، سيتم أخذ جميع
الأجهزة المحدّدة في الاعتبار عند تخصيص العمل. يُرجى العِلم أنّه في حال عدم إدراج وحدة المعالجة المركزية
في القائمة، سيتم إيقاف تنفيذ وحدة المعالجة المركزية. عند استخدام ANeuralNetworksCompilation_create
، سيتم أخذ جميع الأجهزة المتاحة في الاعتبار، بما في ذلك وحدة المعالجة المركزية.
يتم التوزيع من خلال اختيار الجهاز المتوافق مع العملية من قائمة الأجهزة المتاحة لكل
من العمليات في النموذج، ثم تحديد أفضل أداء، أي أسرع وقت تنفيذ أو
أدنى استهلاك للطاقة، استنادًا إلى الإعدادات المفضّلة للتنفيذ التي يحدّدها العميل. لا تراعي خوارزمية التقسيم هذه
الاختلالات المحتملة الناتجة عن عمليات الإدخال/الإخراج بين المعالجات المختلفة، لذا عند تحديدمعالجات متعددة (إما بشكل صريح عند استخدام
ANeuralNetworksCompilation_createForDevices
أو بشكل ضمني باستخدام
ANeuralNetworksCompilation_create
)، من المهم إنشاء ملف شخصي للتطبيق الناتج.
لفهم كيفية تقسيم النموذج بواسطة NNAPI، راجِعملفّات log في Android بحثًا عن رسالة (على مستوى INFO باستخدام العلامة ExecutionPlan
):
ModelBuilder::findBestDeviceForEachOperation(op-name): device-index
op-name
هو الاسم الوصفي للعملية في الرسم البياني، وdevice-index
هو فهرس الجهاز المعنيّ في قائمة الأجهزة.
هذه القائمة هي الإدخال المقدَّم إلى ANeuralNetworksCompilation_createForDevices
أو، في حال استخدام ANeuralNetworksCompilation_createForDevices
، قائمة الأجهزة
المعروضة عند تكرار جميع الأجهزة باستخدام ANeuralNetworks_getDeviceCount
و
ANeuralNetworks_getDevice
.
الرسالة (على مستوى INFO مع العلامة ExecutionPlan
):
ModelBuilder::partitionTheWork: only one best device: device-name
تشير هذه الرسالة إلى أنّه تم تسريع الرسم البياني بالكامل على الجهاز
device-name
.
التنفيذ
تطبِّق خطوة التنفيذ النموذج على مجموعة من المدخلات وتخزِّن مخرجات العمليات الحسابية في مساحة تخزين مؤقت أو مساحة ذاكرة واحدة أو أكثر للمستخدمين خصصها تطبيقك.
لتنفيذ نموذج مجمَّع، اتّبِع الخطوات التالية:
استخدِم الدالة
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 ميكرو ثانية بين وقت إرسال إشعار إلى سلسلة مهام أو بدء معالجة سلسلة مهام ووقت ربطها أخيرًا بأحد نوى وحدة المعالجة المركزية.
لتحسين وقت الاستجابة، يمكنك بدلاً من ذلك توجيه تطبيق لإجراء مكالمة تحليل تزامنية
إلى وقت التشغيل. ولن يتم عرض هذا الطلب إلا بعد اكتمال عملية الاستنتاج بدلاً من عرضه بعد بدء عملية الاستنتاج. بدلاً من memanggil
ANeuralNetworksExecution_startCompute
لإجراء مكالمة استنتاج غير متزامنة مع وقت التشغيل، يُجري التطبيق مكالمة
ANeuralNetworksExecution_compute
لإجراء مكالمة متزامنة مع وقت التشغيل. لا تتطلب المكالمة إلى
ANeuralNetworksExecution_compute
استخدام ANeuralNetworksEvent
ولا تتم إقرانها بمكالمة إلى ANeuralNetworksEvent_wait
.
عمليات التنفيذ المكثّفة
على أجهزة Android التي تعمل بالإصدار 10 (المستوى 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);
قوائم انتظار الأوامر غير المتزامنة والتنفيذ المحدود
في الإصدار 11 من Android والإصدارات الأحدث، تتيح مكتبة 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 إلى تنفيذ وحدة المعالجة المركزية (CPU) لعملية واحدة أو أكثر من العمليات.
إذا كان برنامج NNAPI العميل يتضمّن إصدارات محسّنة من العملية (مثل TFLite)، قد يكون من المفيد إيقاف الإجراء الاحتياطي لوحدة المعالجة المركزية ومعالجة حالات الفشل من خلال تنفيذ العملية المحسّنة للعميل.
في Android 10، إذا تم إجراء عملية الترجمة باستخدام
ANeuralNetworksCompilation_createForDevices
، سيتم إيقاف الإجراء الاحتياطي لوحدة المعالجة المركزية.
في نظام التشغيل Android P، يتم تنفيذ NNAPI على وحدة المعالجة المركزية في حال تعذّر التنفيذ على برنامج التشغيل.
وينطبق ذلك أيضًا على نظام التشغيل Android 10 عند استخدام ANeuralNetworksCompilation_create
بدلاً من
ANeuralNetworksCompilation_createForDevices
.
في حال تعذّر التنفيذ الأول، يتم الرجوع إلى هذا القسم الفردي، وإذا استمرت المشكلة، تتم إعادة محاولة تنفيذ النموذج بأكمله على وحدة المعالجة المركزية.
إذا تعذّر التقسيم أو الترجمة، سيتمّ اختبار النموذج بأكمله على وحدة المعالجة المركزية.
في بعض الحالات، لا تكون بعض العمليات متوافقة مع وحدة المعالجة المركزية، وفي مثل هذه الحالات، سيتعذّر التجميع أو التنفيذ بدلاً من الرجوع إلى الإجراء البديل.
حتى بعد إيقاف وضع "الرجوع إلى وحدة المعالجة المركزية"، قد تظل هناك عمليات في النموذج
مُجدوَلة على وحدة المعالجة المركزية. إذا كانت وحدة المعالجة المركزية مضمّنة في قائمة المعالجات التي تم إرسالها
إلى ANeuralNetworksCompilation_createForDevices
، وكانت إما المعالج الوحيد
الذي يتيح تنفيذ هذه العمليات أو المعالج الذي يُزعَم أنّه يقدّم أفضل
أداء لهذه العمليات، سيتم اختياره كمنفّذ أساسي (غير احتياطي).
لضمان عدم تنفيذ وحدة المعالجة المركزية، استخدِم ANeuralNetworksCompilation_createForDevices
مع استبعاد nnapi-reference
من قائمة الأجهزة.
بدءًا من Android P، أصبح من الممكن إيقاف الإجراء الاحتياطي في وقت التنفيذ على ملفّات برمجية قيد التطوير من خلال ضبط القيمة debug.nn.partition
على 2.
نطاقات الذكريات
في الإصدار 11 من نظام التشغيل Android والإصدارات الأحدث، تتيح واجهة برمجة التطبيقات 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
إذا لم يكن ذلك متوافقًا مع
البرنامج الأساسي. ننصح العميل بتنفيذ منطق التراجع عن طريق تخصيص ملف احتياطي
كبير بما يكفي يستند إلى Ashmem أو وضع BLOB AHardwareBuffer
.
عندما لا يحتاج NNAPI إلى الوصول إلى عنصر الذاكرة غير الشفاف، يجب تحرير
مثيل ANeuralNetworksMemory
المقابل:
ANeuralNetworksMemory_free(opaqueMem);
قياس الأداء
يمكنك تقييم أداء تطبيقك من خلال قياس وقت التنفيذ أو من خلال التحليل.
وقت التنفيذ
عندما تريد تحديد إجمالي وقت التنفيذ خلال وقت التشغيل، يمكنك استخدام
واجهة برمجة التطبيقات للتنفيذ المتزامن وقياس الوقت الذي يستغرقه الطلب. عندما تريد معرفة إجمالي وقت التنفيذ من خلال مستوى أدنى من بنية البرنامج، يمكنك استخدام ANeuralNetworksExecution_setMeasureTiming
وANeuralNetworksExecution_getDuration
للحصول على ما يلي:
- وقت التنفيذ على مسرع (وليس في برنامج التشغيل الذي يعمل على معالج المضيف ).
- وقت التنفيذ في برنامج التشغيل، بما في ذلك الوقت المستغرق في المسرّع
يستبعد وقت التنفيذ في برنامج التشغيل الوقت المستغرَق في عمليات الدعم، مثل وقت بيئة التشغيل نفسه ووقت واجهة برمجة التطبيقات (IPC) اللازمة لبيئة التشغيل للتواصل مع برنامج التشغيل.
تقيس واجهات برمجة التطبيقات هذه المدة بين حدثَي إرسال العمل وإكماله، بدلاً من الوقت الذي يخصصه برنامج التشغيل أو المسرِّع لإجراء المعالجة، والذي قد يتخلّله تبديل السياق.
على سبيل المثال، إذا بدأ الاستنتاج 1، يوقف السائق العمل لتنفيذ الاستنتاج 2، ثم يستأنف الاستنتاج 1 ويكمله، وسيتضمّن وقت التنفيذ للاستنتاج 1 الوقت الذي تم فيه إيقاف العمل لتنفيذ الاستنتاج 2.
قد تكون معلومات التوقيت هذه مفيدة عند نشر أحد التطبيقات في قناة الإصدار العلني لجمع بيانات القياس عن بُعد لاستخدامها بلا إنترنت. يمكنك استخدام بيانات التوقيت لتعديل التطبيق وتحسين أدائه.
عند استخدام هذه الوظيفة، يُرجى مراعاة ما يلي:
- قد يؤدي جمع معلومات التوقيت إلى التأثير سلبًا في الأداء.
- لا يمكن إلا لبرنامج التشغيل احتساب الوقت الذي يقضيه في نفسه أو في المسرِّع، باستثناء الوقت الذي يقضيه في وقت تشغيل NNAPI وفي واجهة برمجة التطبيقات بين العمليات.
- لا يمكنك استخدام واجهات برمجة التطبيقات هذه إلا مع
ANeuralNetworksExecution
تم إنشاؤه باستخدامANeuralNetworksCompilation_createForDevices
معnumDevices = 1
. - لا يُشترط أن يكون لدى السائق إذن لتسجيل معلومات التوقيت.
إنشاء ملف شخصي لتطبيقك باستخدام أداة Android Systrace
بدءًا من Android 10، تنشئ NNAPI تلقائيًا أحداث systrace التي يمكنك استخدامها لإنشاء ملف شخصي لتطبيقك.
يتضمّن مصدر NNAPI أداة parse_systrace
لمعالجة أحداث systrace التي ينشئها تطبيقك وإنشاء عرض جدولي يعرض المدّة التي تمّ إنفاقها في المراحل المختلفة لدورة حياة النموذج (إنشاء النموذج
والتحضير والترجمة والتنفيذ والإيقاف) والطبقات المختلفة
للتطبيقات. في ما يلي الطبقات التي يتم تقسيم تطبيقك إليها:
Application
: رمز التطبيق الرئيسي-
Runtime
: وقت تشغيل NNAPI IPC
: الاتّصال بين العمليات بين NNAPI Runtime ورمز السائقDriver
: عملية برنامج تشغيل المسرّع
إنشاء بيانات تحليل الملف الشخصي
بافتراض أنّك نزّلت شجرة مصدر AOSP في $ANDROID_BUILD_TOP، وباستخدام مثال تصنيف الصور باستخدام مكتبة TFLite كتطبيق مستهدف، يمكنك إنشاء بيانات ملف تعريف أداء NNAPI من خلال تنفيذ الخطوات التالية:
- ابدأ أداة systrace في Android باستخدام الأمر التالي:
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py -o trace.html -a org.tensorflow.lite.examples.classification nnapi hal freq sched idle load binder_driver
تشير المَعلمة -o trace.html
إلى أنّه سيتم تسجيل المسارات في trace.html
. عند إنشاء ملف شخصي لتطبيقك، عليك
استبدال org.tensorflow.lite.examples.classification
باسم العملية
المحدّد في بيان التطبيق.
سيؤدي ذلك إلى إبقاء إحدى وحدات تحكّم وحدة التحكّم في البرامج مشغولة، ولا تشغِّل الأمر في
الخلفية لأنّه ينتظر بشكل تفاعلي انتهاء عملية enter
.
- بعد بدء أداة جمع بيانات systrace، ابدأ تطبيقك وشغِّل اختبار الأداء.
في هذه الحالة، يمكنك تشغيل تطبيق تصنيف الصور من Android Studio أو من واجهة مستخدم الهاتف التجريبي مباشرةً إذا سبق تثبيت التطبيق. لإنشاء بعض بيانات NNAPI، عليك ضبط التطبيق لاستخدام NNAPI من خلال اختيار NNAPI كجهاز مستهدف في مربّع حوار إعدادات التطبيق.
عند اكتمال الاختبار، أوقِف systrace بالضغط على
enter
في وحدة تحكّم وحدة التحكّم النشطة منذ الخطوة 1.شغِّل الأداة
systrace_parser
لإنشاء إحصاءات تراكمية:
$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html
يقبل المُحلِّل المَعلمات التالية:
- --total-times
: تعرِض هذه المَعلمة إجمالي الوقت الذي تمّ قضاؤه في إحدى الطبقات، بما في ذلك الوقت
الذي تمّ قضاؤه في انتظار التنفيذ عند طلب الوصول إلى طبقة أساسية.
- --print-detail
: تُطبع هذه المَعلمة جميع الأحداث التي تمّ جمعها من systrace
- --per-execution
: تُطبع هذه المَعلمة التنفيذ والمراحل الفرعية فقط
(حسب أوقات التنفيذ) بدلاً من الإحصاءات لجميع المراحل
- --json
: تُنشئ هذه المَعلمة الإخراج بتنسيق JSON
في ما يلي مثال على الإخراج:
===========================================================================================================================================
NNAPI timing summary (total time, ms wall-clock) Execution
----------------------------------------------------
Initialization Preparation Compilation I/O Compute Results Ex. total Termination Total
-------------- ----------- ----------- ----------- ------------ ----------- ----------- ----------- ----------
Application n/a 19.06 1789.25 n/a n/a 6.70 21.37 n/a 1831.17*
Runtime - 18.60 1787.48 2.93 11.37 0.12 14.42 1.32 1821.81
IPC 1.77 - 1781.36 0.02 8.86 - 8.88 - 1792.01
Driver 1.04 - 1779.21 n/a n/a n/a 7.70 - 1787.95
Total 1.77* 19.06* 1789.25* 2.93* 11.74* 6.70* 21.37* 1.32* 1831.17*
===========================================================================================================================================
* This total ignores missing (n/a) values and thus is not necessarily consistent with the rest of the numbers
قد يتعذّر على المُحلِّل تحليل الأحداث التي تم جمعها إذا لم تكن تمثّل أثر استخدام التطبيق بالكامل. قد يتعذّر على الميزة تحديد القسم على وجه التحديد إذا كانت أحداث systrace التي تم إنشاؤها لوضع علامة على نهاية قسم متوفّرة في التتبُّع بدون حدث مرتبط ببدء القسم. يحدث ذلك عادةً إذا تم إنشاء بعض الأحداث من جلسة تحليل أداء سابقة عند بدء أداة جمع بيانات systrace. في هذه الحالة، عليك إجراء عملية وضع التقارير مرة أخرى.
إضافة إحصاءات لرمز تطبيقك إلى ناتج systrace_parser
يستند تطبيق parse_systrace إلى وظيفة systrace المضمّنة في Android. يمكنك إضافة عمليات تتبُّع لعمليات معيّنة في تطبيقك باستخدام واجهة برمجة التطبيقات systrace API (للغة Java ، للتطبيقات الأصلية ) مع أسماء أحداث مخصّصة.
لربط الأحداث المخصّصة بمراحل دورة حياة التطبيق، أضِف إحدى السلاسل التالية إلى مقدمة اسم الحدث:
[NN_LA_PI]
: حدث على مستوى التطبيق لبدء التشغيل[NN_LA_PP]
: حدث على مستوى التطبيق للتحضير[NN_LA_PC]
: حدث على مستوى التطبيق للترجمة[NN_LA_PE]
: حدث على مستوى التطبيق للتنفيذ
في ما يلي مثال على كيفية تغيير مثال رمز معالجة الصور في TFLite
من خلال إضافة قسم runInferenceModel
للمرحلة Execution
وApplication
الطبقتين اللتين تحتويان على أقسام أخرى preprocessBitmap
لن يتم أخذها في الاعتبار في عمليات تتبُّع NNAPI. سيكون القسم runInferenceModel
جزءًا من أحداث systrace التي يعالجها محلل systrace في nnapi:
Kotlin
/** Runs inference and returns the classification results. */ fun recognizeImage(bitmap: Bitmap): List{ // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap") convertBitmapToByteBuffer(bitmap) Trace.endSection() // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel") long startTime = SystemClock.uptimeMillis() runInference() long endTime = SystemClock.uptimeMillis() Trace.endSection() ... return recognitions }
Java
/** Runs inference and returns the classification results. */ public ListrecognizeImage(final Bitmap bitmap) { // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap"); convertBitmapToByteBuffer(bitmap); Trace.endSection(); // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel"); long startTime = SystemClock.uptimeMillis(); runInference(); long endTime = SystemClock.uptimeMillis(); Trace.endSection(); ... Trace.endSection(); return recognitions; }
جودة الخدمة
في الإصدار 11 من نظام التشغيل Android والإصدارات الأحدث، يتيح NNAPI جودة خدمة أفضل من خلال السماح للتطبيق بتحديد الأولويات النسبية لنماذجه، والحد الأقصى للوقت المتوقّع لإعداد نموذج معيّن، والحد الأقصى للوقت المتوقّع لإكمال عملية حسابية معيّنة. يقدّم نظام التشغيل Android 11 أيضًا رموز نتائج إضافية لـ NNAPI تتيح للتطبيقات فهم حالات الفشل، مثل عدم الالتزام بالمهلة الزمنية للتنفيذ.
ضبط أولوية حمولة العمل
لضبط أولوية "وحدة عمل" NNAPI، يمكنك الاتصال بـ
ANeuralNetworksCompilation_setPriority()
قبل الاتصال بـ ANeuralNetworksCompilation_finish()
.
تحديد المواعيد النهائية
يمكن للتطبيقات تحديد مواعيد نهائية لكلٍّ من تجميع النماذج والاستنتاج.
- لضبط مهلة التجميع، اتصل بـ
ANeuralNetworksCompilation_setTimeout()
قبل الاتصال بـANeuralNetworksCompilation_finish()
. - لضبط مهلة الاستنتاج، استخدِم الدالة
ANeuralNetworksExecution_setTimeout()
قبل بدء عملية الترجمة.
مزيد من المعلومات حول المَعلمات
يتناول القسم التالي مواضيع متقدّمة حول استخدام المُعامِلات.
المتجهات الكمّية
إنّ مصفوفة كثافة الكمّية هي طريقة مختصرة لتمثيل صفيف من n بُعد من قيم النقطة الثابتة.
يتوافق NNAPI مع مصفوفات تنشيط الخلايا الكمّية غير المتماثلة بسعة 8 بت. بالنسبة إلى مصفوفات الخلاصات هذه، يتم تمثيل قيمة كل خلية بالعدد الصحيح المكوّن من 8 بت. يرتبط بالtensor مقياس وقيمة نقطة الصفر. وتُستخدَم هذه الوحدات لتحويل الأعداد الكاملة المكوّنة من 8 بت إلى قيم الكسور العشرية التي يتم تمثيلها.
الصيغة هي:
(cellValue - zeroPoint) * scale
حيث تكون قيمة zeroPoint عددًا صحيحًا بسعة 32 بت، ويكون scale قيمة نقطة عائمة بسعة 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 للطول.
إذا كان القرار بشأن ما إذا كان الم Operand متوفّرًا أم لا يختلف لكل عملية تنفيذ، يمكنك الإشارة إلى أنّه تم حذف الم Operand باستخدام الدالتَين
ANeuralNetworksExecution_setInput()
أو
ANeuralNetworksExecution_setOutput()
، مع ضبط NULL
للوسيط و0 للطول.
مصفوفات ذات ترتيب غير معروف
أدخل نظام التشغيل Android 9 (المستوى 28 من واجهة برمجة التطبيقات) عوامل تشغيل نماذج ذات أبعاد غير معروفة ولكن مرتبة معروفة (عدد الأبعاد). أدخل نظام التشغيل Android 10 (المستوى 29 لواجهة برمجة التطبيقات) مصفوفات ذات ترتيب غير معروف، كما هو موضّح في ANeuralNetworksOperandType.
مقياس أداء 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"
يعرض هذا التعبير حاليًا العلامات التالية:
- BurstBuilder
- عمليات معاودة الاتصال
- CompilationBuilder
- CpuExecutor
- ExecutionBuilder
- ExecutionBurstController
- ExecutionBurstServer
- ExecutionPlan
- FibonacciDriver
- GraphDump
- IndexedShapeWrapper
- IonWatcher
- مدير
- الذاكرة
- MemoryUtils
- MetaModel
- ModelArgumentInfo
- ModelBuilder
- NeuralNetworks
- OperationResolver
- العمليات
- OperationsUtils
- PackageInfo
- TokenHasher
- TypeManager
- Utils
- ValidateHal
- VersionedInterfaces
للتحكّم في مستوى رسائل السجلّ التي يعرضها logcat
، استخدِم
متغيّر البيئة ANDROID_LOG_TAGS
.
لعرض المجموعة الكاملة من رسائل سجلّ NNAPI وإيقاف أي رسائل أخرى، اضبط ANDROID_LOG_TAGS
على
القيمة التالية:
BurstBuilder:V Callbacks:V CompilationBuilder:V CpuExecutor:V ExecutionBuilder:V ExecutionBurstController:V ExecutionBurstServer:V ExecutionPlan:V FibonacciDriver:V GraphDump:V IndexedShapeWrapper:V IonWatcher:V Manager:V MemoryUtils:V Memory:V MetaModel:V ModelArgumentInfo:V ModelBuilder:V NeuralNetworks:V OperationResolver:V OperationsUtils:V Operations:V PackageInfo:V TokenHasher:V TypeManager:V Utils:V ValidateHal:V VersionedInterfaces:V *:S.
يمكنك ضبط ANDROID_LOG_TAGS
باستخدام الأمر التالي:
export ANDROID_LOG_TAGS=$(grep -R 'define LOG_TAG' | awk -F '"' '{ print $2 ":V" }' | sort -u | egrep -v "Sample|FileTag|test" | xargs echo -n; echo ' *:S')
يُرجى العِلم أنّ هذا مجرد فلتر ينطبق على logcat
. لا يزال عليك
ضبط القيمة debug.nn.vlog
على all
لإنشاء معلومات حول السجلّ التفصيلي.