Implementierung eines neuronalen Netzes zur Echtzeit-Bilddatenklassifikation auf dem Kendryte K210
Der Einsatz neuronaler Netze und anderer Methoden aus dem Bereich des maschinellen Lernens bzw. der KI eröffnet völlig neue Möglichkeiten bei der Auswertung von Bilddaten. Im Gegensatz zu konventionellen Techniken erfordert dies jedoch einen vergleichsweise hohen Rechenaufwand und damit entsprechend leistungsfähige Hardware. Der mobile Einsatz oder in Geräten ohne Internetzugang war daher lange schwierig oder gar nicht möglich. Um KI universeller einsetzen zu können und deren Popularität zu steigern, kamen in den letzten Jahren viele spezialisierte Chipsätze auf den Markt, welche bei geringer Stromaufnahme gleichzeitig noch eine ausreichende Rechenleistung bieten. Ausreichende Rechenleistung ist in diesem Fall als „ausreichend für die Aufgabe“ zu verstehen, zum Training der Netze taugen diese Chips meist nicht, hier wird nach wie vor deutlich mehr Rechenleistung benötigt.
Zu dieser Familie gehört auf der K210 von Kendryte. Der Embedded-SoC verfügt über zwei RISC V Cores und eine für neuronale Netze spezialisierte Einheit, welche Kendryte KPU nennt (Knowledge Processing Unit). Die RISC Cores teilen sich 6 Megabyte RAM, der KPU sind 2 MB vorbehalten. Technische Daten, siehe: https://kendryte.com/#products .
Die Rechenleistung im Vergleich mit anderen Chips dieser Familie kann sich sehen lassen, vor allem, wenn man bedenkt, dass Boards mit K210 sogar für einige Minuten mit einem kleinen Akku betrieben werden können.
Leider sind für den SoC im Netz derzeit nur wenige Projekte dokumentiert, welche über das Ausführen des Demo Programms der Hersteller hinausgeht. Aus diesem Grund möchte ich meinen Workflow zur Bilddatenklassifikation mit anderen Makern teilen. Meines Wissens ist dies auch die erste Dokumentation eines Projekts für diesen Chipsatz in deutscher Sprache.
Beispiel-Projekt: Klassifikation von Abfall
Es gibt zwar einige KI-Demos wie die Klassifikation von Hunden vs Katzen oder von handgeschriebenen Ziffern (das „Hello world“-Äquivalent der KI, https://www.tensorflow.org/datasets/keras_example, http://yann.lecun.com/exdb/mnist/). Das war mir aber eindeutig zu langweilig. Die Klassifikation von Abfall ist deutlich anspruchsvoller und zeigt eindrucksvoll, wo die Stärke beim Einsatz eines Neuronalen Netzes (NN) liegt. Am Ende soll ein Bild aufgenommen und nach der Auswertung durch das NN ausgegeben werden, in welchen Abfalleimer das Objekt entsorgt werden muss (Es beherrscht das deutsche Abfallsystem! 😉).
Der komplette Quellcode des Projekts befindet sich in meinem Github Repository: https://github.com/jschw/K210-Trash-Classification.
Voraussetzungen
- Entwicklungsboard mit K210, Kameramodul und Display (z.B. Sipeed Maix Go oder M5Stick AI Camera)
- Wenn möglich Computer mit leistungsstarker NVIDIA-GPU und korrekt konfiguriertem CUDA/cuDNN
- Linux-Installation
- Python-Installation mit Tensorflow >=2.0
Der Datensatz
Als Trainings- und Testdatensatz wurde eine angepasste Variante der von G. Thung erstellten Bildersammlung verwendet (https://github.com/garythung/trashnet, Artikel: http://cs229.stanford.edu/proj2016/report/ThungYang-ClassificationOfTrashForRecyclabilityStatus-report.pdf).
Die Klasse „plastic“ wurde im hier verwendeten Datensatz aufgesplittet in „plastic_bottle“ und „plastic_other“, um zwischen Kunstofffolie und Kunststoffflaschen unterscheiden zu können. Dadurch ergibt sich auch der Vorteil, einen Hinweis ausgeben zu können, dass das Objekt nicht in den gelben Sack gehört, sondern möglicherweise eine Pfandflasche ist. Außerdem wurde die Klasse „trash“ entfernt. Alles, was nicht eindeutig zugeordnet werden kann, wird hier nach dem Ausschlussverfahren als „Restmüll“ klassifiziert.
Der Datensatz befindet sich im Unterordner „data“ des Projektordners. Die Ordnerstruktur hat folgendermaßen auszusehen, damit die Skripte einwandfrei funktionieren:
Die Teilung der Daten erfolgt automatisch im Verhältnis 80:20 (siehe Parameter von training.py). Dieses Verhältnis ist bei ausreichender Bildanzahl meist zweckmäßig, eine feste Regel existiert jedoch nicht (https://glassboxmedicine.com/2019/09/15/best-use-of-train-val-test-splits-with-tips-for-medical-data/). Bei einem extremeren Verhältnis (z.B. 90:10) könnte es zu Schwankungen der validation accuracy kommen. In diesem Fall empfiehlt es sich, nicht wie im Code umgesetzt nur das „beste“ Modell nach validation accuracy zu speichern, sondern in festen Abständen zu speichern. Dazu muss das Callback der Fit-Methode entsprechend angepasst werden.
Vorbereitung des Entwicklungssystems
Wie anfangs erwähnt, wird zur kompletten Umsetzung des Projekts ein Linux-System empfohlen. Der nncase Compiler ist zwar auch für Windows und in Vorversionen auch für macOS erhältlich, ich konnte bisher jedoch nur die Linux-Version ohne Fehlermeldungen zum Laufen bringen. Mit kommenden Versionen dürfte die Kompatibilität noch verbessert werden, es wird augenscheinlich zumindest an Versionen für alle drei Plattformen gearbeitet.
Zudem wird eine funktionierende Python-Umgebung mit Tensorflow ab Version 2 benötigt, im besten Fall mit GPU-Unterstützung (Installation TF: https://www.tensorflow.org/install/pip , GPU mit CUDA: https://www.tensorflow.org/install/gpu).
Wie im TF installation guide empfohlen, rate ich dringend zur Arbeit mit einem Virtual Environment, da die Standard-Implementierung des MobileNet im Keras package angepasst werden muss. Mit einem Virtual Environment umgeht man die Gefahr, die systemweite Python-Umgebung zu beschädigen (siehe: https://docs.python.org/3/tutorial/venv.html).
Noch ein Hinweis an Windows-Nutzer: Wer die Powershell nutzt, muss das VEnv, anders als im TF installation guide beschrieben, mit folgendem Befehl aktivieren:
. ./<venv_name>/Scripts/activate.ps1
Alle benötigten Abhängigkeiten zu Python Bibliotheken können schnell mit einem Befehl installiert werden:
pip3 install -r requirements.txt
Aufbau des NN und Training
Beim Aufbau und Training des Neuronalen Netzes kommt die Methode des Transfer Learnings zum Einsatz (https://www.industry-of-things.de/iot-basics-was-ist-transfer-learning-a-884674/).
Der komplette Workflow sieht zusammengefasst so aus:
Das Netz selbst ist vom Typ MobileNet (Google Inc., https://arxiv.org/pdf/1704.04861.pdf), aufgesetzt auf einen Fully Connected (FC) Layer mit Softmax Aktivierung. Durch entsprechende Parametrierung bei der Initialisierung wird die Parameteranzahl des Netzes so weit reduziert, dass es am Ende in die 2 Megabyte RAM der KPU passt.
Eine Änderung an der Keras-Implementierung des MobileNet muss jedoch vorgenommen werden, weil diese in den Conv2D Layern padding=‘valid‘ verwendet, der nncase Compiler (derzeit) jedoch nur die Einstellung ‚same‘ ohne padding oder “valid” mit padding, also gleiche Layer Input- und Outputgrößen unterstützt (anschauliche Erläuterung von padding ‚same‘ und ‚valid‘: https://www.youtube.com/watch?v=qSTv_m-KFk0).
Am einfachsten ist, im Virtual Environment die Datei ‚mobilenet.py‘ durch die in meinem Github-Repo Bereitgestellte zu tauschen. Nicht vergessen, vorher eine Sicherheitskopie der Originalversion zu erstellen!
Das Programm „training.py“ habe ich so aufgebaut, dass es ohne Anpassung auch mit anderen Datensätzen trainiert werden kann. Der Aufrufparameter entspricht dabei dem Ordnernamen des Datensatzes im „data“ Ordner:
python3 training.py -dataset trash -val_split 0.2
… trainiert auf den Datensatz „trash“ mit dem train-validation-Verhältnis 80:20.
Wie im Flowchart dargestellt, wird das Modell in zwei Schritten trainiert. Um den Klassifizierer erst separat zu trainieren, werden nach Modelldefinition alle Convolution Layer „eingefroren“, also vom Training ausgeschlossen:
Anschließend wird der Klassifizierer für 5 Epochen mit einer recht hohen Learning Rate (LR) von 0.2 trainiert. Die eingefrorenen Layer werden zwar bei der Gradientenberechnung berücksichtigt, jedoch werden keine Gewichte und Biases angepasst.
Im nächsten Schritt werden alle Layer wieder „aufgetaut“ und mit einer deutlich geringeren LR für 30 Epochen weitertrainiert:
Wie bereits erwähnt kann die Speicherung des Modells mit einem Callback weiter angepasst werden:
Hier wird das Modell mit der höchsten Accuracy gespeichert, die vorhandene Version wird überschrieben.
Durch entsprechendes Anparametrieren kann die Speicherfrequenz beeinflusst bzw. optimiert werden. Der Parameter save_frq=‘epoch‘ kann eine gute Alternative zu ‚save_best_only‘ sein, an dieser Stelle sei verwiesen auf die Keras-Dokumentation zu Callbacks: https://keras.io/api/callbacks/ .
Ist das Training abgeschlossen, wird das NN sowohl im h5-Formt (kann erneut mit Keras geladen werden) und im Tensorflow lite (TFlite) Format gespeichert.
Die Funktion nutzt den Kompatibilitätslayer von Tensorflow 1.x um das NN ins TFlite Format zu konvertieren. Grund hierfür ist, dass der nncase Compiler in Version 0.1.x derzeit das Format von TF 2.x nicht unterstützt und die KPU Firmware wiederum keine neuere Version des nncase Compilers unterstützt. Aus diesem Grund muss derzeit auf diesen Workaround zurückgegriffen werden.
Im Ordner „train“ wird nach Start des Trainingsskripts ein neuer Ordner (sofern nicht vorhanden) mit dem Namen des Datensatzes erstellt. Wie eingangs erwähnt, kann das Training und die Validierung auch an einem Mac oder Windows Rechner stattfinden. In dem Ordner befinden sich nach erfolgtem Training einige Dateien, welche Aufschluss über den Trainingsvorgang selbst geben:
Das Schaubild der train- und validation Accuracy zeigt einen stetigen Anstieg, jedoch mit Schwankungen. Ein Grund dafür könnte ein zu kleiner validation-Datensatz mit zu großer Varianz der einzelnen Samples einer Klasse sein.
Performance des trainierten Netzes
Der Fokus dieser Projektbeschreibung soll nicht eine Einführung in Deep Learning sein, trotzdem möchte ich die Validierung des Modells zur Abfall Klassifikation kurz betrachten.
Die Accuracy der besten Trainingsepoche (=gespeichertes Modell) liegt im Durchschnitt über alle Klassen bei 84,4 %.
Bei einem komplexen Klassifizierungsproblem wie dem Abfall-Beispiel ist es auch sinnvoll, nicht nur die overall-Accuracy zu betrachten, sondern auch einen Blick auf möglicherweise systematische Fehlinterpretationen zu werfen. Hierfür ist die Confusion Matrix geeignet:
In y-Richtung werden die korrekten Labels abgetragen, in x-Richtung sind die vom trainierten Model ausgegebenen Labels mit dem jeweiligen prozentualen Anteil. Es ist zu beachten, dass für Erstellung der Confusion Matrix ebenfalls der Validation-Datensatz verwendet wurde, welcher in seiner Größe stark begrenzt ist.
Auffällig ist, dass eine große Mehrheit der Fehlklassifikationen auf die Klasse „Papier“ fällt. So werden zum Beispiel 26% der Metallobjekte falsch als Papier klassifiziert. Ein Grund ist sicherlich, dass viele der Objekte Beschriftungen aufweisen, welche auch in der Klasse Papier logischerweise stark dominieren.
Betrachtet man nur die Confusion Matrix, stehen die 84,4% der overall-Accuracy auf einem in einem ganz anderen Licht. So extrem schlecht ist die Quote in der Realität jedoch nicht. Wie schon erwähnt, ist die Repräsentation des realen Abfalls im validation-Datensatz eben doch sehr begrenzt.
Umwandlung mit dem nncase Compiler
Bedingt durch die geringen Ressourcen (2 MB RAM!), läuft auf dem K210 keine Version von Tensorflow. Zur Ausführung des Modells müssen die Tensorflow-Rechenoperationen also erst in einen spezialisierten Befehlssatz für die KPU übersetzt werden. Zudem erfolgt eine Quantisierung des Modells zur Reduzierung der benötigten Rechenleistung bzw. des Speicherverbrauchs (https://www.qualcomm.com/news/onq/2019/03/12/heres-why-quantization-matters-ai).
Der Hersteller einiger K210-Entwicklungsboards (Sipeed) stellt eine Reihe sinnvoller Skripte für Konvertierungsaufgaben zur Verfügung. Es bietet sich an, das komplette Repo https://github.com/sipeed/Maix_Toolbox in das Projektverzeichnis zu klonen. Somit ist die Versionskompatibilität zwischen exportierter TFlite <-> kmodel Datei immer gegeben, auch wenn eine neue Version von nncase veröffentlicht wird.
Das Skript get_nncase.sh lädt die erforderlichen Dateien des Compilers vom Kendryte-Repository (https://github.com/kendryte/nncase) und entpackt sie.
Hinweis: Vor dem Ausführen muss ein Fehler in dem Skript korrigiert werden:
tar -Jxf ncc-linux-x86_64.tar.gzrm ncc-linux-x86_64.tar.gz
In diesen beiden Zeilen muss .gz durch .xz ersetzt werden, damit das Entpacken funktioniert.
Die Verzeichnisstruktur müsste dann so aussehen:
Anschließend kann die beim Training exportierte *.tflite Datei des NN mit tflite2kmodel.sh im Projektverzeichnis kompiliert werden. Meine Version des Skripts basiert auf dem Sipeed-Skript und wurde um den Aufrufparameter Modellname erweitert. So kann beim Aufruf der Name des gewünschten Modells mitgegeben werden, ohne das Skript bearbeiten zu müssen:
./tflite2kmodel.sh trash
Der Aufruf des Compilers enthält noch weitere Parameter:
-i und -o legen das Ein- und Ausgabedatenformat fest. –dataset übergibt den Speicherort einiger Bilddaten-Samples, welche zur Quantisierung des Modells benötigt werden. Wichtig ist, dass diese in ihrer Auflösung exakt dem Eingabeformat des ersten Layers des NN entsprechen. In diesem Projekt sind das 224x224 Pixel.
Prinzipiell reichen wenige Samples, eine größere Anzahl kann jedoch die Qualität des Modells verbessern. Das in meinem Github zur Verfügung gestellte Modell wurde mit allen ~2.500 Trainingssamples quantisiert (das dauert eine kleine Weile…).
Nach erfolgreicher Kompilierung sollte eine Datei mit der Endung *.kmodel im Verzeichnis train/<Name Datensatz>/ liegen.
Ausführung der Klassifikation auf dem K210
Im letzten Schritt geht es nun darum, das Modell auf dem Entwicklungsboard auszuführen, mit Bilddaten zu füttern und das Ergebnis auszuwerten und weiterzuverarbeiten. Der K210 kann entweder mit C++ oder Micropython programmiert werden. Ich habe mich an dieser Stelle für Python entschieden, weil sich Python im Bereich ML/KI als Quasistandard entwickelt hat. Eine Umsetzung in C++ wäre zwar auch möglich, Python hat jedoch den Vorteil, dass die Vorgehensweise auf dem Embedded System sich nicht grundsätzlich von jener auf dem Entwicklungssystem unterscheidet. Im Hinblick auf Multithreading wäre C++ wieder interessant, vorausgesetzt, die KPU bekäme in Zukunft mehr Speicher. Vorstellbar wäre dann, das Laden zweier Modelle in den RAM und je einem Thread und dann kurz versetztes oder gar zeitgleiches Anwenden auf Daten.
Im Rahmen dieses Projekts wurden folgende drei Betriebsmodi zur Ausführung der Klassifikation implementiert:
Die Betriebsmodi 0 und 2 sind auch im Demo-Video am Anfang des Artikels zu sehen.
Mode 0 / Continuous: Das System nimmt kontinuierlich Bilder auf, füttert diese an die KPU und gibt das Ergebnis zusammen mit der aktuellen Framerate und Verarbeitungszeit für einen Frame aus. Ist Logging aktiviert, kann durch Auslösen eines Triggers (für den Maix Go ist Druck auf den 3-Wege-Button und Druck auf beliebige Stelle des Touchscreens implementiert). Dieser Mode ist für Testzwecke am besten geeignet.
Mode 1 / Single Shot: Nach Setzen des Triggers erfolgt die Aufnahme eines Bilds und Verarbeitung mit der KPU. Die Rohdaten der Ausgabe werden eines Abfalleimers des deutschen Mülltrennungs-Systems zugeordnet und das entsprechende Piktogramm angezeigt. Ist die erkannte Klasse des Objekts „glass“ oder „plastic_bottle“ wird ein zusätzlicher Hinweis eingeblendet, ob es sich vielleicht um eine Pfandflasche handelt. Ist Logging aktiviert, erfolgt die Speicherung bei jedem ausgelösten Trigger.
Mode 2 / Multi Shot: Mode 2 ist identisch zu Mode 1, bis auf einen Unterschied: Nach Auslösen des Triggers werden mehrere Bilder aufgenommen und ausgewertet. Die Entscheidung erfolgt anhand des Mittelwerts aller Einzelergebnisse. Dieses Vorgehen liefert ein stabileres Ergebnis bei qualitativ schlechten Bildaufnahmen oder verwackelten Aufnahmen. Ist ein Bild schlecht, die anderen aber in Ordnung, passt am Ende auch das Ergebnis. Im Single-Shot-Mode wäre das Ergebnis dann möglicherweise nicht korrekt. Nachteil ist die längere Rechenzeit. Statt den ca. 72 ms, vergehen bei 10 Bildern 720 ms, bis das Ergebnis vorliegt.
Die Daten wie Modell, Skripte etc. können entweder im Flash des K210 oder einer Micro SD Karte abgelegt werden. Zum Transfer in den Flash kann das Programm „uPyLoader“ (https://github.com/BetaRavener/uPyLoader) verwendet werden, die SD Karte hat sich für mich jedoch als deutlich praktischer erwiesen.
Hinweis: Die Firmware kommt nur mit FAT32 formatierten SD Karten zurecht, mit NTFS oder exFAT formatierte Karten werden nicht erkannt.
In meinem Github Repo befindet sich ein Ordner „sdcard_content“, dessen Inhalt komplett auf die SD Karte kopiert werden muss.
Nach dem Start des Boards (Schiebeschalter!) wird nach der Datei „boot.py“ gesucht und falls vorhanden ausgeführt. Ein Verweis in boot.py auf das Hauptprogramm hat den großen Vorteil, dass ohne Hin- und Herkopieren und Ändern von Dateien zwischen verschiedenen Hauptprogrammen und Konfigurationen gewechselt werden kann.
Voraussetzung für Parameteränderungen ist, dass die Zuweisung im Hauptprogramm auskommentiert wird, andernfalls erfolgt beim Ausführen die Überschreibung mit den im Hauptprogramm festgelegten Werten.
Das Hauptprogramm trash_main_program.py im Ordner /sd/models/trash enthält die eigentliche Implementierung. Konfiguriert werden können folgende Parameter:
img_num_mshot: Legt die Anzahl der im Multishot-Modus verarbeiteten Bilder fest.
img_logging: Aktiviert oder deaktiviert die Speicherung von Bildern auf der SD Karte.
img_log_path: Legt den Ordner fest, in welchen beim Logging gespeichert werden soll (Hinweis: Ordner muss vorhanden sein!)
kmodel_path: Speicherort des NN Modells
threshold: Minimale Aussagesicherheit, damit das Objekt nicht als Restmüll klassifiziert wird (Wenn Ergebnis > 0.7 / 70% , dann Ausgabe des jeweiligen Klassenlabels, sonst nicht erkannt, also Restmüll).
Laden des NN Modells in die KPU…
task = kpu.load(kmodel_path)
… Festlegen des Klassenlabel-Vektors
class_labels = ['Pappe', 'Glas', 'Metall', 'Papier', 'Kunststoffflasche', 'Kunststoff', 'Restmuell']
Die Zuordnung zu den Indizes des Ausgabevektors können in der beim Training gespeicherten Datei „classnames.txt“ (Ordner: train/<Datensatz>) nachgesehen werden:
{'cardboard': 0, 'glass': 1, 'metal': 2, 'paper': 3, 'plastic_bottle': 4, 'plastic_other': 5}
Der wichtigste Teil, die Bildaufnahme und Auswertung, ist ziemlich einfach:
Zuerst erfolgt die Aufnahme eines Bilds, welches durch das NN propagiert wird. Dann wird der Ausgabevektor in eine Liste (plist=prediction list) umgewandelt und der größte der Werte ermittelt. Ist der Wert größer als die festgelegte Grenze, wird mit dessen Listenindex (=Labelvektorindex) das passende Label aus dem Vektor geladen oder andernfalls die Fallback-Klasse für nicht erkannte Objekte geladen.
Im Multi-Shot Mode sieht der Vorgang etwas anders aus:
Nach jedem aufgenommenen Bild, erfolgt die Auswertung durch das NN und Aufaddieren der Einzelwerte des aktuellen Schritts auf die gesamte prediction list. Wurden alle Bilder aufgenommen und ausgewertet, erfolgt die Mittelwertbildung aller Einzelwerte des Vektors.
Die Zuordnung des höchsten Werts der prediction list zum jeweiligen Klassenlabel unterscheidet sich nicht vom Ablauf des Continuous oder Single Shot Modes. Das erkannte Material muss nun zur Kategorie im deutschen Müllsystem zugeordnet werden. Das kann mit einer simplen Fallunterscheidung erledigt werden.
Im Fall von plastic_bottle oder glass wird is_bottle=True gesetzt und sorgt bei der Ausgabe für den Hinweis, dass es sich um eine Pfandflasche handeln könnte.
Hinweis: Sollte das Programm zur Klassifikation nach dem Einschalten nicht automatisch gestartet werden, wurde die SD Karte zu langsam eingebunden. Ein Druck auf den Reset-Button löst einen Warmstart aus — nach dem Start sollte die boot.py auf der SD Karte ausgeführt werden.
Blick in die Blackbox: Nachvollziehbarkeit der Ergebnisse
Ein häufiger Kritikpunkt an KI ist das Blackbox-Verhalten vieler Systeme, was die Fehlersuche im laufenden Betrieb schwer macht. Zur Vergrößerung der Akzeptanz, sollte daher in Zukunft mehr Energie in die Herstellung von Transparenz in Entscheidungen und Ergebnisse investiert werden.
Im Fall von CNN (Convolutional Neural Nets) in der Bildverarbeitung ist dieses Problem vergleichsweise einfach zu lösen: Und zwar durch die Visualisierung der Klassenaktivierung der Featuremap des letzten Faltungslayers als sogenannte Heatmap. Mit dieser wird dann das Quellbild überlagert und vermittelt so einen visuellen Eindruck, welcher Bereich des Bildes besonders starke Auswirkung auf das Ergebnis hat.
Bei nicht erkannten Objekten könnten zwar auch allgemeine Fotos oder mit dem Smartphone aufgenommene Fotos verwendet werden, jedoch spiegeln diese nicht das Bild in seiner Qualität wider, welches das Embedded System „sieht“. Es bietet sich daher an, die bereits beschriebene Logging-Funktionalität zu verwenden, um die Auswertung auf die selben Bilddaten anwenden zu können.
Folgende zwei Beispielobjekte wurden nicht korrekt erkannt:
Beide Objekte wurden falsch als „Papier“ klassifiziert, hätten aber Metall/Kunststoff und Kunststoff sein müssen.
Nachfolgend ist der letzte Teil des NN zu sehen. Die Ausgabe des Dense Layers (FC) ist bekannt, diese gibt das Klassifikationsergebnis aus. Zur Visualisierung der für diese Entscheidung verantwortlichen Werte, ist der letzte Conv2D Layer und dessen Featuremap interessant:
Nach Aufruf des Skripts…
python3 predict_single_heatmap.py -dataset trash -image <Ordner>/<Bildname>.jpg
…werden für die beiden Beispiele folgende Heatmaps angezeigt:
Auf den Bildern lässt sich erkennen, dass das Netz die Objekte gar nicht „gefunden“ hat: Der Bereich der starken Aktivierungen ist unnötig groß, bei der Soßentube entfällt der größte Bereich sogar auf den Hintergrund. Vermutlich wurde das Ergebnis aufgrund der Beschriftungen beider Objekte von der Softmax Aktivierung des letzten Layers auf die Klasse „Papier“ hochgezogen. Durch Einsatz einer anderen Aktivierungsfunktion könnte möglicherweise erreicht werden, dass solche Fälle als „Restmüll“ (nicht erkannt) klassifiziert werden würden.
Das Beispiel zeigt auch, wie wichtig bei komplexen Objekten ein farblich neutraler und strukturfreier Bildhintergrund ist.
Hier noch zwei Beispiele korrekt erkannter Objekte:
Im zweiten Bild mit der Kunststofffolie ist gut zu erkennen, dass das Netz den Hintergrund weitestgehend ausblendet, und sich auf die für Kunststofffolien typische glänzende Falte und nicht auf die Beschriftung konzentriert.
Im folgenden Video ist noch ein weiteres Beispiel zu sehen, dass manche Objekte auch unabhängig von ihrer räumlichen Lage nicht korrekt erkannt werden können. In diesem Fall liegt das daran, dass das NN in seinen Trainingsdaten fast keine Kunststoffbecher “gesehen” hat.
Zusammenfassung und Ausblick
Diese Projektbeschreibung zeigt einen allgemeinen Workflow zur Implementierung einer Bildklassifikation auf Basis eines neuronalen Netzes auf der Kendryte K210 Plattform auf. Die bereitgestellten Skripte wurden erläutert und grundlegende Möglichkeiten zur Validierung und Fehlerfindung des Netzes vorgestellt.
Durch solche energiesparenden Embedded Plattformen eröffnen sich auch im kleinen, nicht-industriellen Umfeld ganz neue Möglichkeiten. Diese Projektbeschreibung soll dazu beitragen, dass KI auch in der Maker-Szene ankommt.
Folgende Schritte müssen zusammengefasst ausgeführt werden, um den Workflow auf einen eigenen Datensatz anzuwenden:
- Ordner mit dem Namen des Datensatzes im Ordner train erstellen
- In diesem Ordner die Unterordner images_train und images_maix erstellen
- Jeder Unterordner des Ordners images_train enthält die Bilder einer Klasse
- Im Ordner images_maix müssen sich einige Trainingsbilder (mindestens eines pro Klasse) befinden. Auflösung zwingend 224x224.
- Das Trainingsskript mit dem gewünschten validation-Split ausführen
- Das Modell konvertieren mit tfllite2kmodel.sh
- Modell *.kmodel und Skript(e) auf die SD Karte kopieren
- Labelnamen und Pfad zum Modell im Hauptprogramm anpassen
- boot.py anpassen, damit ggf. das richtige Hauptprogramm im jeweiligen Unterordner geladen wird.
Obwohl der Maix Go und ähnliche Boards im Vergleich zur Workstation mit GPU schon sehr klein und energiesparend ist, geht es bei Systemen mit dem K210 noch deutlich kleiner. An der Stelle möchte ich noch kurz den „M5Stick AI Camera“ (siehe: https://m5stack.com/products/stickv) erwähnen: