Neural Networks API

‫Android Neural Networks API ‏(NNAPI) هي واجهة برمجة تطبيقات C لنظام التشغيل Android مصمّمة لتنفيذ عمليات تتعلّق بالذكاء الاصطناعي على أجهزة Android، وهي عمليات تتطلّب معالجة مكثفة. صُممت NNAPI لتوفير طبقة أساسية من الوظائف للوصول إلى مستويات أعلى أطر التعلم الآلي، مثل TensorFlow Lite وCaffe2، التي تبني الشبكات العصبية وتدربها. تتوفر واجهة برمجة التطبيقات على جميع أجهزة Android التي تعمل بالإصدار 8.1 من نظام التشغيل Android (المستوى 27 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث.

يتيح NNAPI الاستنتاج من خلال تطبيق البيانات من أجهزة Android على نماذج سبق أن تم تدريبها وتحديدها من قِبل المطوّر. تتضمن أمثلة الاستقصاء تصنيف والصور والتنبؤ بسلوك المستخدم وتحديد الاستجابات المناسبة استعلام البحث.

هناك العديد من المزايا التي توفّرها تقنية الإبلاغ على الجهاز فقط:

  • وقت الاستجابة: لست بحاجة إلى إرسال طلب عبر اتصال بالشبكة انتظار الرد. على سبيل المثال، يمكن أن يكون ذلك مهمًا لتطبيقات الفيديو التي تعالج اللقطات المتتالية الواردة من الكاميرا.
  • مدى التوفّر: يتم تشغيل التطبيق حتى عندما يكون خارج تغطية الشبكة.
  • السرعة: جهاز جديد مخصص لمعالجة الشبكة العصبونية يوفر حسابًا أسرع بكثير من وحدة المعالجة المركزية للأغراض العامة فقط.
  • الخصوصية: لا يتم نقل البيانات خارج جهاز Android.
  • التكلفة: لا حاجة إلى مزرعة خوادم عند إجراء جميع العمليات الحسابية على جهاز Android.

هناك أيضًا بعض السلبيات التي يجب أن يأخذها المطوّر في الاعتبار:

  • استخدام النظام: يتطلّب تقييم الشبكات العصبية إجراء الكثير من العمليات الحسابية، ما قد يؤدي إلى زيادة استخدام طاقة البطارية. يجب مراعاة مراقبة حالة البطارية إذا كان ذلك يشكّل مصدر قلق لتطبيقك، خاصةً لعمليات الحساب التي تستغرق وقتًا طويلاً.
  • حجم التطبيق: انتبه إلى حجم نماذجك. قد تستهلك النماذج مساحة تبلغ عدة ميغابايت. في حال جمع نماذج كبيرة في حزمة APK في المستخدمين بلا داعٍ، ننصحك بتنزيل النماذج بعد تثبيت التطبيق، أو استخدام نماذج أصغر، أو تشغيل العمليات الحسابية في السحابة. لا يوفّر NNAPI وظيفة لتشغيل نماذج في السحابة الإلكترونية.

اطّلِع على نموذج Android Neural Networks API للاطّلاع على مثال واحد على كيفية استخدام NNAPI.

التعرّف على وقت تشغيل واجهة برمجة تطبيقات الشبكات العصبونية

من المفترض أن يتم طلب واجهة NNAPI من قبل مكتبات وأُطر وأدوات التعلم الآلي تتيح للمطوّرين تدريب نماذجهم خارج الأجهزة ونشرها على Android الأجهزة. لن تستخدم التطبيقات عادةً NNAPI مباشرةً، ولكن بدلاً من ذلك تستخدم أطر عمل عالية المستوى لتعلم الآلة. ويمكن أن تستخدم هذه الأطر بدورها NNAPI لتنفيذ عمليات الاستنتاج المستندة إلى الأجهزة على الأجهزة المتوافقة.

استنادًا إلى متطلبات التطبيق وإمكانات الأجهزة على جهاز Android، يمكن لوقت تشغيل الشبكة العصبية في Android توزيع حمولة الحوسبة بكفاءة على المعالجات المتاحة على الجهاز، بما في ذلك الأجهزة المخصّصة للشبكة العصبية ووحدات معالجة الرسومات ومعالجات الإشارات الرقمية.

بالنسبة إلى أجهزة Android التي تفتقر إلى برنامج تشغيل متخصص للمورّد، سيكون وقت تشغيل NNAPI وينفذ الطلبات على وحدة المعالجة المركزية.

يعرض الشكل 1 بنية النظام العالية المستوى لواجهة برمجة التطبيقات NNAPI.

الشكل 1. بنية النظام لواجهة برمجة التطبيقات Android Neural Networks API

نموذج برمجة واجهة برمجة التطبيقات للشبكات العصبية

لإجراء العمليات الحسابية باستخدام NNAPI، يجب أولاً إنشاء ملف يحدد العمليات الحسابية المراد إجراؤها. يعد هذا الرسم البياني الحاسوبي مجتمعًا ببيانات الإدخال (على سبيل المثال، الأوزان والتحيزات التي تم تمريرها من التعلم الآلي)، يشكل نموذجًا لتقييم وقت تشغيل NNAPI.

تستخدم واجهة NNAPI أربعة تجريدات رئيسية:

  • النموذج: رسم بياني للعمليات الحسابية للعمليات الحسابية والقيمة الثابتة التي تم تعلّمها من خلال عملية تدريب هذه العمليات خاصة بالشبكة العصبية. وتشمل هذه العمليات التفاف ثنائي الأبعاد والتفعيل اللوجستي (sigmoid) والتفعيل الخطي المصحَّح (ReLU) وغيرها. إنّ إنشاء نموذج هو عملية متزامنة. وبعد إنشائها بنجاح، يمكن إعادة استخدامها في سلاسل المحادثات وعمليات التجميع. في NNAPI، يتم تمثيل النموذج كمثيل ANeuralNetworksModel .
  • التجميع: يمثل إعدادًا لتجميع نموذج NNAPI في رمز المستوى الأدنى. إنّ إنشاء مجموعة اختبارات هو عملية متزامنة. بعد إنشاءها بنجاح، يمكن إعادة استخدامها في جميع سلاسل المحادثات وعمليات التنفيذ. ضِمن NNAPI، يتم تمثيل كل تجميع ANeuralNetworksCompilation مثال.
  • الذاكرة: تمثّل الذاكرة المشتركة والملفات المُعرَّفة بالذاكرة ومستودعات الذاكرة المشابهة. يتيح استخدام ذاكرة تخزين مؤقت لنقل البيانات إلى برامج التشغيل في وقت تشغيل NNAPI بكفاءة أكبر. ينشئ التطبيق عادةً مخزنًا مؤقتًا واحدًا للذاكرة يحتوي على كل متوتر مطلوب لتحديد أي نموذج. يمكنك أيضًا استخدام ملف ذاكرة المخازن المؤقتة لتخزين المدخلات والمخرجات لمثيل التنفيذ. في NNAPI، يتم تمثيل كل مخزن مؤقت للذاكرة ANeuralNetworksMemory مثال.
  • التنفيذ: واجهة لتطبيق نموذج NNAPI على مجموعة من المدخلات وجمع النتائج يمكن تنفيذ الإجراء بشكل متزامن أو غير متزامن.

    بالنسبة إلى التنفيذ غير المتزامن، يمكن لعدة سلاسل مهام الانتظار إلى أن يكتمل التنفيذ نفسه. عند اكتمال هذا التنفيذ، يتم إلغاء جميع سلاسل التعليمات.

    في NNAPI، يتم تمثيل كل عملية تنفيذ على أنّها مثيل ANeuralNetworksExecution .

يوضح الشكل 2 تدفق البرمجة الأساسي.

الشكل 2. مسار البرمجة لواجهة برمجة تطبيقات الشبكات العصبية في Android

يوضّح الجزء المتبقّي من هذا القسم خطوات إعداد نموذج 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 القيم العددية في القيم المنطقية نقطة عائمة 16 بت، ونقطة عائمة 32 بت، وعدد صحيح 32 بت، وغير موقَّعة تنسيقات الأعداد الصحيحة 32 بت

تشتمل معظم العمليات في NNAPI على متسللات. المتغيرات عبارة عن صفائف مكوّنة من n. يتوافق NNAPI مع مصفوفات السلاسل المتسلسلة ذات النقطة العائمة بسعة 16 بت و32 بت و8 بت المقسَّمة و16 بت مقسَّمة و32 بت عدد صحيح و8 بت قيم منطقية.

على سبيل المثال، يمثل الشكل 3 نموذجًا به عمليتين: إضافة متبوعة بالضرب. يأخذ النموذج مصفوفة إدخال وينتج عنها مصفوفة إخراج واحدة.

الشكل 3. مثال على المُعامِلات لنموذج NNAPI

يتضمن النموذج أعلاه سبعة معاملات. ويتم تحديد هذه المُعامِلات بشكل ضمني من خلال فهرس الترتيب الذي تتم إضافتها به إلى النموذج. يكون للمَعلمة الأولى التي تتم إضافتها فهرس 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.

لإنشاء النموذج، يُرجى اتباع الخطوات التالية:

  1. عليك استدعاء ANeuralNetworksModel_create() لتحديد نموذج فارغ.

    ANeuralNetworksModel* model = NULL;
    ANeuralNetworksModel_create(&model);
  2. أضِف المُعامِلات إلى النموذج من خلال استدعاء ANeuralNetworks_addOperand(). ويتم تحديد أنواع بياناتها باستخدام بنية البيانات ANeuralNetworksOperandType .

    // In our example, all our tensors are matrices of dimension [3][4]
    ANeuralNetworksOperandType tensor3x4Type;
    tensor3x4Type.type = ANEURALNETWORKS_TENSOR_FLOAT32;
    tensor3x4Type.scale = 0.f;    // These fields are used for quantized tensors
    tensor3x4Type.zeroPoint = 0;  // These fields are used for quantized tensors
    tensor3x4Type.dimensionCount = 2;
    uint32_t dims[2] = {3, 4};
    tensor3x4Type.dimensions = dims;

    // We also specify operands that are activation function specifiers ANeuralNetworksOperandType activationType; activationType.type = ANEURALNETWORKS_INT32; activationType.scale = 0.f; activationType.zeroPoint = 0; activationType.dimensionCount = 0; activationType.dimensions = NULL;

    // Now we add the seven operands, in the same order defined in the diagram ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 0 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 1 ANeuralNetworksModel_addOperand(model, &activationType); // operand 2 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 3 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 4 ANeuralNetworksModel_addOperand(model, &activationType); // operand 5 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 6
  3. بالنسبة إلى المُعامِلات التي لها قيم ثابتة، مثل الأوزان والانحيازات التي يحصل عليها تطبيقك من عملية تدريب، استخدِم الدالتَين ANeuralNetworksModel_setOperandValue() و ANeuralNetworksModel_setOperandValueFromMemory() .

    في المثال التالي، نضبط قيمًا ثابتة من ملف بيانات التدريب المتوافقة مع ذاكرة التخزين المؤقت التي أنشأناها في إتاحة الوصول إلى بيانات التدريب.

    // In our example, operands 1 and 3 are constant tensors whose values were
    // established during the training process
    const int sizeOfTensor = 3 * 4 * 4;    // The formula for size calculation is dim0 * dim1 * elementSize
    ANeuralNetworksModel_setOperandValueFromMemory(model, 1, mem1, 0, sizeOfTensor);
    ANeuralNetworksModel_setOperandValueFromMemory(model, 3, mem1, sizeOfTensor, sizeOfTensor);

    // We set the values of the activation operands, in our example operands 2 and 5 int32_t noneValue = ANEURALNETWORKS_FUSED_NONE; ANeuralNetworksModel_setOperandValue(model, 2, &noneValue, sizeof(noneValue)); ANeuralNetworksModel_setOperandValue(model, 5, &noneValue, sizeof(noneValue));
  4. لكل عملية في الرسم البياني الموجَّه تريد احتسابها، أضِف العملية إلى النموذج من خلال استدعاء الدالة ANeuralNetworksModel_addOperation() .

    يجب أن يقدّم تطبيقك ما يلي كمَعلمات لهذا الطلب:

    • نوع العملية
    • عدد قيم الإدخال
    • مصفوفة الفهارس لمعاملات الإدخال
    • عدد قيم الإخراج
    • مصفوفة الفهارس لمعاملات الإخراج

    يُرجى العلم أنّه لا يمكن استخدام عامل تشغيل لكل من إدخال العملية المماثلة وإخراجها.

    // We have two operations in our example
    // The first consumes operands 1, 0, 2, and produces operand 4
    uint32_t addInputIndexes[3] = {1, 0, 2};
    uint32_t addOutputIndexes[1] = {4};
    ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, addInputIndexes, 1, addOutputIndexes);

    // The second consumes operands 3, 4, 5, and produces operand 6 uint32_t multInputIndexes[3] = {3, 4, 5}; uint32_t multOutputIndexes[1] = {6}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_MUL, 3, multInputIndexes, 1, multOutputIndexes);
  5. تحديد المعاملات التي يجب أن يتعامل بها النموذج كمدخلات ومخرجات واستدعاء ANeuralNetworksModel_identifyInputsAndOutputs() الأخرى.

    // Our model has one input (0) and one output (6)
    uint32_t modelInputIndexes[1] = {0};
    uint32_t modelOutputIndexes[1] = {6};
    ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, modelInputIndexes, 1 modelOutputIndexes);
  6. يمكنك اختياريًا تحديد ما إذا كان يُسمح بحساب ANEURALNETWORKS_TENSOR_FLOAT32 باستخدام نطاق أو دقة منخفضة مثل التنسيق IEEE 754 للنقطة العائمة 16 بت من خلال استدعاء ANeuralNetworksModel_relaxComputationFloat32toFloat16().

  7. يُرجى الاتصال على ANeuralNetworksModel_finish() لإكمال تعريف النموذج. في حال عدم حدوث أي أخطاء، تعرض هذه الدالة رمز النتيجة ANEURALNETWORKS_NO_ERROR.

    ANeuralNetworksModel_finish(model);

