Im ersten Artikel dieser kleinen Serie wurde die Hardware des Maixduino vorgestellt und gezeigt, wie man diesen mit der Arduino-IDE in C++ programmieren kann. Dabei wurde die Leistungsfähigkeit des Prozessors mit einem KI-Modell zur Objekterkennung demonstriert. Wie die künstliche Intelligenz beim vertieften Lernen funktioniert, soll in diesem Beitrag angerissen werden. Wer sich ernsthaft mit KI beschäftigen möchte, kommt zudem an Linux und Python nicht vorbei. Doch mit den entsprechenden Tools ist das kein Hexenwerk.

Bitte erwarten Sie nicht, dass Sie nach dem Lesen der nachfolgenden Passagen das Deep Learning vollständig verstanden haben. Dazu waren bei mir das Studium einiger Bücher und Tutorials sowie zahlreiche Programmierversuche erforderlich (und vieles weiß ich noch immer nicht). Aber ich möchte Ihnen zumindest einen Überblick vermitteln, welche Strukturen und Methoden eingesetzt werden, um ein universell einsetzbares Programm in Form eines Neuronalen Netzes (NN) mit einer spezialisierten Intelligenz auszustatten.

 

Aufbau eines Neuronalen Netzes

Ein Neuronales Netz (NN) besteht aus mehreren Schichten, die jeweils mehrere Knoten aufweisen. In Bild 1 sind die Knoten einer Schicht vertikal angeordnet. Dabei können pro Schicht theoretisch beliebig viele Knoten und im Netz beliebig viele Schichten vorhanden sein. Die Architektur wird von der jeweiligen Aufgabe bestimmt und die Größe ist natürlich auch abhängig von den Ressourcen der benutzen Rechnerplattform. Soll das NN z.B. Objekte klassifizieren, die ihm per Bild übermittelt werden, übernimmt die erste Schicht die Daten zur Lösung der Aufgabe und stellt zur Erfassung eine entsprechende Anzahl von Eingangsknoten zur Verfügung. Besteht ein niedrig aufgelöstes Bild nur aus 28 * 28 Pixeln, dann sind bei einer Darstellung in Grauwerten 784 Knoten erforderlich. Zur Erfassung von Farbbildern würde sich die Anzahl verdreifachen. Bei der Bilderfassung erhält jeder Eingangsknoten den Grauwert „seines“ Pixels.

Bild 1. Struktur eines Neuronalen Netzes.

Das Ergebnis der Bildanalyse wird in der Ausgabeschicht präsentiert, die für jedes Resultat einen eigenen Knoten vorhält. Sollte das NN 1000 Objekte erkennen, hätte diese Schicht die gleiche Anzahl von Ausgangsknoten. Dabei zeigt jeder Knoten eine Wahrscheinlichkeit zwischen 0 und 1,0 an und signalisiert damit, wie sicher sich das NN beim gefundenen Resultat ist. So könnte bei der Aufnahme einer Hauskatze der zugehörige Knoten einen Wert von 0,85 und der Knoten „Tiger“ einen Wert von 0,1 aufweisen. Alle anderen Knoten hätten dann noch geringere Wahrscheinlichkeiten und das Ergebnis wäre damit eindeutig.

Die eigentliche Analysearbeit wird beim Deep-Learning von den verdeckten Schichten erledigt. Abhängig von der Aufgabe können beliebig viele verdeckte Schichten eingesetzt werden; in der Praxis sind durchaus 100 bis 200 solcher Schichten im Einsatz. Dabei kann man neben der oben gezeigte Architektur auch komplexere Strukturen mit Rückkopplungen oder zwischengeschalteten Filtern zur Steigerung der Genauigkeit finden. Die Anzahl der Knoten in jeder verdeckten Schicht ist ebenfalls beliebig. Im oben erwähnten Beispiel zur Objekterkennung könnten bereits drei verdeckte Schichten mit jeweils 200 Knoten schon brauchbare Resultate liefern.

Wird beim menschlichen Gehirn die Intelligenz durch die Kopplung der Neuronen mittels Synapsen erzielt, sind auch beim NN die Knoten miteinander vernetzt. Dabei ist jeder Knoten einer Schicht mit sämtlichen Knoten der nachfolgenden verbunden. Jede Verbindung (im Bild als Pfeil dargestellt) enthält einen Wert, der als Gewicht bezeichnet ist. Diese Gewichte zwischen den Schichten sind in Matrizen gespeichert. Deshalb werden im Bereich des Deep Learnings Programmiersprachen bevorzugt, bei denen Matrix-Operationen einfach und schnell durchgeführt werden können.

