Guide Wie man TinyML auf MCUs verwendet

Von Nikolas Rieder* & Rafael Tappe Maestro* |

Anbieter zum Thema

Sie sind interessiert an Künstlicher Intelligenz (KI), am Machine Learning (ML) und wollen diese Technologie auf ihnen bekannten Mikrocontrollern verwenden? In diesem Artikel führen wir Sie anhand eines Beispiels in das Thema TinyML ein.

Praktischer Guide: Wie man TinyML auf MCUs verwendet
Praktischer Guide: Wie man TinyML auf MCUs verwendet
(Bild: gemeinfrei / Pixabay)

Sicher haben Sie schon von Technologieunternehmen wie DeepMind und OpenAI gehört. Sie dominieren die ML-Domäne mit Experten und GPU-Power. Die leistungsfähigsten KIs, wie sie von Google Translate verwendet werden, benötigen monatelanges Training. Sie verwenden hunderte von Hochleistung-GPUs gleichzeitig. TinyML dreht den Spieß etwas um, mit einem Fokus auf das Kleine, denn aufgrund von Speicherbeschränkungen passen große KI-Modelle nicht auf Mikrocontroller. Die folgende Abbildung zeigt die Unterschiede der Hardwarebedingungen zwischen PCs und Mikrocontrollern.

(Bild: Itemis)

Welche Vorteile bietet ML auf MCUs gegenüber der Nutzung von KI-Diensten in der Cloud? Wir finden sieben Argumente.

Kosten

Mikrocontroller sind kostengünstig in der Anschaffung und im Betrieb.

Umweltfreundlich

Das Betreiben von KI auf Mikrocontrollern verbraucht wenig Energie.

Integration

Mikrocontroller lassen sich einfach in bestehende Umgebungen, beispielsweise Produktionslinien, integrieren.

Datenschutz

Daten können lokal auf dem Gerät verarbeitet werden und müssen nicht über das Internet versendet werden.

Schnelle Prototypenentwicklung

TinyML ermöglicht es Ihnen, Proof of Concept Lösungen in kurzer Zeit zu entwickeln.

Autonom und verlässlich

TinyML Geräte können überall eingesetzt werden, auch wenn keine Infrastruktur vorhanden ist.

Echtzeit

Die Daten werden ohne Latenz auf dem Mikrocontroller verarbeitet. Die einzige Einschränkung ist die Verarbeitungsgeschwindigkeit der MCU.

Schere, Stein, Papier

Sie spielen gegen ein ESP-EYE Board, das TinyML verwendet. Unser Projekt zur spielenden KI umfasst fünf Schritte. Jedem Schritt ist einer der folgenden Abschnitte gewidmet. Weitere Details gibt es in der Dokumentation unseres Repositorys.

Das Sammeln von Daten ist essentiell im Machine Learning. Um loszulegen, beginnen wir mit der Aufnahme einer Hand in den Gesten Schere, Stein und Papier. Die Bilder sollten möglichst unterschiedlich sein. Die KI lernt daraus, dass die Hand verschiedene Positionen und Winkel einnehmen kann, und dass Lichtverhältnisse variieren. Ein vollständiger Datensatz verbindet mit jedem Bild eine Klasse. Dieser Ansatz ist bekannt als überwachtes Lernen oder Supervised Learning.

Es ist am besten, wenn Sensoren und Umgebung beim Feldeinsatz der KI gleich mit denen sind, die auch zum Datensammeln verwendet wurden. Dadurch wird sichergestellt, dass das Modell mit den eingehenden Daten vertraut ist. Denken Sie zum Beispiel an Temperatursensoren, die aufgrund von Herstellungsunterschieden verschiedene Spannungen für die gleiche Temperatur ausgeben. Für unseren Zweck bedeutet dies, dass die Aufnahme von Bildern mit der ESP-EYE Kamera auf einem einheitlichen Hintergrund ideal ist. Sie können Bilder auch mit einer Webcam aufnehmen, allerdings auf Kosten der Genauigkeit. Aufgrund der begrenzten Kapazität der MCU wollen wir mit Graustufenbildern mit 96 × 96 Pixeln arbeiten.