بمجرد إنشاء نموذج، يمكنك تجميعه أي عدد من المرات وتنفيذ كل التجميع لأي عدد من المرات.

مسار التحكّم

لدمج تدفّق التحكّم في نموذج NNAPI، اتّبِع الخطوات التالية:

  1. أنشئ الرسوم البيانية الفرعية للتنفيذ المقابلة (الرسوم البيانية الفرعية then وelse لعبارة IF والرسوم البيانية الفرعية condition وbody لحلقة WHILE) كنماذج ANeuralNetworksModel* مستقلة:

    ANeuralNetworksModel* thenModel = makeThenModel();
    ANeuralNetworksModel* elseModel = makeElseModel();
  2. قم بإنشاء المعاملات التي تشير إلى تلك النماذج داخل النموذج الذي يحتوي على تدفق التحكم:

    ANeuralNetworksOperandType modelType = {
        .type = ANEURALNETWORKS_MODEL,
    };
    ANeuralNetworksModel_addOperand(model, &modelType);  // kThenOperandIndex
    ANeuralNetworksModel_addOperand(model, &modelType);  // kElseOperandIndex
    ANeuralNetworksModel_setOperandValueFromModel(model, kThenOperandIndex, &thenModel);
    ANeuralNetworksModel_setOperandValueFromModel(model, kElseOperandIndex, &elseModel);
  3. أضِف عملية تدفّق التحكّم:

    uint32_t inputs[] = {kConditionOperandIndex,
                         kThenOperandIndex,
                         kElseOperandIndex,
                         kInput1, kInput2, kInput3};
    uint32_t outputs[] = {kOutput1, kOutput2};
    ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_IF,
                                      std::size(inputs), inputs,
                                      std::size(output), outputs);

