Künstliche neuronale Netze auf einem Mikrocontroller

Einleitung

Wo man auch hinschaut, alles spricht von künstlicher Intelligenz (KI, eng: AI). Jene die sich mit dem Thema etwas eingehender beschäftigen betitelten es eher als machine learning bzw. machinelles Lernen.

Einen kleinen Überblick gab es diese Woche auch auf 3sat, beim Wissenschaftsmagazin nano, das ich nur allen wärmsten empfehlen kann. Nicht unbedingt nur diese Folge, sondern allgemein ist diese Magazin sehr gut umgesetzt.

Die wirklich interessanten Einsatzgebiete sind jedoch zu uninteressant, um diese im TV einem allgemeinen Publikum aufzubereiten. Große Datenmengen analysieren, zuordnen oder durch Regression Vorhersagen treffen.

Beispiele:

  • Suchanfragen an den Benutzer anpassen: Wenn ich zuvor wieder und wieder etwas über das Programmieren gesucht habe, dann werde ich bei der Eingabe von „C Aufbau“ nicht die Note meinen, da ich anscheinend eher weniger musikalisch bin. Ich bin also an der Programmiersprache interessiert. Dazu müssen logischerweise möglichst viele Daten über mich gesammelt werden. Natürlich kann das Netzwerk was meine Daten aufbereitet auch andere Anfragen bearbeiten: Hobbys? Beruf? Interessen? -> Welche Werbung soll präsentieren werden? Insgesamt also eine Win-Win Situation?! Ein potenzieller Arbeitgeber der Zugriff auf diese Daten hätte, könnte meine C-Kenntnisse von der KI abfragen lassen. Dann lande ich im Ranking mit der Anfrage „C Aufbau“ auf den hinteren Plätzen. Wobei man mit „C function pointer callback“ in den vorderen Plätzen landet. Wer nicht glaubt wie einfach das ist, sollte mal folgendes Spiel probieren: http://20q.net/
  • Spracheingabe und Sprachausgabe: Siri und co. lassen grüßen und das in einer sehr guten Qualität. Zur Sprachausgabe werden bisher Laute aneinander gehängt, dass könnte sich auch bald ändern. Es gibt schon Netzwerke die direkt Binärdaten bearbeiten. Mit WaveNet lassen sich auch Emotionen in die Aussprache bringen. Leider ist der Rechenaufwand noch zu groß.
  • Bilderkennung: Ein gutes Beispiel von Klassifikation? Auch! PCs die Katzen- von Hundebilder unterscheiden können und bei den sozialen Netzwerken werden sofort die hochgeladenen Fotos mit den richtigen Freunden markiert. Jedoch kann der PC auch von den Bildern lernen und selbst tätig werden (von Kreativität möchte ich nicht sprechen). Ein bekanntes Beispiel ist googles DeepDream oder das Beispiel aus der Nano-Sendung.

Für die meisten Aufgaben könnte man die unterschiedlichsten machine learning -Verfahren verwenden. Durchgesetzt hat sich gerade bei sehr komplexen Aufgaben das künstliche neuronale Netzwerk. Dabei gibt es viele verschiedene Netzwerkstrukturen, die auch weiterhin Forschungsschwerpunkte bleiben werden. Welche Netzwerkstruktur kann welches Problem am besten lösen? Sind es die rückführenden Netzwerke, die Convolutional Neural Networks oder eine Kombination daraus die mein Aufgabenstellung lösen können? Vom eigentlichem Deep Learning wird immer dann gesprochen wenn das Netzwerk sehr viele Layer und Neuronen besitzt.

Was wohl immer zutrifft: Je komplexer die Aufgabe desto größer die nötige Rechenleistung oder desto cleverer der Entwurf des Netzwerkes.

Deep Learning auf dem Mikrocontroller?

Wenn man mit einem neuronalem Netzwerk soo viele Probleme lösen kann, wäre es doch praktisch diese Möglichkeit auf einem „kleinen“ CortexM Mikrocontroller ebenfalls verfügbar zu haben. Da Speicher und Prozessorleistung sehr begrenzt ist würde man wohl eher nicht vom Deep Learning sprechen. Für einfache „Multilayer-Feedforward-Netzwerke“ reicht es aber allemal.

Dafür habe ich die C-Bibliothek FANN (Fast Artificial Neural Network Library) auf den CortexM4 (mit FPU) portiert und verschiedene Benchmark-Untersuchungen unternommen. Diese werde ich evtl. später noch aufbereiten und auf dem Blog präsentieren.