(Bild: Itemis)

Nach dem Sammeln von Daten ist es wichtig, die Daten in ein Trainings- und ein Testset aufzuteilen. Wir tun dies, um zu sehen, wie gut unser Modell Bilder von Handgesten erkennt, die es zuvor noch nicht gesehen hat. Bei Bildern, die es bereits im Training gesehen hat, wird das Modell gut abschneiden.

Hier sind einige Beispielbilder. Wenn Sie Zeit sparen wollen und das Datensammeln umgehen möchten, finden Sie hier unseren vorgefertigten Datensatz.

Datenaufbereitung

Mustererkennung ist nicht nur für Menschen schwierig. Um die KI zu unterstützen, ist es üblich, Daten vor dem Training aufzubereiten, auch Preprocessing genannt. Unser Datensatz besteht aus Webcambildern und Fotos mit der ESP-EYE Kamera. Weil der ESP-EYE Bilder in Graustufe mit 96 × 96 Auflösung aufnehmen kann, ist hier keine weitere Verarbeitung notwendig. Die Webcambilder müssen in ihrer Auflösung verringert und auf das richtige Format zugeschnitten werden. Darauf folgt die Umwandlung von RGB zu Graustufe. Zuletzt werden alle Bilder normalisiert. Am folgenden Beispiel sind die einzelnen Aufbereitungsschritte nachzuvollziehen.

Modell Architektur

Das Entwerfen eines Modells ist gar nicht so einfach und ins Detail zu gehen sprengt den Rahmen des Artikels. Wir werden die grundlegenden Komponenten eines Modells beschreiben und erläutern, wie wir unser Modell entworfen haben. Unsere KI basiert auf einem neuronalen Netzwerk. Sie können sich ein neuronales Netzwerk als eine Ansammlung von Neuronen vorstellen, ein bisschen wie in unserem Gehirn.

Wenn alle Neuronen in einem Netzwerk miteinander verbunden sind, spricht man von vollständig verbundenen Netz. Dies kann als die grundlegendste Art eines neuronalen Netzwerks angesehen werden. Da wir möchten, dass unsere KI Handgesten aus Bildern erkennt, verwenden wir ein für Bilder geeigneteres Convolutional Neural Network (CNN). Faltungen (Convolutions) reduzieren die Dimensionalität von Bildern, extrahieren wichtige Muster und erhalten lokale Beziehungen zwischen Pixeln. Um ein Modell zu entwerfen, verwenden wir die TensorFlow-Bibliothek. Sie enthält vorgefertigte neuronale Netzwerkkomponenten, sogenannte Schichten oder Ebenen; dies vereinfacht die Erstellung neuronaler Netze.

Jetzt Newsletter abonnieren

Verpassen Sie nicht unsere besten Inhalte

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Ein Modell besteht aus einer Verkettung verschiedener Verarbeitungsebenen. Die richtige Auswahl und Verkettung der Ebenen ist entscheidend, um ein robustes und genaues Modell zu entwerfen. Das folgende Bild zeigt die von uns verwendeten Ebenen. Conv2D steht für eine Faltungsebene (Convolution). Die BatchNormalization Ebene wendet eine Form der Normalisierung auf die Ausgabe der darüber liegenden Ebene an. Daraufhin werden die Daten in eine Aktivierungsebene gespeist; hier werden nichtlineare Eigenschaften eingeführt und unwichtige Datenpunkte werden herausgefiltert. Diese Aneinanderreihung von Ebenen wird einige Male wiederholt; wie oft wird durch Erfahrung und Versuche bestimmt. Danach wird die Ebene Flatten verwendet, um zweidimensionale Bilder in ein eindimensionales Array zu übersetzen. Zuletzt, wird jedes Element der Array Ebene mit drei Ausgabeneuronen verbunden, welche die drei Klassen Schere, Stein, Papier darstellen.

