Ky dokument shpjegon se si të trajnoni një model dhe të ekzekutoni konkluzionet duke përdorur një mikrokontrollues.
Shembulli Hello World
Shembulli Hello World është krijuar për të demonstruar bazat absolute të përdorimit të LiteRT për mikrokontrolluesit. Ne trajnojmë dhe ekzekutojmë një model që përsërit një funksion sinus, dmth, merr një numër të vetëm si hyrje të tij dhe nxjerr vlerën e sinusit të numrit. Kur vendoset te mikrokontrolluesi, parashikimet e tij përdoren ose për të ndezur LED ose për të kontrolluar një animacion.
Rrjedha e punës nga fundi në fund përfshin hapat e mëposhtëm:
- Trajnimi i një modeli (në Python): Një skedar python për të trajnuar, konvertuar dhe optimizuar një model për përdorim në pajisje.
- Ekzekutimi i konkluzionit (në C++ 17): Një test njësie nga fundi në fund që ekzekuton konkluzionet në model duke përdorur bibliotekën C++ .
Merrni një pajisje të mbështetur
Shembulli i aplikacionit që do të përdorim është testuar në pajisjet e mëposhtme:
- Arduino Nano 33 BLE Sense (duke përdorur Arduino IDE)
- SparkFun Edge (ndërtimi direkt nga burimi)
- Kompleti i zbulimit STM32F746 (duke përdorur Mbed)
- Adafruit EdgeBadge (duke përdorur Arduino IDE)
- Adafruit LiteRT për paketën e mikrokontrolluesve (duke përdorur Arduino IDE)
- Adafruit Circuit Playground Bluefruit (duke përdorur Arduino IDE)
- Espressif ESP32-DevKitC (duke përdorur ESP IDF)
- Espressif ESP-EYE (duke përdorur ESP IDF)
Mësoni më shumë rreth platformave të mbështetura në LiteRT për mikrokontrolluesit .
Trajnoni një model
Përdor train.py për trajnimin e modelit hello world për njohjen e valëve mëkate
Run: 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/
Ekzekutoni konkluzionet
Për të ekzekutuar modelin në pajisjen tuaj, ne do të kalojmë nëpër udhëzimet në README.md
:
Seksionet e mëposhtme kalojnë në shembullin e evaluate_test.cc
, testi i njësisë i cili demonstron se si të ekzekutoni konkluzionet duke përdorur LiteRT për mikrokontrolluesit. Ai ngarkon modelin dhe ekzekuton konkluzionet disa herë.
1. Përfshini titujt e bibliotekës
Për të përdorur bibliotekën LiteRT për Mikrokontrolluesit, duhet të përfshijmë skedarët e mëposhtëm të kokës:
#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"
-
micro_mutable_op_resolver.h
ofron operacionet e përdorura nga interpretuesi për të ekzekutuar modelin. -
micro_error_reporter.h
nxjerr informacionin e korrigjimit. -
micro_interpreter.h
përmban kod për të ngarkuar dhe ekzekutuar modelet. -
schema_generated.h
përmban skemën për formatin e skedarit të modelit LiteRTFlatBuffer
. -
version.h
ofron informacione versioni për skemën LiteRT.
2. Përfshi kokën e modelit
Përkthyesi LiteRT për Mikrokontrolluesit pret që modeli të ofrohet si një grup C++. Modeli përcaktohet në skedarët model.h
dhe model.cc
. Titulli përfshihet me rreshtin e mëposhtëm:
#include "tensorflow/lite/micro/examples/hello_world/model.h"
3. Përfshi kokën e kornizës së testit të njësisë
Për të krijuar një test njësie, ne përfshijmë kornizën e testimit të njësisë LiteRT për Mikrokontrollues duke përfshirë linjën e mëposhtme:
#include "tensorflow/lite/micro/testing/micro_test.h"
Testi përcaktohet duke përdorur makrot e mëposhtme:
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(LoadModelAndPerformInference) {
. // add code here
.
}
TF_LITE_MICRO_TESTS_END
Tani diskutojmë kodin e përfshirë në makro më lart.
4. Vendosni regjistrimin
Për të konfiguruar regjistrimin, krijohet një tregues tflite::ErrorReporter
duke përdorur një tregues në një shembull tflite::MicroErrorReporter
:
tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = µ_error_reporter;
Kjo variabël do të kalojë në interpretues, i cili e lejon atë të shkruajë regjistrat. Meqenëse mikrokontrolluesit shpesh kanë një sërë mekanizmash për regjistrim, zbatimi i tflite::MicroErrorReporter
është krijuar për t'u përshtatur për pajisjen tuaj të veçantë.
5. Ngarko një model
Në kodin e mëposhtëm, modeli instantohet duke përdorur të dhëna nga një grup char
, g_model
, i cili është deklaruar në model.h
. Më pas kontrollojmë modelin për t'u siguruar që versioni i skemës së tij është i pajtueshëm me versionin që po përdorim:
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. Zgjidhës i menjëhershëm i operacioneve
Është deklaruar një shembull MicroMutableOpResolver
. Kjo do të përdoret nga përkthyesi për të regjistruar dhe aksesuar operacionet që përdoren nga modeli:
using HelloWorldOpResolver = tflite::MicroMutableOpResolver<1>;
TfLiteStatus RegisterOps(HelloWorldOpResolver& op_resolver) {
TF_LITE_ENSURE_STATUS(op_resolver.AddFullyConnected());
return kTfLiteOk;
MicroMutableOpResolver
kërkon një parametër shabllon që tregon numrin e operacioneve që do të regjistrohen. Funksioni RegisterOps
regjistron funksionet me zgjidhësin.
HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));
7. Alokoni memorien
Ne duhet të paracaktojmë një sasi të caktuar memorie për vargjet hyrëse, dalëse dhe të ndërmjetme. Kjo ofrohet si një grup uint8_t
me madhësi tensor_arena_size
:
const int tensor_arena_size = 2 * 1024;
uint8_t tensor_arena[tensor_arena_size];
Madhësia e kërkuar do të varet nga modeli që po përdorni dhe mund të duhet të përcaktohet me eksperiment.
8. Përkthyes i çastit
Ne krijojmë një shembull tflite::MicroInterpreter
, duke kaluar në variablat e krijuar më parë:
tflite::MicroInterpreter interpreter(model, resolver, tensor_arena,
tensor_arena_size, error_reporter);
9. Alokoni tensorët
Ne i themi interpretuesit të ndajë memorie nga tensor_arena
për tensorët e modelit:
interpreter.AllocateTensors();
10. Vërtetoni formën e hyrjes
Shembulli MicroInterpreter
mund të na japë një tregues për tensorin hyrës të modelit duke thirrur .input(0)
, ku 0
përfaqëson tensorin e parë (dhe të vetëm) hyrës:
// Obtain a pointer to the model's input tensor
TfLiteTensor* input = interpreter.input(0);
Më pas e inspektojmë këtë tensor për të konfirmuar që forma dhe lloji i tij janë ato që presim:
// 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);
Vlera e numrit kTfLiteFloat32
është një referencë për një nga llojet e të dhënave LiteRT dhe është përcaktuar në common.h
.
11. Jepni një vlerë hyrëse
Për të siguruar një hyrje në model, ne vendosim përmbajtjen e tensorit të hyrjes, si më poshtë:
input->data.f[0] = 0.;
Në këtë rast, ne futim një vlerë me pikë lundruese që përfaqëson 0
.
12. Drejtoni modelin
Për të ekzekutuar modelin, ne mund të thërrasim Invoke()
në shembullin tonë tflite::MicroInterpreter
:
TfLiteStatus invoke_status = interpreter.Invoke();
if (invoke_status != kTfLiteOk) {
TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed\n");
}
Ne mund të kontrollojmë vlerën e kthimit, një TfLiteStatus
, për të përcaktuar nëse ekzekutimi ishte i suksesshëm. Vlerat e mundshme të TfLiteStatus
, të përcaktuara në common.h
, janë kTfLiteOk
dhe kTfLiteError
.
Kodi i mëposhtëm pohon se vlera është kTfLiteOk
, që do të thotë se përfundimi u ekzekutua me sukses.
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);
13. Merrni rezultatin
Tenzori i daljes së modelit mund të merret duke thirrur output(0)
në tflite::MicroInterpreter
, ku 0
përfaqëson tensorin e parë (dhe të vetëm) të daljes.
Në shembull, dalja e modelit është një vlerë e vetme me pikë lundruese e përfshirë brenda një tensor 2D:
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);
Ne mund ta lexojmë vlerën drejtpërdrejt nga tensori i daljes dhe të pohojmë se është ajo që presim:
// 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. Drejtoni përsëri përfundimin
Pjesa e mbetur e kodit ekzekuton konkluzionet disa herë të tjera. Në çdo rast, ne i caktojmë një vlerë tensorit të hyrjes, thërrasim interpretuesin dhe lexojmë rezultatin nga tensori i daljes:
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);