Fazit war jedoch: Trainieren kann man ja auf dem PC und Ausführen auf dem Mikrocontroller, da alleine die Trainigdateien über der verfügbaren Flash-Größe liegen können. Dazu muss natürlich dem Mikrocontroller das angelernte Netzwerk übergeben werden. Das geschieht bei FANN über das Dateisystem, mit Hilfe von Konfigurationsdateien. Um keine Veränderungen an FANN selbst vornehmen zu müssen, habe ich dieses kleine Read-only Dateisystem entworfen.

Letztendlich habe ich mit der System Workbench FANN für den STM32F4 mit FPU (Hardware FPU Befehlssatz) cross-compiliert. Die entsprechende lib ist Bestandteil des Beispielprojektes und kann für weitere Versuche auf dem STM32F4 Discovery-Board oder anderen CortexM mit FPU verwendet werden. Später werde ich dem original FANN Github-Fork ein Static Lib – System Workbench Projekt hinzufügen.

Hello World or Blinky Neural Network Project

Ziel (sehr einfach): Ein einfaches Netzwerk wird trainiert, um eine der vier LEDs des Discovery-Boards nach der Trainingsphase blinken zu lassen. Der Benutzer kann sich durch zwei Buttons die LED aussuche. Aufbau KNN:

  • Vier Eingänge: der aktuelle Status jeder LED -> -1.0 = LED ist aus; +1.0 = LED ist an
  • Vier Ausgänge: soll LED an oder ausgeschalten sein -> -1.0 = LED aus; +1.0 = LED an
  • ein Hidden Layer mit 12 Neuronen (unnötig viel 😉 )

Ziel (einfach): Ein einfaches Netzwerk wird trainiert, um ein Blinkmuster auf den LEDs darzustellen. Dazu muss das Netzwerk auf die Übergänge zwischen den LEDs achten. Es darf keine LED doppelt aufleuchten, jedoch dürfen auch LEDs weggelassen werden. Der Benutzer kann sich durch zwei Buttons das Blinkmuster innerhalb der Spezifikation aussuche. Aufbau KNN:

  • Vier Eingänge: der aktuelle Status jeder LED -> -1.0 = LED ist aus; +1.0 = LED ist an
  • Vier weitere Eingänge: der vorhergehende Status jeder LED -> -1.0 = LED war aus; +1.0 = LED war an
  • Vier Ausgänge: soll LED an oder ausgeschalten sein -> -1.0 = LED aus; +1.0 = LED an
  • ein Hidden Layer mit 12 Neuronen (unnötig viel 😉 )

Es gibt unterschiedliche Arten, wie man ein neuronales Netzwerk trainieren kann. Mit Trainingsdaten wäre dieses „Hello World“ Projekt recht langweilig geworden. Ohne Trainingsdaten funktioniert es über das Reinforment Learning ganz ausgezeichnet. Dafür braucht es einen Agent. Dieser belohnt oder bestraft das Netzwerk, wenn die Aktion gerade nach Wunsch oder auch nicht stattfand. Den Agent könnte man sich programmieren, wenn die Möglichkeiten viel zu groß sind oder (wie in diesem Beispiel) man übernimmt  selbst die Aufgabe des Agents.

Da FANN keine Routinen für das bestärkende Lernen zur Verfügung gestellt, musste ich diese selbst implementieren. Ich habe mich für den Q-Learning Algorithmus entschieden und habe hier sehr gute Informationen und auch Implementierungsbeispiele erhalten:

http://outlace.com/Reinforcement-Learning-Part-3/

Einfach ausgedrückt funktioniert es so: Die Lernphase hat eine festgelegte Anzahl an Trainingszyklen. Am Anfang steht das untrainierte Netz. Es wird eine Zufallsaktion ausgeführt und auf die Bewertung gewartet. Die Bewertung ist schlecht, wenn der Fehler zwischen der erwarteten Aktion und der ausgeführten Aktion groß ist. Der Fehler wird durch einen Gradientenabstiegsverfahren (Backpropagation) Rückwärts durch das Netzwerk „geschickt“ und die Gewichtungen der Neuronenverbindungen werden angepasst. Nach und nach werden die Zufallsaktionen durch Vermutungen/Ausgaben die das Netzwerk trifft ersetzt. Bis am Ende fast ausschließlich Aktionen des Netzwerkes präsentiert werden.