موسيقى مجمّعة

تحدّد خطوة التجميع المعالجات التي سيتم تنفيذ نموذجك عليها. ويطلب من برامج التشغيل المقابلة الاستعداد لتنفيذه. وقد يشمل ذلك إنشاء رمز آلي خاص بالمعالجات التي سيتم تشغيل النموذج عليها.

لتجميع نموذج، اتّبِع الخطوات التالية:

  1. عليك استدعاء ANeuralNetworksCompilation_create() لإنشاء مثيل تجميع جديد.

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

    يمكنك اختياريًا استخدام تخصيص الجهاز لتحديد الأجهزة التي سيتم تنفيذها عليها.

  2. يمكنك اختياريًا ضبط طريقة توزيع بيئة التشغيل بين طاقة البطارية. وسرعة الاستخدام والتنفيذ. يمكنك إجراء ذلك من خلال الاتصال بالرقم ANeuralNetworksCompilation_setPreference().

    // Ask to optimize for low power consumption
    ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);

    تشمل الإعدادات المفضَّلة التي يمكنك تحديدها ما يلي:

    • ANEURALNETWORKS_PREFER_LOW_POWER: تفضيل التنفيذ بطريقة تقلل من استنزاف البطارية. ويُفضَّل استخدام هذا الإجراء لعمليات التجميع التي يتم تنفيذها كثيرًا.
    • ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER: تفضيل عرض إجابة واحدة في أسرع وقت ممكن، حتى إذا أدى ذلك استهلاكًا أكبر للطاقة. هذا هو الخيار التلقائي.
    • ANEURALNETWORKS_PREFER_SUSTAINED_SPEED: يُفضَّل زيادة معدل نقل البيانات إلى أقصى حد في اللقطات المتتالية، مثل عند معالجة اللقطات المتتالية الواردة من الكاميرا.
  3. يمكنك اختياريًا إعداد ذاكرة التخزين المؤقت للترجمة من خلال استدعاء ANeuralNetworksCompilation_setCaching.

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

    استخدام getCodeCacheDir() لـ cacheDir. يجب أن يكون token المحدد فريدًا لكل نموذج ضمن التطبيق.

  4. يمكنك إنهاء تعريف عملية الترجمة من خلال استدعاء ANeuralNetworksCompilation_finish(). وإذا لم تكن هناك أخطاء، فستُرجع هذه الدالة رمز نتيجة ANEURALNETWORKS_NO_ERROR

    ANeuralNetworksCompilation_finish(compilation);

اكتشاف الأجهزة وتعيينها

على أجهزة Android التي تعمل بالإصدار 10 (المستوى 29 لواجهة برمجة التطبيقات) والإصدارات الأحدث، توفّر واجهة برمجة التطبيقات NNAPI وظائف تسمح لتطبيقات ومكتبات إطار عمل تعلُّم الآلة بالحصول على معلومات عن الأجهزة المتاحة وتحديد الأجهزة التي سيتم استخدامها لتنفيذ العمليات. من خلال تقديم معلومات عن الأجهزة المتاحة، يمكن للتطبيقات الحصول على الإصدار الدقيق لبرامج التشغيل المتوفّرة على الجهاز لتجنُّب عدم التوافق المعروف. من خلال منح التطبيقات القدرة على تحديد الأجهزة التي تريد استخدامها تنفذ أقسامًا مختلفة من النموذج، ويمكن تحسين التطبيقات للتوافق مع أنظمة الجهاز الذي يتم استخدامها عليه.

رصد الأجهزة

استخدِم رمز ANeuralNetworks_getDeviceCount للحصول على عدد الأجهزة المتاحة. لكل جهاز، استخدم ANeuralNetworks_getDevice لضبط مثيل ANeuralNetworksDevice على مرجع إلى ذلك الجهاز.

بمجرد أن يكون لديك مرجع جهاز، يمكنك معرفة معلومات إضافية حول هذا الجهاز باستخدام الدوال التالية:

تعيين الجهاز

استخدِم رمز ANeuralNetworksModel_getSupportedOperationsForDevices لاكتشاف العمليات التي يمكن تنفيذها في نموذج معيّن على أجهزة محدّدة.

