با میکروکنترلرها شروع کنید

این سند نحوه آموزش یک مدل و اجرای استنتاج با استفاده از یک میکروکنترلر را توضیح می دهد.

مثال سلام جهان

مثال Hello World برای نشان دادن اصول اولیه استفاده از LiteRT برای میکروکنترلرها طراحی شده است. ما مدلی را آموزش می‌دهیم و اجرا می‌کنیم که یک تابع سینوسی را تکرار می‌کند، یعنی یک عدد را به عنوان ورودی می‌گیرد و مقدار سینوسی عدد را خروجی می‌کند. هنگامی که روی میکروکنترلر مستقر می شود، پیش بینی های آن برای چشمک زدن LED ها یا کنترل یک انیمیشن استفاده می شود.

گردش کار انتها به انتها شامل مراحل زیر است:

  1. آموزش یک مدل (در پایتون): یک فایل پایتون برای آموزش، تبدیل و بهینه سازی یک مدل برای استفاده در دستگاه.
  2. اجرای استنتاج (در C++ 17): یک تست واحد سرتاسری که استنتاج را روی مدل با استفاده از کتابخانه C++ اجرا می‌کند.

یک دستگاه پشتیبانی شده دریافت کنید

برنامه نمونه ای که ما استفاده خواهیم کرد در دستگاه های زیر آزمایش شده است:

درباره پلتفرم های پشتیبانی شده در LiteRT برای میکروکنترلرها بیشتر بیاموزید.

یک مدل تربیت کنید

از train.py برای آموزش مدل hello world برای تشخیص امواج سینوسی استفاده کنید

اجرا: bazel build tensorflow/lite/micro/examples/hello_world:train bazel-bin/tensorflow/lite/micro/examples/hello_world/train --save_tf_model --save_dir=/tmp/model_created/

استنتاج را اجرا کنید

برای اجرای مدل بر روی دستگاه شما، دستورالعمل‌های موجود در README.md را بررسی می‌کنیم:

سلام جهان README.md

بخش‌های زیر از آزمون واحد evaluate_test.cc این مثال استفاده می‌کنند که نحوه اجرای استنتاج با استفاده از LiteRT برای میکروکنترلرها را نشان می‌دهد. مدل را بارگذاری می کند و استنتاج را چندین بار اجرا می کند.

1. سرصفحه های کتابخانه را وارد کنید

برای استفاده از کتابخانه LiteRT برای میکروکنترلرها، باید فایل‌های هدر زیر را اضافه کنیم:

#include "tensorflow/lite/micro/micro_mutable_op_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. هدر مدل را وارد کنید

مفسر LiteRT برای میکروکنترلرها انتظار دارد که مدل به صورت یک آرایه ++C ارائه شود. مدل در فایل های model.h و model.cc تعریف شده است. هدر با خط زیر همراه است:

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

3. سربرگ چارچوب تست واحد را وارد کنید

برای ایجاد یک تست واحد، چارچوب تست واحد LiteRT برای میکروکنترلرها را با گنجاندن خط زیر اضافه می کنیم:

#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. حل کننده عملیات آنی

یک نمونه MicroMutableOpResolver اعلام شده است. این توسط مفسر برای ثبت و دسترسی به عملیاتی که توسط مدل استفاده می شود استفاده می شود:

using HelloWorldOpResolver = tflite::MicroMutableOpResolver<1>;

TfLiteStatus RegisterOps(HelloWorldOpResolver& op_resolver) {
  TF_LITE_ENSURE_STATUS(op_resolver.AddFullyConnected());
  return kTfLiteOk;

MicroMutableOpResolver به یک پارامتر الگو نیاز دارد که تعداد عملیات ثبت شده را نشان دهد. تابع RegisterOps عملیات را با حل کننده ثبت می کند.

HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));

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);

مقدار enum kTfLiteFloat32 ارجاع به یکی از انواع داده LiteRT است و به صورت common.h تعریف شده است.

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 تعریف شده اند kTfLiteOk و kTfLiteError هستند.

کد زیر بیان می کند که مقدار 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);