ابدأ مع ميكروكنترولر

يشرح هذا المستند كيفية تدريب نموذج وتشغيل الاستدلال باستخدام متحكم دقيق.

مثال Hello World

تم تصميم مثال Hello World لتوضيح الأساسيات المطلقة لاستخدام TensorFlow Lite للميكروكونترولر. نحن ندرب ونشغل نموذجًا يكرر دالة الجيب ، أي يأخذ رقمًا واحدًا كمدخل له ، ويخرج قيمة الجيب للرقم. عند نشره في وحدة التحكم الدقيقة ، يتم استخدام تنبؤاته إما لميض مصابيح LED أو التحكم في الرسوم المتحركة.

يتضمن سير العمل الشامل الخطوات التالية:

  1. تدريب نموذج (في Python): دفتر jupyter لتدريب نموذج وتحويله وتحسينه للاستخدام على الجهاز.
  2. تشغيل الاستدلال (في C ++ 11): اختبار وحدة شامل يقوم بتشغيل الاستدلال على النموذج باستخدام مكتبة C ++ .

احصل على جهاز مدعوم

تم اختبار التطبيق النموذجي الذي سنستخدمه على الأجهزة التالية:

تعرف على المزيد حول الأنظمة الأساسية المدعومة في TensorFlow Lite لأجهزة التحكم الدقيقة .

تدريب نموذج

استخدم Google Colaboratory لتدريب نموذجك الخاص . لمزيد من التفاصيل ، راجع الملف README.md :

مرحبًا بالعالم للتدريب README.md

تشغيل الاستدلال

لتشغيل النموذج على جهازك ، سننتقل عبر التعليمات الواردة في README.md :

مرحبًا بالعالم README.md

تتناول الأقسام التالية اختبار الوحدة hello_world_test.cc الخاص بالمثال الذي يوضح كيفية تشغيل الاستدلال باستخدام TensorFlow Lite for Microcontrollers. يقوم بتحميل النموذج وتشغيل الاستدلال عدة مرات.

1. تضمين رؤوس المكتبة

لاستخدام مكتبة TensorFlow Lite for Microcontrollers ، يجب علينا تضمين ملفات الرأس التالية:

#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/version.h"

2. تضمين رأس النموذج

يتوقع مترجم TensorFlow Lite for Microcontrollers أن يتم توفير النموذج كمصفوفة C ++. يتم تعريف النموذج في ملفات model.h و model.cc . الرأس مضمن في السطر التالي:

#include "tensorflow/lite/micro/examples/hello_world/model.h"

3. قم بتضمين رأس إطار اختبار الوحدة

من أجل إنشاء اختبار وحدة ، نقوم بتضمين إطار عمل اختبار وحدة TensorFlow Lite لوحدات التحكم الدقيقة من خلال تضمين السطر التالي:

#include "tensorflow/lite/micro/testing/micro_test.h"

يتم تحديد الاختبار باستخدام وحدات الماكرو التالية:

TF_LITE_MICRO_TESTS_BEGIN

TF_LITE_MICRO_TEST(LoadModelAndPerformInference) {
  . // add code here
  .
}

TF_LITE_MICRO_TESTS_END

نناقش الآن الكود المتضمن في الماكرو أعلاه.

4. إعداد التسجيل

لإعداد التسجيل ، يتم إنشاء مؤشر tflite::ErrorReporter باستخدام مؤشر إلى مثيل tflite::MicroErrorReporter :

tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = &micro_error_reporter;

سيتم تمرير هذا المتغير إلى المترجم ، مما يسمح له بكتابة السجلات. نظرًا لأن المتحكمات الدقيقة غالبًا ما تحتوي على مجموعة متنوعة من آليات التسجيل ، فإن تنفيذ tflite::MicroErrorReporter مصمم للتخصيص لجهازك الخاص.

5. تحميل نموذج

في الكود التالي ، يتم إنشاء مثيل للنموذج باستخدام بيانات من مصفوفة char ، g_model ، التي تم التصريح عنها في model.h . ثم نتحقق من النموذج للتأكد من أن إصدار المخطط الخاص به متوافق مع الإصدار الذي نستخدمه:

const tflite::Model* model = ::tflite::GetModel(g_model);
if (model->version() != TFLITE_SCHEMA_VERSION) {
  TF_LITE_REPORT_ERROR(error_reporter,
      "Model provided is schema version %d not equal "
      "to supported version %d.\n",
      model->version(), TFLITE_SCHEMA_VERSION);
}

6. إنشاء محلل العمليات

تم التصريح عن مثيل AllOpsResolver . سيستخدم المترجم هذا للوصول إلى العمليات التي يستخدمها النموذج:

tflite::AllOpsResolver resolver;

يقوم AllOpsResolver بتحميل جميع العمليات المتاحة في TensorFlow Lite لأجهزة التحكم الدقيقة ، والتي تستخدم قدرًا كبيرًا من الذاكرة. نظرًا لأن نموذجًا معينًا سيستخدم فقط مجموعة فرعية من هذه العمليات ، فمن المستحسن أن تقوم تطبيقات العالم الحقيقي بتحميل العمليات المطلوبة فقط.

