מסמך זה מסביר כיצד לאמן מודל ולהפעיל הסקה באמצעות מיקרו-בקר.
הדוגמה של Hello World
הדוגמה של Hello World נועדה להדגים את היסודות המוחלטים של השימוש ב- TensorFlow Lite עבור מיקרו-בקרים. אנו מאמנים ומריצים מודל המשכפל פונקציית סינוס, כלומר, הוא לוקח מספר בודד כקלט שלו, ומוציא את ערך הסינוס של המספר. כאשר הוא נפרס על המיקרו-בקר, החיזויים שלו משמשים להבהב נוריות או לשלוט בהנפשה.
זרימת העבודה מקצה לקצה כוללת את השלבים הבאים:
- אימון מודל (ב-Python): קובץ פיתון לאימון, המרה ואופטימיזציה של מודל לשימוש במכשיר.
- הפעלת הסקת מסקנות (ב-C++ 17): מבחן יחידה מקצה לקצה שמריץ הסקה על המודל באמצעות ספריית C++ .
קבל מכשיר נתמך
האפליקציה לדוגמה שבה נשתמש נבדקה במכשירים הבאים:
- Arduino Nano 33 BLE Sense (באמצעות Arduino IDE)
- SparkFun Edge (בונה ישירות מהמקור)
- ערכת גילוי STM32F746 (באמצעות Mbed)
- Adafruit EdgeBadge (באמצעות Arduino IDE)
- ערכת Adafruit TensorFlow Lite עבור מיקרו-בקרים (באמצעות Arduino IDE)
- Adafruit Circuit Playground Bluefruit (באמצעות Arduino IDE)
- Espressif ESP32-DevKitC (באמצעות ESP IDF)
- Espressif ESP-EYE (באמצעות ESP IDF)
למידע נוסף על פלטפורמות נתמכות ב- TensorFlow Lite עבור מיקרו-בקרים .
לאמן דוגמנית
השתמש ב-train.py לאימון מודל שלום עולמי לזיהוי sinwave
הפעלה: 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
:
הסעיפים הבאים עוברים על evaluate_test.cc
של הדוגמה, מבחן יחידה המדגים כיצד להפעיל הסקה באמצעות TensorFlow Lite עבור מיקרו-בקרים. הוא טוען את המודל ומפעיל הסקה מספר פעמים.
1. כלול את כותרות הספרייה
כדי להשתמש בספריית TensorFlow Lite for Microcontrollers, עלינו לכלול את קבצי הכותרות הבאים:
#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
מספק את הפעולות המשמשות את המתורגמן להפעלת המודל. -
micro_error_reporter.h
מוציא מידע על ניפוי באגים. -
micro_interpreter.h
מכיל קוד לטעינה והרצה של מודלים. -
schema_generated.h
מכיל את הסכימה עבור פורמט הקובץ של דגם TensorFlow LiteFlatBuffer
. -
version.h
מספק מידע ניהול גרסאות עבור סכימת TensorFlow Lite.
2. כלול את כותרת הדגם
מתורגמן TensorFlow Lite for Microcontrollers מצפה שהמודל יסופק כמערך C++. המודל מוגדר בקבצי model.h
ו- model.cc
. הכותרת כלולה בשורה הבאה:
#include "tensorflow/lite/micro/examples/hello_world/model.h"
3. כלול את כותרת המסגרת לבדיקת יחידה
על מנת ליצור בדיקת יחידה, אנו כוללים את מסגרת הבדיקה של יחידת TensorFlow Lite for Microcontrollers על ידי הכללת השורה הבאה:
#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 = µ_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
רושמת את ה-Ops עם ה-Resolver.
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
הוא הפניה לאחד מסוגי הנתונים של TensorFlow Lite, ומוגדר ב- 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);