للتحكّم في مسرعات التنفيذ التي تريد استخدامها، استخدِم ANeuralNetworksCompilation_createForDevices بدلاً من ANeuralNetworksCompilation_create. استخدِم عنصر ANeuralNetworksCompilation الناتج كالمعتاد. تعرض الدالة خطأ إذا كان النموذج المقدَّم يحتوي على عمليات غير متوافقة مع الأجهزة المحدّدة.

في حال تحديد أجهزة متعددة، يكون وقت التشغيل مسؤولاً عن توزيع العمل على الأجهزة.

على غرار الأجهزة الأخرى، يتم تمثيل تنفيذ وحدة المعالجة المركزية لـ NNAPI باستخدام ملف ANeuralNetworksDevice يحمل الاسم nnapi-reference والنوع ANEURALNETWORKS_DEVICE_TYPE_CPU. عند الاتصال ANeuralNetworksCompilation_createForDevices، لا يتم تنفيذ وحدة المعالجة المركزية (CPU) يُستخدم لمعالجة حالات الإخفاق في تجميع النماذج وتنفيذها.

تقع على عاتق التطبيق مسؤولية تقسيم أي نموذج إلى نماذج فرعية يمكن تشغيلها على الأجهزة المحددة. بالنسبة إلى التطبيقات التي لا تحتاج إلى مشاركة ملف التمهيد يدويًا، يجب أن تستمر في طلب ANeuralNetworksCompilation_create البسيط لاستخدام جميع الأجهزة المتاحة (بما في ذلك وحدة المعالجة المركزية) لتسريع ANeuralNetworksCompilation_create النموذج. إذا تعذر توافق النموذج بالكامل مع الأجهزة التي حدّدتها باستخدام ANeuralNetworksCompilation_createForDevices، ANEURALNETWORKS_BAD_DATA .

تقسيم النموذج

وقت تشغيل NNAPI، عند توفُّر أجهزة متعددة للطراز يوزع العمل عبر الأجهزة. على سبيل المثال، إذا تم تضمين أكثر من جهاز يتم تقديمها إلى ANeuralNetworksCompilation_createForDevices، كل التي سيتم مراعاتها عند تخصيص العمل. يُرجى العِلم أنّه في حال عدم إدراج وحدة المعالجة المركزية في القائمة، سيتم إيقاف تنفيذ وحدة المعالجة المركزية. عند استخدام ANeuralNetworksCompilation_create ، سيتم أخذ جميع الأجهزة المتاحة في الاعتبار، بما في ذلك وحدة المعالجة المركزية.

يتم التوزيع من خلال اختيار الجهاز المتوافق مع العملية من قائمة الأجهزة المتاحة لكل من العمليات في النموذج، ثم تحديد أفضل أداء، أي أسرع وقت تنفيذ أو أدنى استهلاك للطاقة، استنادًا إلى الإعدادات المفضّلة للتنفيذ التي يحدّدها العميل. ولا تراعي خوارزمية التقسيم هذه القيم المحتملة الذي يسببه أمر الإدراج (IO) بين المعالجات المختلفة، تحديد معالجات متعددة (إما بشكل صريح عند استخدام ANeuralNetworksCompilation_createForDevices أو ضمنيًا باستخدام ANeuralNetworksCompilation_create) من المهم تحديد الناتج التطبيق.

لفهم طريقة تقسيم نموذجك باستخدام NNAPI، راجِع سجلات Android لرسالة (على مستوى INFO مع العلامة ExecutionPlan):

ModelBuilder::findBestDeviceForEachOperation(op-name): device-index

op-name هو الاسم الوصفي للعملية في الرسم البياني يمثّل الجهاز device-index فهرس الجهاز المرشَّح للجهاز في قائمة الأجهزة. هذه القائمة هي الإدخال المقدَّم إلى ANeuralNetworksCompilation_createForDevices أو، في حال استخدام ANeuralNetworksCompilation_createForDevices، قائمة الأجهزة المعروضة عند تكرار جميع الأجهزة باستخدام ANeuralNetworks_getDeviceCount و ANeuralNetworks_getDevice.

الرسالة (على مستوى المعلومات مع العلامة ExecutionPlan):

ModelBuilder::partitionTheWork: only one best device: device-name

تشير هذه الرسالة إلى أنه تم تسريع الرسم البياني بأكمله على الجهاز. device-name

التنفيذ

تطبِّق خطوة التنفيذ النموذج على مجموعة من المدخلات وتخزِّن مخرجات العمليات الحسابية في مساحة تخزين مؤقت أو مساحة ذاكرة واحدة أو أكثر للمستخدمين خصصها تطبيقك.

لتنفيذ نموذج مجمَّع، اتّبِع الخطوات التالية:

  1. عليك استدعاء ANeuralNetworksExecution_create() لإنشاء مثيل تنفيذ جديد.

    // Run the compiled model against a set of inputs
    ANeuralNetworksExecution* run1 = NULL;
    ANeuralNetworksExecution_create(compilation, &run1);
  2. حدِّد مكان قراءة تطبيقك لقيم الإدخال لإجراء العملية الحسابية. يمكن لتطبيقك قراءة قيم الإدخال من مخزن مؤقت للمستخدم أو مساحة ذاكرة مخصّصة، وذلك من خلال استدعاء ANeuralNetworksExecution_setInput() أو ANeuralNetworksExecution_setInputFromMemory() على التوالي.

    // Set the single input to our sample model. Since it is small, we won't use a memory buffer
    float32 myInput[3][4] = { ...the data... };
    ANeuralNetworksExecution_setInput(run1, 0, NULL, myInput, sizeof(myInput));
  3. حدِّد المكان الذي يكتب فيه تطبيقك قيم النتائج. يمكن لتطبيقك كتابة قيم الإخراج في ملف تدخُّل مستخدم أو مساحة ذاكرة مخصّصة، وذلك من خلال استدعاء ANeuralNetworksExecution_setOutput() أوANeuralNetworksExecution_setOutputFromMemory() على التوالي.

    // Set the output
    float32 myOutput[3][4];
    ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
  4. ضع جدولاً لعملية التنفيذ للبدء، من خلال استدعاء ANeuralNetworksExecution_startCompute() الأخرى. وإذا لم تكن هناك أخطاء، فستُرجع هذه الدالة رمز نتيجة ANEURALNETWORKS_NO_ERROR

    // Starts the work. The work proceeds asynchronously
    ANeuralNetworksEvent* run1_end = NULL;
    ANeuralNetworksExecution_startCompute(run1, &run1_end);
  5. استخدِم الدالة ANeuralNetworksEvent_wait() للانتظار إلى أن تكتمل عملية التنفيذ. إذا كان التنفيذ ناجحًا، تعرض هذه الدالة رمز النتيجة ANEURALNETWORKS_NO_ERROR. يمكن تنفيذ الانتظار في سلسلة مهام مختلفة عن سلسلة المهام التي تبدأ التنفيذ.

    // For our example, we have no other work to do and will just wait for the completion
    ANeuralNetworksEvent_wait(run1_end);
    ANeuralNetworksEvent_free(run1_end);
    ANeuralNetworksExecution_free(run1);
  6. يمكنك اختياريًا تطبيق مجموعة مختلفة من المدخلات على النموذج المجمَّع من خلال استخدام مثيل الترجمة نفسه لإنشاء مثيل 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 التي تعمل بنظام التشغيل 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);

