Erste Schritte mit Mikrocontrollern

In diesem Dokument wird erläutert, wie Sie ein Modell trainieren und Inferenz mithilfe eines Mikrocontrollers ausführen.

Das Hello World-Beispiel

Das Hallo Welt Beispiel ist auf die absoluten Grundlagen der Verwendung TensorFlow Lite für Mikrocontroller zu demonstrieren. Wir bilden und ein Modell ausführen , die eine Sinusfunktion nachbildet, das heißt, es dauert eine einzige Zahl als seine Eingabe, und gibt die Nummer Sinuswert. Bei der Bereitstellung auf dem Mikrocontroller werden seine Vorhersagen verwendet, um entweder LEDs zu blinken oder eine Animation zu steuern.

Der End-to-End-Workflow umfasst die folgenden Schritte:

  1. Trainieren ein Modell (in Python): Ein jupyter Notebook Zug, zu konvertieren und optimieren , ein Modell für On-Device - Einsatz.
  2. Inferenz ausgeführt (in C ++ 11): Ein End-to-End - Gerät zu testen , die ausgeführt wird auf dem Modell Inferenz unter Verwendung der C ++ Bibliothek .

Holen Sie sich ein unterstütztes Gerät

Die von uns verwendete Beispielanwendung wurde auf den folgenden Geräten getestet:

Weitere Informationen zu unterstützten Plattformen in TensorFlow Lite für Mikrocontroller .

Trainiere ein Modell

Verwenden Sie Google Colaboratory auf Ihr eigenes Modell zu trainieren . Weitere Einzelheiten finden Sie in der README.md :

Hello World Training README.md

Inferenz ausführen

Um das Modell auf dem Gerät läuft, werden wir durch die Anweisungen in dem zu Fuß README.md :

Hallo Welt README.md

In den folgenden Abschnitten zu Fuß durch die dem Beispiel hello_world_test.cc , Unit - Test , der zeigt , wie Inferenz mit TensorFlow Lite für Mikrocontroller auszuführen. Es lädt das Modell und führt die Inferenz mehrmals aus.

1. Fügen Sie die Header der Bibliothek ein

Um die TensorFlow Lite for Microcontrollers-Bibliothek zu verwenden, müssen wir die folgenden Header-Dateien einschließen:

#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. Fügen Sie den Modell-Header ein. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Der TensorFlow Lite für Mikrocontroller-Interpreter erwartet, dass das Modell als C++-Array bereitgestellt wird. Das Modell wird in definierten model.h und model.cc Dateien. Der Header ist in der folgenden Zeile enthalten:

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

3. Fügen Sie den Header des Unit-Test-Frameworks ein

Um einen Unit-Test zu erstellen, binden wir das Unit-Test-Framework TensorFlow Lite für Mikrocontroller ein, indem wir die folgende Zeile einfügen:

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

Der Test wird mit den folgenden Makros definiert:

TF_LITE_MICRO_TESTS_BEGIN

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

TF_LITE_MICRO_TESTS_END

Wir besprechen nun den Code, der im obigen Makro enthalten ist.

4. Protokollierung einrichten

So richten Sie die Protokollierung, ein tflite::ErrorReporter Zeiger erzeugt einen Zeiger auf ein mit tflite::MicroErrorReporter Beispiel:

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

Diese Variable wird an den Interpreter übergeben, der es ihm ermöglicht, Logs zu schreiben. Da oft Mikrocontroller eine Vielzahl von Mechanismen zur Protokollierung haben, die Umsetzung der tflite::MicroErrorReporter ist für das jeweilige Gerät angepasst werden.

5. Laden Sie ein Modell

In dem folgenden Code wird das Modell unter Verwendung von Daten aus einem instanziiert char array, g_model , das erklärt wird model.h . Anschließend überprüfen wir das Modell, um sicherzustellen, dass seine Schemaversion mit der von uns verwendeten Version kompatibel ist:

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. Operationsresolver instanziieren

Eine AllOpsResolver Instanz deklariert. Dies wird vom Interpreter verwendet, um auf die Operationen zuzugreifen, die vom Modell verwendet werden:

tflite::AllOpsResolver resolver;

Die AllOpsResolver Lasten zur Verfügung alle der Operationen in TensorFlow Lite für Mikrocontroller, die viel Speicher verwendet. Da ein bestimmtes Modell nur eine Teilmenge dieser Operationen verwendet, wird empfohlen, dass reale Anwendungen nur die benötigten Operationen laden.

Dies geschieht , um eine andere Klasse verwenden, MicroMutableOpResolver . Sie können sehen , wie es zu benutzen in der Micro Sprach Beispiel des micro_speech_test.cc .

7. Speicher zuweisen

Wir müssen eine bestimmte Speichermenge für Eingabe-, Ausgabe- und Zwischenarrays vorab zuweisen. Dies wird als ein bereitgestellt uint8_t Array der Größe tensor_arena_size :

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

Die erforderliche Größe hängt vom verwendeten Modell ab und muss möglicherweise durch Experimente bestimmt werden.

8. Interpreter instanziieren

Wir schaffen eine tflite::MicroInterpreter Beispiel in den zuvor erstellten Variablen übergeben:

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

9. Tensoren zuweisen

Wir sagen den Interpreter Speicher aus dem zuzuteilen tensor_arena für das Modell der Tensoren:

interpreter.AllocateTensors();

10. Eingabeform validieren

Die MicroInterpreter Instanz kann uns diese mit einem Zeiger auf das Eingang Tensor des Modells durch den Aufruf .input(0) , wobei 0 den ersten (und einzigen) -Eingang Tensor darstellt:

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

Wir untersuchen dann diesen Tensor, um zu bestätigen, dass seine Form und sein Typ unseren Erwartungen entsprechen:

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

Der enum - Wert kTfLiteFloat32 ist ein Verweis auf eine der TensorFlow Lite - Datentypen und ist in definierten common.h .

11. Geben Sie einen Eingabewert ein

Um eine Eingabe für das Modell bereitzustellen, legen wir den Inhalt des Eingabetensors wie folgt fest:

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

In diesem Fall wir Eingang eines Gleitkommawert repräsentiert 0 .

12. Führen Sie das Modell aus

Um das Modell zu laufen, können wir nennen Invoke() auf unserer tflite::MicroInterpreter Beispiel:

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

Wir können den Rückgabewert überprüfen, eine TfLiteStatus , um zu bestimmen , ob der Lauf erfolgreich war. Die möglichen Werte von TfLiteStatus , in definierten common.h , sind kTfLiteOk und kTfLiteError .

Der folgende Code macht geltend , dass der Wert kTfLiteOk , Inferenz wurde Sinn erfolgreich ausgeführt werden .

TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);

13. Erhalten Sie die Ausgabe

Das Ausgang Tensor des Modells kann durch den Aufruf erhalten wird output(0) auf dem tflite::MicroInterpreter , wo 0 ist die erste (und einzige) Ausgabe Tensor.

Im Beispiel ist die Ausgabe des Modells ein einzelner Gleitkommawert, der in einem 2D-Tensor enthalten ist:

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

Wir können den Wert direkt aus dem Ausgabetensor ablesen und behaupten, dass es das ist, was wir erwarten:

// 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. Führen Sie die Inferenz erneut aus

Der Rest des Codes führt die Inferenz mehrmals aus. In jedem Fall weisen wir dem Eingabetensor einen Wert zu, rufen den Interpreter auf und lesen das Ergebnis aus dem Ausgabetensor:

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. Lesen Sie den Anwendungscode

Sobald Sie durch dieses Gerät zu testen gegangen sind, sollten Sie in der Lage sein , dem Beispiel des Anwendungscodes zu verstehen, in sich main_functions.cc . Es folgt einem ähnlichen Prozess, generiert jedoch einen Eingabewert basierend auf der Anzahl ausgeführter Inferenzen und ruft eine gerätespezifische Funktion auf, die dem Benutzer die Ausgabe des Modells anzeigt.