def make_model_simple_cnn(INPUT_IMG_SHAPE, num_classes=3): inputs = keras.Input(shape=INPUT_IMG_SHAPE) x = inputs x = layers.Rescaling(1.0 / 255)(x) x = layers.Conv2D(16, 3, strides=3, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) x = layers.MaxPooling2D()(x) x = layers.Conv2D(32, 3, strides=2, padding="same", activation="relu")(x) x = layers.MaxPooling2D()(x) x = layers.Conv2D(64, 3, padding="same", activation="relu")(x) x = layers.MaxPooling2D()(x) x = layers.Flatten()(x) x = layers.Dropout(0.5)(x) outputs = layers.Dense(units=num_classes, activation="softmax")(x) return keras.Model(inputs, outputs)

Training eines Modells

Sobald wir ein Modell entworfen haben, kann es trainiert werden. Zunächst trifft das KI-Modell zufällige Vorhersagen. Eine Vorhersage ist eine Wahrscheinlichkeit, die mit einem Label verbunden ist; die Labels sind Schere, Stein, Papier. Die KI teilt uns mit, wie wahrscheinlich es ist, dass es sich bei einem Bild um ein bestimmtes Label handelt. Da die KI zu Beginn Labels errät, wird sie oft falsch liegen. Das Training erfolgt, nachdem das vorhergesagte Label mit dem tatsächlichen Label verglichen wird. Vorhersagefehler führen zu Aktualisierungen zwischen den Neuronen im Netzwerk. Diese Form des Lernens wird Gradientenabstieg genannt. Da wir unser Modell in TensorFlow erstellt haben, muss das Training nicht implementiert werden. Unten sehen Sie die Ausgabe während des Trainings – je höher die Genauigkeit (Trainingssatz) und Validierungsgenauigkeit (Testsatz), desto besser!

Epoch 1/6480/480 [==============================] - 17s 34ms/step - loss: 0.4738 - accuracy: 0.6579 - val_loss: 0.3744 - val_accuracy: 0.8718Epoch 2/6216/480 [============>.................] - ETA: 7s - loss: 0.2753 - accuracy: 0.8436

Während des Trainings können mehrere Probleme auftreten. Das häufigste Problem ist das Overfitting. Da das Modell immer wieder denselben Beispielen ausgesetzt ist, beginnt es, die Trainingsdaten auswendig zu lernen, anstatt die zugrunde liegenden Muster zu lernen. Aus der Schule werden sie erinnern, dass Verstehen besser ist als Auswendiglernen. Sobald die Genauigkeit gegenüber den Trainingsdaten weiter steigt, während die Validierungsgenauigkeit (Testsatz) nicht weiter steigt, spricht man von Overfitting.

Modell Umwandlung

Nach dem Training erhalten wir ein KI-Modell im TensorFlow-Format. Da der ESP-EYE dieses Format nicht interpretieren kann, konvertieren wir das Modell in ein mikroprozessorlesbares Format. Die Konvertierung beginnt mit der Umwandlung in ein TfLite-Modell. TfLite ist ein kompakteres TensorFlow-Format. Es verringert die Größe des Modells durch Quantisierung. TfLite wird häufig in Edge-Geräten wie Smartphones oder Tablets verwendet. Der letzte Schritt besteht darin, das TfLite-Modell in ein C-Array umzuwandeln, da der Mikrocontroller nicht in der Lage ist, TfLite direkt zu interpretieren.

Modell Bereitstellung

Jetzt können wir unser Modell auf dem Mikroprozessor bereitstellen. Hierzu müssen wir das neue C-Array in die vorgesehene Datei zu platzieren. Ersetzen Sie den Inhalt des C-Arrays und vergessen Sie nicht, die Array-Längenvariable am Ende der Datei ebenfalls zu ersetzen. Wir haben ein Skript bereitgestellt, das diesen Schritt automatisiert.

Eingebettete Umgebung

Sehen wir uns an, was auf der MCU passiert. Während der Einrichtung wird der Interpreter auf das Maß unserer Bilder konfiguriert.