قوائم انتظار أوامر غير متزامنة وتنفيذ سياج

في نظام التشغيل 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)، قد يكون من المفيد تعطيل الإجراء الاحتياطي لوحدة المعالجة المركزية التعامل مع حالات الفشل مع تنفيذ العملية المُحسَّنة للعميل.

في نظام Android 10، إذا تم التجميع باستخدام ANeuralNetworksCompilation_createForDevices، وسيتم إيقاف استخدام وحدة المعالجة المركزية (CPU) الاحتياطي.

في نظام التشغيل Android P، يتم تنفيذ NNAPI على وحدة المعالجة المركزية في حال تعذّر التنفيذ على برنامج التشغيل. وينطبق ذلك أيضًا على نظام التشغيل Android 10 عند استخدام ANeuralNetworksCompilation_create بدلاً من ANeuralNetworksCompilation_createForDevices.

في حال تعذّر التنفيذ الأول، يتم الرجوع إلى هذا القسم الفردي، وإذا استمرت المشكلة، تتم إعادة محاولة تنفيذ النموذج بأكمله على وحدة المعالجة المركزية.

إذا تعذّر التقسيم أو التجميع، ستتم تجربة النموذج بأكمله على وحدة المعالجة المركزية.

هناك حالات لا تتوفر فيها بعض العمليات على وحدة المعالجة المركزية، وفي مثل ستفشل مواقف أو تنفيذ المواقف بدلاً من التراجع.

حتى بعد إيقاف وضع "الرجوع إلى وحدة المعالجة المركزية"، قد تظل هناك عمليات في النموذج مُجدوَلة على وحدة المعالجة المركزية. إذا كانت وحدة المعالجة المركزية مضمّنة في قائمة المعالجات التي تم إرسالها إلى ANeuralNetworksCompilation_createForDevices، وكانت إما المعالج الوحيد الذي يتيح تنفيذ هذه العمليات أو المعالج الذي يُزعَم أنّه يقدّم أفضل أداء لهذه العمليات، سيتم اختياره كمنفّذ أساسي (غير احتياطي).

لضمان عدم تنفيذ وحدة المعالجة المركزية، استخدِم ANeuralNetworksCompilation_createForDevices مع استبعاد nnapi-reference من قائمة الأجهزة. بدءًا من الإصدار Android P، يمكن إيقاف الإجراء الاحتياطي في وقت التنفيذ على تنشئ أداة "تصحيح الأخطاء" من خلال ضبط السمة debug.nn.partition على القيمة 2.

نطاقات الذكريات

في الإصدار 11 من نظام التشغيل Android والإصدارات الأحدث، تتيح واجهة برمجة التطبيقات NNAPI نطاقات الذاكرة التي توفّر واجهات تخصيص للذاكرة غير الشفافة. ويسمح ذلك للتطبيقات بتمرير ملف ذاكرة برمجي native على مستوى الجهاز في جميع عمليات التنفيذ، كي لا تنسخ NNAPI البيانات أو تنقلها بشكل غير ضروري عند تنفيذ عمليات متتالية على برنامج التشغيل نفسه.

تهدف ميزة نطاق الذاكرة إلى مصفوفات الخلاصات التي تكون داخلية في الغالب للسائق ولا تحتاج إلى الوصول المتكرّر إلى جانب العميل. أمثلة على وتشمل هذه العوامل متوترات الحالة في نماذج المتتابعة. لموتّرات تحتاج إلى الوصول المتكرر إلى وحدة المعالجة المركزية (CPU) من جانب العميل، استخدام مجموعات الذاكرة المشتركة بدلاً من ذلك.

لتخصيص ذاكرة معتمة، نفِّذ الخطوات التالية:

  1. عليك استدعاء ANeuralNetworksMemoryDesc_create() لإنشاء واصف جديد للذاكرة:

    // Create a memory descriptor
    ANeuralNetworksMemoryDesc* desc;
    ANeuralNetworksMemoryDesc_create(&desc);
  2. حدِّد جميع أدوار الإدخال والإخراج المقصودة من خلال استدعاء الدوالّ ANeuralNetworksMemoryDesc_addInputRole() و ANeuralNetworksMemoryDesc_addOutputRole().

    // Specify that the memory may be used as the first input and the first output
    // of the compilation
    ANeuralNetworksMemoryDesc_addInputRole(desc, compilation, 0, 1.0f);
    ANeuralNetworksMemoryDesc_addOutputRole(desc, compilation, 0, 1.0f);
  3. بشكل اختياري، يمكنك تحديد أبعاد الذاكرة من خلال استدعاء ANeuralNetworksMemoryDesc_setDimensions()

    // Specify the memory dimensions
    uint32_t dims[] = {3, 4};
    ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
  4. إنهاء تعريف الواصف من خلال استدعاء ANeuralNetworksMemoryDesc_finish()

    ANeuralNetworksMemoryDesc_finish(desc);
  5. يمكنك تخصيص أي عدد تريده من الذكريات من خلال تمرير أداة الوصف إلى ANeuralNetworksMemory_createFromDesc()

    // Allocate two opaque memories with the descriptor
    ANeuralNetworksMemory* opaqueMem;
    ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
  6. تفريغ واصف الذاكرة عند الاستغناء عنه

    ANeuralNetworksMemoryDesc_free(desc);

يمكن للعميل استخدام عنصر ANeuralNetworksMemory الذي تم إنشاؤه فقط مع ANeuralNetworksExecution_setInputFromMemory() أو ANeuralNetworksExecution_setOutputFromMemory() وفقًا للأدوار المحددة في الكائن ANeuralNetworksMemoryDesc. يجب ضبط وسيطات البدء والطول على 0، ما يشير إلى استخدام الذاكرة بالكامل. العميل أيضًا ضبط محتوى الذاكرة أو استخراجه بشكل صريح باستخدام ANeuralNetworksMemory_copy()

يمكنك إنشاء ذكريات مبهمة باستخدام أدوار ذات أبعاد أو ترتيبات غير محدّدة. في هذه الحالة، قد يتعذّر إنشاء الذاكرة مع ظهور الحالة ANEURALNETWORKS_OP_FAILED إذا لم يكن ذلك متوافقًا مع البرنامج الأساسي. يتم تشجيع العميل على تنفيذ المنطق الاحتياطي من خلال تخصيص مخزن مؤقت كبير بما يكفي بدعم من Ashmem أو وضع تخزين البيانات الثنائية الكبيرة AHardwareBuffer.