يتم ذلك باستخدام فئة مختلفة ، MicroMutableOpResolver . يمكنك معرفة كيفية استخدامه في micro_speech_test.cc في مثال micro_speech_test.cc .

7. تخصيص الذاكرة

نحتاج إلى تخصيص قدر معين من الذاكرة مسبقًا للإدخال والإخراج والمصفوفات الوسيطة. يتم توفير هذا uint8_t بحجم tensor_arena_size :

const int tensor_arena_size = 2 * 1024;
uint8_t tensor_arena[tensor_arena_size];

سيعتمد الحجم المطلوب على النموذج الذي تستخدمه ، وقد يلزم تحديده بالتجربة.

8. إنشاء مترجم فوري

نقوم بإنشاء مثيل tflite::MicroInterpreter ، لتمرير المتغيرات التي تم إنشاؤها مسبقًا:

tflite::MicroInterpreter interpreter(model, resolver, tensor_arena,
                                     tensor_arena_size, error_reporter);

9. تخصيص موترات

نقول للمترجم أن يخصص ذاكرة من tensor_arena النموذج:

interpreter.AllocateTensors();

10. التحقق من صحة شكل الإدخال

يمكن لمثيل MicroInterpreter أن يزودنا بمؤشر لموتّر إدخال النموذج عن طريق استدعاء .input(0) ، حيث يمثل 0 موتر الإدخال الأول (والوحيد):

  // Obtain a pointer to the model's input tensor
  TfLiteTensor* input = interpreter.input(0);

ثم نفحص هذا الموتر للتأكد من أن شكله ونوعه هما ما نتوقعه:

// Make sure the input has the properties we expect
TF_LITE_MICRO_EXPECT_NE(nullptr, input);
// The property "dims" tells us the tensor's shape. It has one element for
// each dimension. Our input is a 2D tensor containing 1 element, so "dims"
// should have size 2.
TF_LITE_MICRO_EXPECT_EQ(2, input->dims->size);
// The value of each element gives the length of the corresponding tensor.
// We should expect two single element tensors (one is contained within the
// other).
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
// The input is a 32 bit floating point value
TF_LITE_MICRO_EXPECT_EQ(kTfLiteFloat32, input->type);

قيمة التعداد kTfLiteFloat32 هي إشارة إلى أحد أنواع بيانات common.h Lite ، ويتم تعريفها بشكل مشترك.

11. قدم قيمة إدخال

لتوفير مدخلات للنموذج ، قمنا بتعيين محتويات موتر الإدخال ، على النحو التالي:

input->data.f[0] = 0.;

في هذه الحالة ، نقوم بإدخال قيمة النقطة العائمة التي تمثل 0 .

12. قم بتشغيل النموذج

لتشغيل النموذج ، يمكننا استدعاء Invoke() على مثيل tflite::MicroInterpreter :

TfLiteStatus invoke_status = interpreter.Invoke();
if (invoke_status != kTfLiteOk) {
  TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed\n");
}

يمكننا التحقق من قيمة الإرجاع ، TfLiteStatus ، لتحديد ما إذا كان التشغيل ناجحًا. القيم المحتملة لـ TfLiteStatus ، المعرفة بشكل عام ، هي common.h kTfLiteError kTfLiteOk

يؤكد الكود التالي أن القيمة هي kTfLiteOk ، مما يعني أنه تم تشغيل الاستدلال بنجاح.

TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);

13. الحصول على الإخراج

يمكن الحصول على موتر إخراج النموذج عن طريق استدعاء output(0) على tflite::MicroInterpreter ، حيث يمثل 0 موتر الإخراج الأول (والوحيد).

في المثال ، ناتج النموذج عبارة عن قيمة فاصلة عائمة واحدة متضمنة في موتر ثنائي الأبعاد:

TfLiteTensor* output = interpreter.output(0);
TF_LITE_MICRO_EXPECT_EQ(2, output->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteFloat32, output->type);

يمكننا قراءة القيمة مباشرة من موتر الإخراج والتأكيد على أنه ما نتوقعه:

// Obtain the output value from the tensor
float value = output->data.f[0];
// Check that the output value is within 0.05 of the expected value
TF_LITE_MICRO_EXPECT_NEAR(0., value, 0.05);

14. قم بتشغيل الاستدلال مرة أخرى

يتم تشغيل ما تبقى من الكود الاستدلال عدة مرات. في كل حالة ، نقوم بتعيين قيمة لموتّر الإدخال ، واستدعاء المترجم الفوري ، وقراءة النتيجة من موتر الإخراج:

input->data.f[0] = 1.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(0.841, value, 0.05);

input->data.f[0] = 3.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(0.141, value, 0.05);

input->data.f[0] = 5.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(-0.959, value, 0.05);

15. اقرأ كود التطبيق

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