// initialize interpreterstatic tflite::MicroInterpreter static_interpreter( model, resolver, tensor_arena, kTensorArenaSize, error_reporter);interpreter = &static_interpreter;model_input = interpreter->input(0);model_output = interpreter->output(0);// assert real input matches expect inputif ((model_input->dims->size != 4) || // tensor of shape (1, 96, 96, 1) has dim 4 (model_input->dims->data[0] != 1) || // 1 img per batch (model_input->dims->data[1] != 96) || // 96 x pixels (model_input->dims->data[2] != 96) || // 96 y pixels (model_input->dims->data[3] != 1) || // 1 channel (grayscale) (model_input->type != kTfLiteFloat32)) { // type of a single data point, here a pixel error_reporter->Report("Bad input tensor parameters in model\n"); return;}

Nachdem die Einrichtung abgeschlossen ist, werden erfasste Bilder an das Modell geschickt; hier findet die Gestenerkennung statt.

// read image from camera into a 1-dimensional arrayuint8_t img[dim1*dim2*dim3]if (kTfLiteOk != GetImage(error_reporter, dim1, dim2, dim3, img)) { TF_LITE_REPORT_ERROR(error_reporter, "Image capture failed.");} // write image to model std::vector<uint8_t> img_vec(img, img + dim1*dim2*dim3); std::vector<float_t> img_float(img_vec.begin(), img_vec.end()); std::copy(img_float.begin(), img_float.end(), model_input->data.f); // apply inference TfLiteStatus invoke_status = interpreter->Invoke();}

Das Modell gibt eine Wahrscheinlichkeit für jede Geste aus. Da ein Wahrscheinlichkeits-Array eine Reihe von Werten zwischen 0 und 1 ist, haben wir noch einen Schritt zu gehen. Wir erachten die erkannte Geste als die mit der höchsten Wahrscheinlichkeit. Die erkannte Geste wird mit dem (zufälligen) Zug der KI vergleichen und es wird bestimmt, wer die Runde gewonnen hat.

// probability for each classfloat paper = model_output->data.f[0];float rock = model_output->data.f[1];float scissors = model_output->data.f[2];

Das untenstehende Diagramm veranschaulicht die Schritte auf der MCU. Für unsere Zwecke ist keine Datenaufbereitung auf dem Mikrocontroller notwendig.

(Bild: Itemis)

Erweitern Sie das Beispiel

Wie wäre es mit einer Herausforderung? Erweitern Sie das Schere, Stein, Papier Beispiel, indem Sie die Klassen Eidechse und Spock hinzufügen. Zuerst sollten Sie sich unser Git Repository für Schere, Stein, Papier ansehen und das Beispiel Schritt für Schritt reproduzieren. Die README-Dateien helfen Ihnen dabei. Die folgende Abbildung zeigt Ihnen die Regeln des erweiterten Spiels. Sie müssen zwei zusätzliche Gesten und ein paar neue Gewinn- und Niederlagebedingungen hinzufügen.

(Bild: Itemis)

Beginnen Sie ihr eigenes Projekt

Wenn Ihnen dieser Artikel gefallen hat und Sie Ihre eigenen Projekte starten möchten, stellen wir Ihnen eine Vorlage zur Verfügung; hier werden dieselben Schritte verwendet wie unserem vorgestellten Projekt. Die Vorlage finden Sie hier. Kontaktieren Sie uns gern mit Ihren Projekten über Social Media.

* Nikolas Rieder ist Student an der Hochschule für Angewandte Wissenschaften in Hamburg. Er studiert B.Sc. Mechatronik, absolviert seit Februar 2022 sein Pflichtpraktikum bei der itemis AG. Er arbeitet zusammen mit seinem Co-Autor im Bereich TinyML, wo er seine Leidenschaft für KI mit seiner Expertise in eingebetteten Systemen verbindet. Nikolas ist ein lebenslanger Lerner, neugierig auf zukünftige Technologien, die das tägliche Leben verbessern.

* Rafael Tappe Maestro ist Werkstudent bei der itemis AG in Hamburg. Er studiert Künstliche Intelligenz im Master an der Universität Groningen. Seine KI-Forschungsinteressen liegen im Bereich Energieeffizienz, Neural Architecture Search und lokales Lernen. Rafael hat Spaß an Programmiersprachen, liest gern die Phoronix Linux Nachrichten und vergibt Sterne auf GitHub.

(ID:48482751)