عندما لا يحتاج NNAPI إلى الوصول إلى عنصر الذاكرة غير الشفاف، يجب تحرير مثيل ANeuralNetworksMemory المرتبط به:

ANeuralNetworksMemory_free(opaqueMem);

قياس الأداء

يمكنك تقييم أداء تطبيقك من خلال قياس وقت التنفيذ أو باستخدام التحليل الوظيفي.

وقت التنفيذ

عندما تريد تحديد إجمالي وقت التنفيذ من خلال بيئة التشغيل، يمكنك استخدام واجهة برمجة التطبيقات للتنفيذ المتزامن وقياس الوقت الذي يستغرقه الاتصال. عندما تريد معرفة إجمالي وقت التنفيذ من خلال مستوى أدنى من بنية البرنامج، يمكنك استخدام ANeuralNetworksExecution_setMeasureTiming وANeuralNetworksExecution_getDuration للحصول على ما يلي:

  • مدة التنفيذ في مسرِّع (وليس في برنامج التشغيل الذي يعمل على المضيف) معالج البيانات).
  • مدة التنفيذ في برنامج التشغيل، بما في ذلك الوقت المستغرَق في مسرِّعة الأعمال.

تُستثنى وقت التنفيذ في برنامج التشغيل النفقات العامة، مثل وقت التشغيل. نفسه وIPC المطلوب لبيئة التشغيل للاتصال ببرنامج التشغيل.

تقيس واجهات برمجة التطبيقات هذه المدة بين حدثَي إرسال العمل وإكماله، بدلاً من الوقت الذي يخصصه برنامج التشغيل أو المسرِّع لإجراء عملية التحليل، والتي قد يتم إيقافها بسبب تبديل السياق.

على سبيل المثال، إذا بدأ الاستنتاج 1، حينئذٍ يتوقف السائق عن العمل لتنفيذ الاستنتاج 2، فإنه يستأنف ويُكمل الاستنتاج 1، وهو وقت تنفيذ سيتضمن الاستنتاج 1 الوقت الذي توقّف فيه العمل لتنفيذ الاستنتاج 2.

قد تكون معلومات التوقيت هذه مفيدة لنشر إنتاج تطبيقًا لجمع بيانات القياس عن بُعد للاستخدام بلا اتصال بالإنترنت. يمكنك استخدام بيانات التوقيت لتعديل التطبيق وتحسين أدائه.

عند استخدام هذه الوظيفة، يُرجى مراعاة ما يلي:

  • قد يؤدي جمع معلومات التوقيت إلى التأثير في الأداء.
  • لا يمكن إلا لبرنامج التشغيل احتساب الوقت الذي يقضيه في نفسه أو في المسرّع، باستثناء الوقت الذي يقضيه في وقت تشغيل NNAPI وفي واجهة برمجة التطبيقات بين العمليات.
  • لا يمكنك استخدام واجهات برمجة التطبيقات هذه إلا مع ANeuralNetworksExecution تم تم الإنشاء باستخدام ANeuralNetworksCompilation_createForDevices مع numDevices = 1.
  • لا يُشترط أن يكون لدى السائق إذن لتسجيل معلومات التوقيت.

إنشاء ملف شخصي على التطبيق باستخدام Android Systrace

بدءًا من الإصدار 10 من نظام التشغيل Android، تنشئ واجهة NNAPI تلقائيًا systrace التي التي يمكنك استخدامها لتحديد تطبيقك.

يأتي مصدر NNAPI مزوّدًا بأداة parse_systrace لمعالجة التي تم إنشاؤها بواسطة التطبيق وإنشاء عرض جدول يوضح الوقت المستغرَق في المراحل المختلفة لدورة حياة النموذج (المثيل المثيلي، الإعداد والتجميع والإنهاء) وطبقات مختلفة من التطبيقات. في ما يلي الطبقات التي يتم تقسيم تطبيقك إليها:

  • Application: رمز التطبيق الرئيسي
  • Runtime: وقت تشغيل NNAPI
  • IPC: الاتصال البيني للعمليات بين "وقت تشغيل NNAPI" و"برنامج التشغيل" رقم الاعتماد
  • Driver: عملية برنامج تشغيل المسرّع

إنشاء بيانات تحليل الملف الشخصي

وبافتراض أنك اطّلعت على شجرة مصدر AOSP على $ANDROID_BUILD_TOP، باستخدام مثال تصنيف صور TFLite استهدافًا، يمكنك إنشاء بيانات تحليل NNAPI باستخدام الخطوات التالية:

  1. ابدأ أداة systrace في Android باستخدام الأمر التالي:
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py  -o trace.html -a org.tensorflow.lite.examples.classification nnapi hal freq sched idle load binder_driver

تشير المعلمة -o trace.html إلى أن عمليات التتبع ستكون المكتوبة في trace.html. عند إنشاء ملف تعريفي خاص، سيكون عليك استبدِل org.tensorflow.lite.examples.classification باسم العملية. المحدد في بيان التطبيق.

سيؤدي ذلك إلى إبقاء إحدى وحدة تحكم Shell مشغولة، لا تُشغل الأمر في الخلفية لأنّه ينتظر بشكل تفاعلي إنهاء enter.

  1. بعد بدء أداة جمع بيانات systrace، ابدأ تطبيقك وشغِّل اختبار الأداء.

يمكنك في هذه الحالة تشغيل تطبيق Image Classification من "استوديو Android". أو مباشرةً من واجهة مستخدم الهاتف التجريبي إذا كان التطبيق قد سبق تثبيته. لإنشاء بعض بيانات NNAPI، يجب إعداد التطبيق لاستخدام NNAPI من خلال تحديد NNAPI كجهاز مستهدف في مربع حوار إعداد التطبيق.

  1. عند اكتمال الاختبار، قم بإنهاء تتبع النظام عن طريق الضغط على enter على أنّ الوحدة الطرفية لوحدة التحكّم نشطة منذ الخطوة 1.

  2. شغِّل الأداة systrace_parser لإنشاء إحصاءات تراكمية:

$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html

يقبل المُحلِّل المَعلمات التالية: - --total-times: تعرِض هذه المَعلمة إجمالي الوقت الذي تمّ قضاؤه في إحدى الطبقات، بما في ذلك الوقت الذي تمّ قضاؤه في انتظار التنفيذ عند طلب الوصول إلى طبقة أساسية - --print-detail: تُطبع هذه المَعلمة جميع الأحداث التي تمّ جمعها من systrace - --per-execution: تُطبع هذه المَعلمة التنفيذ والمراحل الفرعية فقط (حسب أوقات التنفيذ) بدلاً من الإحصاءات لجميع المراحل - --json: تُنشئ هذه المَعلمة الإخراج بتنسيق JSON