Ein Problem besteht darin, innerhalb der Zufallsaktionen möglichst viele Möglichkeiten des Systems abzudecken, um das Netzwerk ausreichend trainieren zu können. Das Verhalten spiegelt sich auch in dem Hello World Projekt wieder: Nur eine LED blinken lassen benötigt nur ca. 10 Trainingszyklen, weil der Zufall hoffentlich jede LED einmal aufleuchten lassen hat. Für die Übergänge der LEDs gibt es viel mehr Möglichkeiten und daher werden auch mindestens 75 besser jedoch 100 Trainingszyklen gebraucht. Was natürlich für recht viel Arbeit sorgt und man muss selbst hochkonzentriert die Aktion bewerten. Daher ist mein Video auch weniger informationsreich geworden als geplant, weil ich mich stark auf die LED-Übergänge konzentrieren musste.

Genug der vielen Theorie, hier das Video des Projektes:

Das komplette Projekt gibt es wie immer auf meiner Github-Seite:

https://github.com/Counterfeiter/ANN-QLearning-CortexM4

Es ist darauf zu achten, dass FANN mit dynamischer Speicherreservierung arbeitet und das damit der Heap im Auge behalten werden sollte.

Gehen wir etwas in den Quellcode. Ein Netzwerk wird mit Standardwerten angelegt. Diese können aber durch einzelne FANN-Funktionen verändert werden. In diesem Beispiel wird die Aktivierungsfunktion aller Layer angepasst:

//create ann -> 1 hidden layer & (one input and one output layer)
 struct fann *ann = fann_create_standard(3, num_inputs, 12, num_outputs);

//use symmetric to deal with -1.0 and +1.0 -> normal for 0.0 to 1.0
 fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
 fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);

Mit fann_run wird das Netzwerk ausgeführt. Dazu muss natürlich der Input-Vektor übergeben werden. Dieser muss selbstverständlich so viele Werte besitzen wie es Input-Neuronen im Netzwerk gibt. Die Rückgabe ist ein Pointer auf den Output-Vektor, der natürlich so lang ist wie es Neuronen am Ausgang gibt.

Da es beim Reinforcement Learning meist nur einen Datensatz gibt, wird dies effektiv mit fann_train angelernt. Da wir trainieren, müssen zu den Inputs auch die passenden Outputs übergeben werden. Q-Learning sorgt dabei dafür, dass im Ausgang der eingetretene Fehler „markiert“ wurde.

Wie man sieht, die FANN-lib nimmt erstaunlich viel Arbeit ab. Es sind gerade mal fünf verschiedene Funktionen angewendet wurden, um mit neuronalen Netzwerken arbeiten zu können. Klasse!

Wer sich selbst versuchen möchte, mein Vorschlag um die Funktionen weiter voranzutreiben: Es gibt durch die beschränkte Anzahl an Eingabevektoren keine Anhaltspunkt darauf wie oft eine LED vorher geblinkt hat. Das heißt, es ist nicht möglich das eine LED zwei mal aufleuchtet, bevor es zur nächsten LED übergeht. Die Lösung wäre für jede LED eine Zählvariable (normiert auf -1.0 bis 1.0) mit in den Input zu geben. Dies resultiert in ein beliebig komplexes Blinkmuster. Da die Möglichkeiten weiter steigen, müsste zielführend ein programmierter Agent das Netzwerk anleiten und über mehrere 100 bis 1000 Trainingszyklen führen. Die Anzahl der Hidden Layer und Neuronen wird, je nach Komplexität des gewünschten Blinkmusters, ansteigen müssen.

Ich wünsche happy coding mit neuronalen Netzen. Ich würde mich freuen, wenn ihr eure Ergebnisse in den Kommentaren hinterlasst.

 

 

Advertisements
Dieser Beitrag wurde unter Allgemein veröffentlicht. Setze ein Lesezeichen auf den Permalink.

3 Antworten zu Künstliche neuronale Netze auf einem Mikrocontroller

  1. Pingback: Robot controlled by artificial neural network | Electronic Stuff

  2. Johannes schreibt:

    Hallo,
    ein sehr interessantes Projekt. Hast du dein Neuronales Netzt auch Grafisch komplett aufgezeichnet und würdest das Veröffentlichen? Input- und Output-Layer sind mir klar (die 4 LEDs). Aber bei den Hidden-Layer habe ich noch große Probleme, wie man die aufstellt.

    Johannes

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s