Und wie erzielt nun ein NN die gewünschten Resultate? Jeder Knoten erhält seine Eingangssignale von allen Knoten der vor ihm liegenden Schicht, in Bild 2 mit x bezeichnet.

Bild 2. Berechnungen innerhalb eines Knotens (https://commons.wikimedia.org/wiki/Artificial_neural_network).

Diese Werte werden mit den Gewichtswerten w multipliziert und addiert, also ist die Netzeingabe

net = x1*w1 + x2*w2 + X3*w3 …

Der errechnete Wert von net wird nun mit einer Aktivierungsfunktion multipliziert und dem Ausgang (und damit allen Knoten der Folgeschicht) zugeleitet. Ein knotenspezifischer Schwellwert bestimmt darüber hinaus, ob dieser Knoten „feuert“.

Die in Bild 3 gezeigten Aktivierungsfunktionen werden den Schichten zugeordnet und sorgen dafür, dass die Ausgangswerte im gewünschten Bereich bleiben. So unterdrückt die Relu-Funktion alle negativen Werte und bei Sigmoid erfolgt eine Begrenzung zwischen 0 und 1. Durch diese Maßnahmen arbeiten NNs in einem übersichtlichen Zahlenbereich und können nicht durch „Ausreißer“ dominiert werden.

Bild 3. Häufig eingesetzte Aktivierungsfunktionen.

 

Training

Nach der Erfassung der Eingangsdaten führt das NN also in jeder Schicht zahlreiche Berechnungen durch und präsentiert die Ergebnisse am Ausgang. Dieser Vorgang wird mit Inferenz bezeichnet. Und wie kommt nun die Intelligenz dort hinein? Das erfolgt wie bei einem Kleinkind durch Schulung und Training. Bei einem untrainierten NN sind die Gewichte meistens mit Zufallszahlen im Wertebereich -1 bis +1 gefüllt, das Netz ist also strohdumm und liefert demzufolge auch nur Zufallsresultate. Zum Training werden dem Netz Daten und bekannte Soll-Ausgangswerte zugeführt. Nach der Berechnung mit diesen Werten wird das Ergebnis mit dem Sollwert verglichen und die Differenz von einer Verlustfunktion dokumentiert. Nun folgt der Lernprozess, auch Backpropagation genannt. Beginnend in der letzten Schicht werden die Gewichte in kleinen Schritten so justiert, dass der Verlust minimiert wird. Nach vielen (oft millionenfachen) Trainingsrunden mit unterschiedlichen Eingangsdaten haben alle Gewichte die zur Aufgabenstellung passenden Werte und das NN kann nun neue, ihm unbekannte Daten analysieren.

Eine besonders zur Bild- und Audioverarbeitung geeignete Architektur stellt das Convolutional Neural Network (zu Deutsch: Faltendes Neuronales Netzwerk) dar, das auch vom Maixduino unterstützt wird. Bei einem solchen CNN sind die Neuronen (zumindest in einigen der Schichten) zweidimensional angeordnet, was zu zweidimensionalen Eingangsdaten wie zum Beispiel einem Bild passt. Gegenüber dem in Bild 1 angedeuteten Netzwerk, bei dem die Aktivität eines Neurons von allen Neuronen der vorhergehenden Schicht (über unterschiedliche Gewichtsfaktoren) abhängt, ist die Abhängigkeit beim CNN vereinfacht und lokal beschränkt. Die Aktivität eines Neurons hängt hier nur von den Werten von (beispielsweise) 3 x 3 Neuronen ab, die in der Schicht vor ihm angesiedelt sind - die Gewichtsfaktoren sind dabei gleich. Mit einem solchen Netzwerk lassen sich besonders gut kleinräumige Strukturen wie Linien, Rundungen, Punkte und weitere Muster erkennen. In den nachfolgenden Schichten werden dann komplexere Details und schließlich ganze Gesichter detektiert.

Programmtechnisch stellt ein NN ähnlich wie bei einer Tabellenkalkulation eine Anordnung von Zellen mit vorgegebenen Berechnungsanweisungen dar, die bei der Ausführung auf mehrdimensionale Matrizen mit den Gewichten zugreifen. Bei der Klassifikation neuer Eingangsdaten werden alle Schichten vom Eingang bis zum Ausgang durchlaufen und die Ausgänge reflektieren die jeweilige Wahrscheinlichkeit des dem Knoten zugeordneten Resultats. Gut trainierte NNs erreichen dabei durchaus Werte um die 0,9. Beim Training erfolgt nach der Interferenz die Backpropagation mit einem Durchlauf von hinten bis zum Eingang zwecks Justierung der Gewichte.

Klingt im ersten Moment kompliziert, aber es stehen zur Umsetzung zahlreiche Hilfsmittel und Bibliotheken zur Verfügung, die ich im weiteren Artikelverlauf vorstelle.

 

Linux und Python

Bereits im ersten Artikel dieser Miniserie habe ich darauf hingewiesen, dass Sie bei der Befassung mit dem Thema KI mit einem Lernaufwand rechnen müssen. Am leichtesten haben Sie es in dieser Welt auf der Linux-Plattform, denn hier stehen die meisten Werkzeuge kostenlos und mit guter Qualität zur Verfügung. Außerdem bietet Linux den gleichen Komfort wie Windows, ist nur anders (meistens besser) verpackt. Meine Präferenz ist Ubuntu, das auch als LTS-Version (Long Term Support) verfügbar ist und mindestens 4 Jahre unterstützt wird. Aber auch die anderen Derivate wie Debian, Mint usw. sind genauso brauchbar, die Wahl ist Geschmackssache. Linux lässt sich neben Windows z.B. in einer virtuellen Maschine installieren, Sie benötigen keinen Extra-Rechner.

Und warum Python? Das ist doch eine interpretierende Programmiersprache, so werden mache sagen, also potentiell langsam. Dieser Nachteil wird durch zahlreiche Vorteile aufgehoben: Zum ersten verzichtet Python auf den Schnickschnack aus Klammern und Semikolons und führt die Blockbildung durch Einrücken der Zeilen durch. Es verfügt über mächtige Datenstrukturen wie Listen, Tupel, Sets und Dictionaries; darüber hinaus ist in Python die Matrizenberechnung bereits voll integriert. Weitere große Vorteile sind die verfügbaren KI-Frameworks und Bibliotheken, die sich aufgrund der hohen Modularität ganz oder teilweise einbinden lassen. Diese sind meistens in C++ geschrieben und weisen daher die erforderliche Performance auf. Und das alles lässt sich mit wenigen Anweisungen bequem installieren.

Wenn Sie loslegen wollen, installieren Sie bitte das von Ihnen favorisierte Linux sowie pip3 und Python 3, Anweisungen dazu sind im Netz reichlich vorhanden.

 

Maixduino spricht MicroPython

Damit Python auch auf Systemen mit weniger Hauptspeicher laufen kann, steht eine abgespeckte Version unter der Bezeichnung MicroPython zur Verfügung; sie ist auf Plattformen wie Maixduino, ESP32 und anderen installierbar. MicroPython enthält neben einem ausgewogenen Befehls-Repertoire um die 55 Zusatzmodule für zahlreiche mathematische und systemtechnische Funktionen. Um neue Versionen oder KI-Modelle einzupflegen, ist das Flashing-Tool Kflash erforderlich.

 

Installation von Kflash unter Linux:

  • Downloaden der Version 1.5.3 oder höher von Weblink als gepackte Datei kflash_gui_v1.5.3_linux.tar.xz.
  • Transfer in einen Ordner Ihrer Wahl.
  • Entpacken mittels Befehl tar xvf kflash_gui_v1.5.3_linux.tar.xz.
  • Wechseln in den neu angelegten Ordner /kflash_gui_v1.5.2_linux/kflash_gui.
  • Starten mit ./kflash_gui (bei Startproblemen gegebenenfalls in den Eigenschaften dieser Datei das Häkchen bei Datei als Programm ausführen setzen).

Damit wird die grafische Oberfläche von Kflash (Bild 4) gestartet und es können nun Firmware oder KI-Modelle in den Maixduino geladen werden.

Bild 4. Oberfläche von Kflash.

 

Installation Firmware auf Maixduino

Wenngleich der Maixduino bei der Auslieferung bereits mit MicroPython ausgestattet ist, sollte man stets die neueste Version nachladen. Beim Schreiben dieser Zeilen hat die entsprechende Firmware die Bezeichnung v0.5.0 und diese steht über den Weblink zum Download bereit. Wählen Sie maixpy_v0.5.0_8_g9c3b97f oder höher und im nächsten Bild die Variante maixpy_v0.5.0_8_g9c3b97f_minimum_with_ide_support.bin oder höher, eine ca. 700 kB große Datei, die auch eine Unterstützung für die MaixPy-IDE enthält.

Mit Hilfe von Kflash ist die neue Firmware schnell installiert. Über den Button Open File wird diese ausgewählt und nach der Einstellung von Board, Port, Baudrate und Speed mode wie in Bild 5 startet ein Klick auf Download den Ladevorgang. Danach kann man bereits mit einem Terminalemulator (z.B. Putty) über den Port /dev/ttyUSB0 beim Maixduino die ersten Python-Befehle ausführen. Hier ein kleines Beispiel mit Array-Befehlen:

 

>>>                                # Python-Prompt

>>> import array as arr            # Import Array-Modul

>>> a = arr.array('i',[1,2,3])     # Anlegen Array a mit Integern

>>> b = arr.array('i',[1,1,1])     # Anlegen Array b mit Integern

>>> c = sum(a + b)                 # Summe aus allen Array-Werten bilden

>>> print(a,b,c)                   # und ausgeben

array('i', [1, 2, 3]) array('i', [1, 1, 1]) 9     # Ausgabe

>>> 

Mit Bibliotheken wie numpy (unter Linux) oder umatlib stehen noch mehr Funktionen zur Verfügung.

 

Installation MaixPy-IDE

Viel komfortabler gehen Entwicklung und Tests mit der Entwicklungsumgebung MaixPy-IDE vonstatten. Darüber können Python-Programme entwickelt und getestet sowie auf den Maixduino geladen und ausgeführt werden. Zusätzlich stehen, wie in Bild 5 dargestellt, Tools zur Bildanalyse zur Verfügung.

Bild 5. Oberfläche der MaixPy-IDE.

Die Installation wird wie folgt durchgeführt:

 

  • Download der Version maixpy-ide-linux-x86_64-0.2.4-installer-archive.7z oder höher von Weblink.
  • Transfer in einen Ordner Ihrer Wahl.
  • Entpacken mittels Befehl tar maixpy-ide-linux-x86_64-0.2.4-installer-archive.7z.
  • In den neuen Ordner maixpy-ide-linux-x86_64-0.2.4-installer-archive wechseln und folgende Befehle eingeben:

./setup.sh

./bin/maixpyide.sh

Danach startet die IDE. Bei nachfolgenden Starts ist natürlich nur der letztgenannte Befehl erforderlich.

Damit stehen sämtliche Werkzeuge zur Implementierung von KI-Modellen zur Verfügung. Nachfolgend wird eine Gesichtserkennung getestet.

 

Gesicht erkannt!

Zur Gesichtserkennung wird ein bereits trainiertes KI-Modell eingesetzt, das bereits mehrere Tausend Gesichter nach charakteristischen Merkmalen analysiert hat - die Gewichte des eingesetzten NN sind entsprechend justiert. Das Modell steht unter dem Weblink unter der Bezeichnung face_model_at_0x300000.kfpkg zum Download bereit. Basis der Entwicklung ist hierbei das KI-Framework Yolo2 (You Only Look Once), das die Bildobjekte in mehrere Zonen unterteilt, diese gesondert analysiert und dadurch hohe Erkennungsraten erreicht (auf die KI-Frameworks werde ich im weiteren Verlauf der Serie noch eingehen). Das KI-Modell für den KI-Prozessor ist im kfpkg-Format gepackt und muss im Maixduino an die Adresse 0x300000 geflasht werden. Dieses lässt sich ebenfalls mit Kflash erledigen; hierfür einfach die Datei mittels Open File aufsuchen und anschließend mit den Parametern von Bild 4 auf das Board laden.

Zur komfortablen Abwicklung des Python-Skripts kommt die MaixPy-IDE zum Einsatz, mit der man Programme entwickeln, testen und in den Maixduino transferieren kann. Bild 5 zeigt die in drei Fenster eingeteilte Oberfläche:

Editor, links-oben: In diesem Bereich erfolgt die Programmeingabe mit Syntax-Highlighting.

Terminal, darunter: Darstellung der Programmausgaben.

Bildanalyse, rechts: Hier erfolgen die Darstellung von Bildern und ihre spektrale Aufteilung in die Farben Rot, Grün, Blau.

Neben anderen verfügbaren Buttons und Menüpunkten sind die beiden Knöpfe links unten wichtig: Mit der „Büroklammer“ wird die Verbindung zum Maixduino über den Port „ttyUSB0“ hergestellt (Farbe Grün) bzw. getrennt (Rot). Ein grünes Dreieck darunter startet das Skript; danach wandelt sich dieser Button in einen roten Punkt mit „x“ und dient zum Stoppen des Programms.

Zur Durchführung des Tests habe ich die Gesichter von zwei öffentlich bekannten Personen (Albert Einstein und Rudi Völler) ausgedruckt und an eine Wand gepinnt. Die Auswahl war rein zufällig, aber man sagt beiden Gesichtern eine gewisse  Ähnlichkeit nach, was von mir allerdings nicht bestätigt wird. Bei der Aufnahme dieser Bilder wurden die Gesichter sofort erkannt und mit einem Rahmen markiert. Es ist darauf zu achten, dass die Darstellung wie gezeigt im Querformat erfolgt, ansonsten sinkt die Erkennungsrate spürbar ab.

Das Programm Face-detect.py finden Sie im Download-Ordner auf der Elektor-Website. Seine Kürze reflektiert wiederum die Leistung der eingesetzten Bibliotheken. Zum Start erfolgen die Einbindung der benötigten Bibliotheken für Kamera, LCD und KPU sowie deren Initialisierung. Danach wird das NN ab Adresse 0x300000 in die KPU geladen. Bei der Initialisierung des KI-Modells durch den Befehl kpu.init_yolo2 werden zusätzliche Konstanten zur Einstellung der Genauigkeit und Optimierung übergeben. Nun wird in einer endlosen while-Schleife die Bildklassifikation durchgeführt, ein Bild aufgenommen und dem NN zugeführt. Sind Gesichter entdeckt worden, erhält die Variable i für jedes Gesicht die Koordinaten und Größe eines Markierungsrahmens, der anschließend in das Bild eingezeichnet wird. Abschließend erfolgen die Ausgabe des Bildes (auf dem LCD-Panel) und der Markierungsdaten (auf dem seriellen Terminal). Weitere Details zu den KPU-Befehlen sind unter dem Link abrufbar.

In der MaixPy-IDE wird das Bild rechts oben ebenfalls gezeigt und darunter das dazugehörende Farbspektrum dargestellt. Falls Sie diese Information nicht benötigen, ist das rechte Bildfenster mit dem Deaktivieren-Button abschaltbar.

Zur besseren Handhabbarkeit habe ich Maixduino und LCD auf ein kleines Brettchen montiert und die Kamera nach vorne ausgerichtet (siehe Bild 6). Damit lassen sich leicht echte Gesichter, gedruckte Bilder oder Bildschirminhalte erfassen und analysieren.

Bild 6. Testaufbau für die Gesichtserkennung.

Die Darstellung auf dem LCD-Panel sieht man in Bild 7, Ähnlichkeiten der Personen sind nicht zu erkennen.

Bild 7. Darstellung auf dem Maixduino-LCD.

Aber was steckt hinter diesem Yolo2-Modell? Das Neuronale Netz verfügt über 24 Convolutional-Schichten und zwei voll verbundene Ausgangs-Layer (siehe Bild 8). Dazwischen sind einige Maxpool-Layer als Filter zwischengeschaltet, um Komplexität herauszunehmen und den Hang zum „Auswendiglernen“ zu verringern. Es fällt auf, dass weitgehend eine Fenstergröße von 3x3 zur Detailerkennung genutzt wird. Exakt diese wird von der KPU-Hardware unterstützt, was dem Maixduino für solche Aufgaben eine hohe Leistungsfähigkeit sichert.

Bild 8. Netzarchitektur zur Gesichtserkennung (Quelle: https://bit.ly/3cK3DUR).

Andere bekannte NN-Strukturen verfügen sogar über mehrere Hundert Schichten, haben Rückführungen oder andere Extras. Der Kreativität sind in diesem Bereich keine Grenzen gesetzt, vieles hängt vom Budget, sprich Rechenleistung, ab.

 

Und weiter geht´s!

Die leistungsstarke Hardware und die bereits jetzt verfügbare Software-Umgebung zeigen, dass der Maixduino für den Einstieg in die Künstliche Intelligenz gut geeignet ist. Er bietet sich aufgrund des geringen Stromverbrauchs gut für den Einsatz in mobilen Geräten mit bereits trainierten Neuronalen Netzen an. In einem dritten Teil der Serie werde ich Ihnen zeigen, wie man ein eigenes Neuronales Netz entwickeln, trainieren und ausführen kann. Die Schnittstelle zum Entwickler bildet dabei das KI-Framework Keras, das auch als komfortabler „Legobaukasten für KI“ bezeichnet wird. Außerdem erhalten Sie Hinweise, wie man den ESP32 auf dem Board programmieren kann - zum Beispiel um analoge Werte zu erfassen.

Bleiben Sie neugierig!

 

----------------------------------------------------------------------------------------------------------------------

Wollen Sie weitere Elektor-Artikel lesen? Jetzt Elektor-Mitglied werden und nichts verpassen!

----------------------------------------------------------------------------------------------------------------------