في ما يلي مثال على الإخراج:

===========================================================================================================================================
NNAPI timing summary (total time, ms wall-clock)                                                      Execution
                                                           ----------------------------------------------------
              Initialization   Preparation   Compilation           I/O       Compute      Results     Ex. total   Termination        Total
              --------------   -----------   -----------   -----------  ------------  -----------   -----------   -----------   ----------
Application              n/a         19.06       1789.25           n/a           n/a         6.70         21.37           n/a      1831.17*
Runtime                    -         18.60       1787.48          2.93         11.37         0.12         14.42          1.32      1821.81
IPC                     1.77             -       1781.36          0.02          8.86            -          8.88             -      1792.01
Driver                  1.04             -       1779.21           n/a           n/a          n/a          7.70             -      1787.95

Total                   1.77*        19.06*      1789.25*         2.93*        11.74*        6.70*        21.37*         1.32*     1831.17*
===========================================================================================================================================
* This total ignores missing (n/a) values and thus is not necessarily consistent with the rest of the numbers

قد يفشل المحلل اللغوي إذا لم تكن الأحداث التي تم جمعها تمثل وصفًا كاملاً تتبع التطبيقات. وقد تفشل على وجه الخصوص إذا تم إنشاء أحداث تتبع النظام للإشارة إلى وجود نهاية قسم في التتبع بدون إضافة علامة حدث بداية القسم. يحدث ذلك عادةً إذا تم إنشاء بعض الأحداث من جلسة تحليل أداء سابقة عند بدء أداة جمع بيانات systrace. في هذه الحالة، عليك إجراء عملية وضع الملف الشخصي مرة أخرى.

إضافة إحصاءات لرمز التطبيق إلى إخراج systrace_parser

يستند تطبيق parse_systrace إلى وظيفة systrace المضمّنة في Android. يمكنك إضافة عمليات تتبُّع لعمليات معيّنة في تطبيقك باستخدام واجهة برمجة التطبيقات systrace API (للغة Java ، للتطبيقات الأصلية ) مع أسماء أحداث مخصّصة.

لربط الأحداث المخصّصة بمراحل نشاط التطبيق، أضِف اسم الحدث أولاً بإحدى السلاسل التالية:

  • [NN_LA_PI]: حدث على مستوى التطبيق لبدء التشغيل
  • [NN_LA_PP]: حدث على مستوى التطبيق للاستعداد
  • [NN_LA_PC]: حدث على مستوى التطبيق في ميزة التجميع
  • [NN_LA_PE]: حدث على مستوى التطبيق للتنفيذ

إليك مثال على كيفية تغيير مثال تصنيف صور TFLite عن طريق إضافة القسم runInferenceModel للمرحلة Execution تحتوي طبقة Application الأخرى على أقسام preprocessBitmap الأخرى التي لن يتم اعتبارها في تقارير NNAPI. سيكون القسم runInferenceModel جزءًا من أحداث systrace التي يعالجها محلل systrace في nnapi:

Kotlin

/** Runs inference and returns the classification results. */
fun recognizeImage(bitmap: Bitmap): List {
   // This section won’t appear in the NNAPI systrace analysis
   Trace.beginSection("preprocessBitmap")
   convertBitmapToByteBuffer(bitmap)
   Trace.endSection()

   // Run the inference call.
   // Add this method in to NNAPI systrace analysis.
   Trace.beginSection("[NN_LA_PE]runInferenceModel")
   long startTime = SystemClock.uptimeMillis()
   runInference()
   long endTime = SystemClock.uptimeMillis()
   Trace.endSection()
    ...
   return recognitions
}

Java

/** Runs inference and returns the classification results. */
public List recognizeImage(final Bitmap bitmap) {

 // This section won’t appear in the NNAPI systrace analysis
 Trace.beginSection("preprocessBitmap");
 convertBitmapToByteBuffer(bitmap);
 Trace.endSection();

 // Run the inference call.
 // Add this method in to NNAPI systrace analysis.
 Trace.beginSection("[NN_LA_PE]runInferenceModel");
 long startTime = SystemClock.uptimeMillis();
 runInference();
 long endTime = SystemClock.uptimeMillis();
 Trace.endSection();
  ...
 Trace.endSection();
 return recognitions;
}

جودة الخدمة

في الإصدار 11 من نظام التشغيل Android والإصدارات الأحدث، يتيح NNAPI جودة خدمة أفضل من خلال السماح للتطبيق بتحديد الأولويات النسبية لنماذجه، والحد الأقصى للوقت المتوقّع لإعداد نموذج معيّن، والحد الأقصى للوقت المتوقّع لإكمال عملية حسابية معيّنة. نقدّم لكم أيضًا Android 11 رموز نتائج إضافية من NNAPI تمكّن التطبيقات من فهم حالات الإخفاق، مثل التنفيذ الفائت والمواعيد النهائية.

تحديد أولوية عبء العمل

لضبط أولوية عبء عمل NNAPI، اتصل ANeuralNetworksCompilation_setPriority() قبل الاتصال بـ ANeuralNetworksCompilation_finish().

تحديد المواعيد النهائية

يمكن للتطبيقات تحديد مواعيد نهائية لكلٍ من تجميع النماذج والاستنتاج.

مزيد من المعلومات عن المَعلمات

يتناول القسم التالي مواضيع متقدّمة حول استخدام المُعامِلات.

نقاط التوتر الكمّية

مصفوفة الكمّية هي طريقة مختصرة لتمثيل صفيف من n بُعد من قيم النقطة الثابتة.

يتيح NNAPI استخدام مصفوفات كمية غير متماثلة بسعة 8 بت. وبالنسبة إلى هذه المؤشرات، يتم تمثيل قيمة كل خلية بعدد 8 بت صحيح. الأجهزة المرتبطة متنسورة هو مقياس وقيمة نقطة صفرية. وتُستخدَم هذه الوحدات لتحويل الأعداد الكاملة المكوّنة من 8 بت إلى قيم الكسور العشرية التي يتم تمثيلها.

المعادلة هي:

(cellValue - zeroPoint) * scale

حيث تكون قيمة zeroPoint عددًا صحيحًا بسعة 32 بت، ويكون scale قيمة نقطة عائمة بسعة 32 بت.

