The principle of neural networks – Eine einfache Sicht auf neuronale Netze

Meine Gedanken schweifen im Moment in die Richtung Abstraktion für neuronale Netze. Irgendwie hab ich den Eindruck, dass mein altes feed-forward Programm nicht mehr ganz aktuell ist, wenn ich auf die deep belief networks schaue. Es ist ja so, dass man(mit einigem Aufwand) so ein deep belief network bauen kann. Sonst gäbe es sie ja nicht. Aber das ist immer mit sehr viel Tipperei verbunden, und nicht jeder, der diese Netze gerne mal austesten würde beherrscht eine Programmiersprache und kennt sich mit den mathematischen Hintergründen aus. Wie Komplex diese Dinger sind merkt man spätestens wenn man mal versucht eins selbst zu programmieren(wie ich es jetzt in C# geschafft hab). ich könnte jetzt meinen Quellcode posten, und vielleicht würde ihn sogar der eine oder andere verstehen, aber irgendwie gefällt mir der Ansatz nicht für jedes dieser Netzte ein neues Programm zu schreiben(was dann auch noch so riesig ist!). Deshalb ja auch schon meine klein Anwendung für feed-forward. Also muss auch für die deep belief networks ein Tool her(meiner Meinung nach). Allerdings findet man im Internet dazu überhaupt nichts, und zumindest die meisten Tools haben die Dinger nicht in ihrer Feature-liste. Die Antwort in den Support Foren ist dann meistens: Haben wir nicht, kriegen wir auch nicht. Und auch bei den Tutorials steht überall: „Man schreibe den Code für …“. Also gibt es noch nicht wirklich ein Tool, was deep lerning Algorithmen bereitstellt. Ok, aber warum nicht? ist es denn wirklich so schwierig? Ich denke ja, und nein. Zunächst: ja, natürlich, komplizierter als Backprop allemal, und wenn  man dann noch wie Hinton Backprop und deep lerning kombinieren will, wirds erst recht kompliziert, wenn nicht sogar unmöglich für die meisten(zum Beispiel mein Programm). Also, warum dann nein? Weil ich der Meinung bin, dass man für so etwas zunächst mal seine Perspektive verändern muss:

Was ist ein neuronales Netz, woraus besteht es?

Alle neuronalen Netze sind doch im Prinzip sehr ähnlich in gewissen Dingen. Da fallen mir eigentlich 3 ein, nämlich: Sie alle haben Gewichte, Schichten, und Abhängigkeiten. Abhängigkeiten heißt: Unit a hängt von Unit b mit dem Gewicht g ab. Schichten deshalb, weil ich einfach zu faul bin über 700 Units einzeln aufzulisten. Wenn das nötig ist befindet sich eben in jeder Schicht nur eine Unit, aber der Benutzer soll  nicht(wie das bei manchen Tools der fall ist) mit einem riesigen Bild von Kreisen die über Striche verbunden sind verärgert werden, weil er da sowieso nicht durchsieht, sondern er soll eine schöne überschaubare Liste haben, die soweit wie möglich zusammengefasst ist. Deshalb: Schichten, und keine Units, das ist der erste wichtige Punkt. Abhängigkeiten definieren dann eben nicht Abhängigkeiten zwischen Units, sondern zwischen Schichten, und Gewichte sind Eigenschaften der Abhängigkeiten. Warum also die Gewichte noch von den Abhängigkeiten trennen, eigentlich gibt es doch(normalerweise) auch nur einfach Gewichte, und nicht noch was drüber. Mein Gedanke war da der folgende: Zum Lernen brauch ich Gewichte, und wirklich nur Gewichte. Ich hab einen Algorithmus, und der verändert Gewichte nach einem bestimmten Schema(natürlich anhand von Lerndaten, die aber nicht Bestandteil des Netzes sind). Das heißt ich trenne die Lernlogik von der Ausführungslogik, was es mir ermöglicht mehrere Gewichte mit unterschiedlichen Algorithmen zu trainieren, und auf eine noch ganz andere selbst definierte Weise auszuführen.

Also haben wir jetzt eine ganz neue Definition von einem neuronalen Netz, die sich viel leichter implementieren lässt. Wie das Programm aussieht, und wie es im konkreten funktioniert, darüber später mehr. Wichtig für mich ist das Folgende: Finden wir eine Definition für alle neuronalen Netze, die so einfach ist, dass sie 1. der Benutzer versteht und anwenden kann, ohne sich über Algorithmen Gedanken machen zu müssen, und die aber auch so genau ist, dass 2. ein Algorithmus genau weiß, was er zu tun hat, und die 3. so dynamisch ist, dass sie auf alle erdenklichen Variationen von implementierten Algorithmen angewandt werden kann, weil: der Algorithmus ändert sich ja nicht, sondern nur die Struktur, wie das Netz ausgeführt wird, oder besser gesagt wie die Gewichte ausgeführt werden.

Demzufolge liegt mir besonders die Trennung von Gewichten und allen anderen Bestandteilen am Herzen, weil die es ermöglicht den Algorithmus immer so zu lassen wie er ist.

Hier schon mal ein erster Screenshot von der Anwendung im Design-Modus:

Ein einfaches feed forward Netz ohne hidden Schicht:

Viele Layer im deep belief network

In einem der letzten Posts war bereits  eine ganz einfache Restricted Boltzmann Machine das Thema, jetzt soll es darum gehen, wie mehrere Layer aussehen, und wie das Lernen implementiert wird.

1. Wie funktioniert eine RBM?

Wir müssen uns bei dem Umstieg von einem feed-forward Netz zur RBM zunächst mit einigen grundsätzlichen Dingen vertraut machen. Ein feed-forward Netz hat Eingabe- und Ausgabeunits, und beliebig viele versteckte zwischengeschaltete Schichten. Von der Optik her sieht die RBM ganz ähnlich aus, mit dem Unterschied, dass es prinzipiell nur 2 Schichten gibt, wobei die Units allerdings nicht nur in eine Richtung, sondern in beide Richtungen miteinander verkettet sind:

Eine RBM verwendet ausschließlich binäre Aktivierungsfunktionen, wobei beachtet werden muss, dass das ganze mit Wahrscheinlichkeiten rechnet, dazu aber später mehr. Das Netz berechnet sich aber jetzt nicht mehr so einfach wie feed forward(logisch), sondern die erste Schicht dient sowohl als Eingabe-, als auch als Ausgabeschicht, wobei wenn sich diese ändert, sich wieder die nachgeschaltete Schicht ändert, die wieder die erste Schicht beeinflusst u.s.w. Ein bestimmter Zustand des Netzes bezeichnet eine sogenannte Energie des Zustandes. Je geringer diese Energie umso wahrscheinlicher, dass das Netz in der Stellung verharrt, oder je größer die Energie umso größer die nachfolgende Änderung, ähnlich wie beim Hopfield – Netz. Die Energie berechnet sich aus den bestimmten Zuständen der Units und den Gewichten nach der Formel:

v = ist der Zustand aller input(visible) Units, also erste Schicht(normalerweise Vektor oder im Programm eine Liste),
h = der Zustand aller hidden Units(zweite Schicht),
w = Gewichtsmatrix
b = die bias Parameter der Units(könnte auch als eigenständige Bias Unit entwickelt werden, dann entspricht dieser Wert den Bias – Gewichten auf die Unit)

Es gibt also nicht mehr input und target Werte, sondern einfach nur noch input Werte, was die ganze Sache vereinfacht, das Netz lernt sich selbst ein. Der Lernvorgang bezeichnet also eigentlich das Vermindern der Energie für einen bestimmten Zustand, damit die Wahrscheinlichkeit höher ist, dass dieser am Ende herauskommt. Zu der Verknüpfung der gelernten Daten mit anderen Werten als den inputs kommen wir später mal. Jetzt ist unser Ziel erst mal die exakte Reproduktion gelernter Daten(d.h. ich lerne einen Zustand ein, und wenn der dann wieder kommt ändert sich das Netz nicht, d.h. der Zustand hat eine zu geringe Energie für eine Änderung)

Das tatsächliche Lernverfahren ist relativ einfach:

  • Als erstes werden neben den input und hidden Schichten noch zwei weitere Schichten erstellt, die wir nur zum Lernen brauchen. Wir erstellen also ein Netz was das Netz trainiert. Die neuen Schichten entsprechen den ersten Schichten, und auch die Gewichte sind immer dieselben. Dieses Kopieren hat den einzigen Grund, dass die Werte zwar reproduziert werden sollen, aber der Eingangsvektor unverändert bleiben soll, weil der dann später noch gebraucht wird. Man kann sich das auch so vorstellen, dass das Netz nur einmal ausgeführt wird, also die input Werte genau einmal auf die hidden, dann wieder auf die inputs, und dann noch mal auf die hidden propagiert werden, und zwischendurch alle Werte und Zwischenstellungen gemerkt werden. Sieht dann so aus:
  • Dann werden die Gewichte zufällig initialisiert(oder eben bereits vorhandene Gewichte von anderen Lernvogängen sind schon da), wobei alle drei Gewichtsmatrizen exakt gleich sind. Man schaue sich zur Verdeutlichung noch mal  das Bild weiter oben an. Man sieht, dass die Gewichte nach oben und unten übereinstimmen, und da der letzte sowieso  nur eine Kopie vom ersten ist sind die natürlich auch gleich.
  • Jetzt kommt das eigentliche Lernen(simpel): Der Eingabevektor wird auf die ganz linke untere Schicht draufgegeben, dann werden alle anderen Schichten berechnet. Wenn in den beiden linken Schichten zwei Units gleichzeitig aktiv(eine oben, eine unten) sind wird deren Gewicht um eine Lernrate erhöht, wenn dasselbe in der rechten der Fall ist wird das Gewicht um die Rate vermindert.

Fertig mit Lernen!

2. Wie funktioniert ein deep belief network?

Was ist der Unterschied zwischen einer RBM und einem deep belief network? Ganz einfach ein deep belief network ist eine Stapelung von RMB´s, das heißt ich nehme einfach die hidden Schicht von meiner RBM als input für die nächste RBM, die dann oben darüber gebaut wird. Dann nehm ich deren hidden wieder als input für die nächste, und so weiter… Das macht das Ganze wesentlich effizienter, weil wir viel mehr Regelmäßigkeiten/Features oder Muster oder wie man es auch immer nennen will erkennen können, weilwir in den Mustern wieder nach Mustern suchen. Klingt vielleicht kompliziert, ist es aber nicht. Am Lernen ändert sich überhaupt nichts. Es wird einfach erst die unterste RBM eingelernt, dann wird mit deren hidden die nächste trainiert, und die nächste und nächste…

Wäre natürlich langweilig wenn das alles über deep belief networks wäre, man kann die noch tunen, da kommt dann wieder mal die Backprop, aber das schreib ich jetzt nicht mehr hier rein. Also: Auch wenn Deutschland(leider) jetzt nicht ins WM Finale kommt: Das war erst der Anfang(und nicht das Ende, wie bei der WM)

Restricted Boltzmann Machine – einfacher Test

Die neue Generation der neuronalen Netzwerke ist angebrochen!
Ich hab mal versucht meine Infrastruktur auf die neuronalen Netze von hier anzuwenden. Natürlich erst mal mit dem ganz einfachen Beispiel am Anfang probiert. Meine Anwendung sieht jetzt so aus:

Dazu hab ich mir noch ein kleines Control geschrieben, was die Pixeleingabe ermöglicht. Man kann also jetzt ein Muster eingeben:

und dann mit Train trainieren. Ich hab das mal mit ein Paar Einsen ausprobiert, und jetzt kommt eine Eins, die er noch nie gesehen hat:

Ok, zugegeben ich bin nicht besonders gut im Einsen malen, aber er macht die noch schön gerade, und verändert sie dabei fast nicht:

Und dabei hab ich ihn nur mit handgemalten(hässlichen) Daten, und auch nicht mit besonders vielen trainiert. Find ich genial, vor allen Dingen, weil das ja eigentlich noch gar nicht fertig ist, es kommen ja noch sehr viele Schichten dazu(also höhere Genauigkeit). Ich werden demnächst mal meine Anwendung mit diesen Beispielen erweitern. Vorher sind aber erstmal die  nächsten Schichten angesagt.