مقارنةً بوحدات التخزين المكثّفة التي تتضمّن قيمًا بنقطة عائمة 32 بت، تتمتع وحدات التخزين المكثّفة التي تتضمّن قيمًا بدقة 8 بت بميزتَين:

  • يكون تطبيقك أصغر حجمًا، لأنّ الأوزان المدربة تشغل ربع حجم المصفوفات الثنائية 32 بت.
  • غالبًا ما يتم تنفيذ العمليات الحسابية بشكلٍ أسرع. ويرجع ذلك إلى قلة البيانات التي يجب جلبها من الذاكرة وكفاءة المعالِجات مثل الخصائص المنصات الصلبة (DSP) في حساب الأعداد الصحيحة.

وعلى الرغم من إمكانية تحويل نموذج النقطة العائمة إلى نموذج كَمي، المستخدم قد أظهرت أنه يتم تحقيق نتائج أفضل من خلال تدريب نموذج نموذج مباشرة. في الواقع، تتعلّم الشبكة العصبية تعويض الدقة المتزايدة لكل قيمة. لكل موتر كمي، يتم حساب يتم تحديد قيم zeroPoint أثناء عملية التطبيق.

في NNAPI، يمكنك تعريف أنواع متسابقه الكمّي عن طريق تعيين حقل النوع ANeuralNetworksOperandType هيكل البيانات ANEURALNETWORKS_TENSOR_QUANT8_ASYMM يمكنك أيضًا تحديد مقياس مصفوفة تينسور وقيمة نقطة الصفر لها في بنية البيانات هذه.

بالإضافة إلى مأخذ القوة غير المتماثلة 8 بت، تدعم NNAPI ما يلي:

المَعلمات الاختيارية

تأخذ بعض العمليات، مثل ANEURALNETWORKS_LSH_PROJECTION، مَعلمات اختيارية. للإشارة في النموذج إلى أنّه تمت إزالة المُشغِّل الاختياري، استدِع الدالة ANeuralNetworksModel_setOperandValue() ، مع ضبط القيمة NULL للتخزين المؤقت والقيمة 0 للطول.

إذا كان القرار بشأن ما إذا كان المعامل موجودًا أم لا يختلف حسب كل ستشير إلى أنه تم حذف المعامل باستخدام ANeuralNetworksExecution_setInput() أو ANeuralNetworksExecution_setOutput() مع تمرير NULL للمخزن المؤقت و0 للطول.

مصفوفات ذات ترتيب غير معروف

قدّم نظام Android 9 (مستوى واجهة برمجة التطبيقات 28) معاملات نماذج ذات أبعاد غير معروفة، ولكن رتبة معروفة (عدد الأبعاد). تقديم Android 10 (مستوى واجهة برمجة التطبيقات 29) المضاعفات ذات الرتبة غير المعروفة كما هو موضح في ANeure NetworksOperandType.

مقياس أداء NNAPI

يتوفّر مقياس أداء NNAPI على AOSP في platform/test/mlts/benchmark (تطبيق قياس الأداء) وplatform/test/mlts/models (النماذج ومجموعات البيانات).

يقيّم مقياس الأداء وقت الاستجابة والدقة ويقارن بين السائقين وما شابه. باستخدام Tensorflow Lite الذي يتم تشغيله على وحدة المعالجة المركزية (CPU) لنفس الطرازات ومجموعات البيانات.

لاستخدام مقياس الأداء، اتّبِع الخطوات التالية:

  1. وصِّل جهاز Android مستهدفًا بالكمبيوتر، وافتح نافذة طرفية، وتأكَّد من إمكانية الوصول إلى الجهاز من خلال adb.

  2. في حال ربط أكثر من جهاز Android واحد، عليك تصدير الجهاز المستهدَف. متغيّر بيئة واحد (ANDROID_SERIAL)

  3. انتقِل إلى دليل المصدر العالي المستوى على Android.

  4. شغِّل الأوامر التالية:

    lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available
    ./test/mlts/benchmark/build_and_run_benchmark.sh
    

    في نهاية عملية قياس الأداء، سيتم عرض نتائجه في شكل صفحة HTML. تم تمريره إلى xdg-open.

سجلات NNAPI

تنشئ واجهة NNAPI معلومات تشخيصية مفيدة في سجلات النظام. لتحليل السجلات، استخدِم دالة logcat. الأخرى.

يمكنك تفعيل التسجيل المطوَّل NNAPI لمراحل أو مكونات معيّنة من خلال ضبط الخاصية debug.nn.vlog (باستخدام adb shell) إلى قائمة القيم التالية، مفصولة بمسافة أو نقطتين أو فاصلة:

  • model: إنشاء نموذج
  • compilation: إنشاء خطة تنفيذ النموذج وتجميعه
  • execution: تنفيذ النموذج
  • cpuexe: تنفيذ العمليات باستخدام تنفيذ وحدة المعالجة المركزية (CPU) فئة NNAPI
  • manager: معلومات حول إضافات NNAPI وواجهات الاستخدام والإمكانات المتاحة
  • all أو 1: كل العناصر المذكورة أعلاه

على سبيل المثال، لتفعيل أسلوب التسجيل المطوَّل بالكامل، استخدِم الأمر adb shell setprop debug.nn.vlog all. لإيقاف التسجيل المطوَّل، استخدم الأمر adb shell setprop debug.nn.vlog '""'

بعد التمكين، ينشئ التسجيل المطوَّل إدخالات السجل على مستوى المعلومات بعلامة تم تعيينها إلى اسم المرحلة أو المكون.

إلى جانب debug.nn.vlog الرسائل الخاضعة للرقابة، توفّر مكوّنات واجهة برمجة التطبيقات NNAPI إدخالات سجلّ أخرى على مستويات مختلفة، يستخدم كلّ منها علامة سجلّ محدّدة.

للحصول على قائمة بالمكوّنات، ابحث في شجرة المصدر باستخدام التعبير التالي:

grep -R 'define LOG_TAG' | awk -F '"' '{print $2}' | sort -u | egrep -v "Sample|FileTag|test"

يعرض هذا التعبير حاليًا العلامات التالية:

  • BurstBuilder
  • طلبات معاودة الاتصال
  • أداة إنشاء التحويل البرمجي
  • CpuExecutor
  • أداة إنشاء التنفيذ
  • وحدة التحكّم التنفيذية
  • خادم ExecutionBurstServer
  • ExecutionPlan
  • سائق فيبوناتشي
  • تفريغ الرسم البياني
  • ملف IndexedShapeWrapper
  • ساعة IonWatcher
  • مدير
  • الذاكرة
  • أدوات الذاكرة
  • نموذج وصفي
  • ModelArgumentInfo
  • مصمم النماذج
  • NeuralNetworks
  • OperationResolver
  • العمليات
  • OperationsUtils
  • PackageInfo
  • رمز TokenHasher
  • TypeManager
  • Utils
  • ValidateHal
  • واجهات ذات إصدارات

للتحكّم في مستوى رسائل السجلّ التي يعرضها 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 لإنشاء معلومات السجلّ المطوَّل.