Neuronale Netze Tutorial – Übersicht der Konzepte

Da hab ich also nun bereits zum zweiten Mal das Buch Neuronale Netze selbst programmieren gelesen und habe aber immer noch Mühe, das ganze in ein Big Picture zu setzen. Daher möchte ich in diesem Beitrag alle Konzepte zusammentragen, die für ein neuronales Netz wichtig sind, ohne hier auf die Details einzugehen.

Ich probiere, das Thema so lange herunterzubrechen, bis man das Wesen der neuronalen Netze auf einen Blick erkennen kann. Von diesem Punkt an kann man sich dann wieder darauf konzentrieren, einzelne Themen ins Detail zu studieren.

Übersicht der Konzepte

Folgende Themen fassen den Umgang mit neuronalen Netzen zusammen:

1. Neuronen und Sigmoidfunktion
2. Verschiedene Schichten
3. Gewichte
4. Matrizen
5. Eingangs- und Ausgangssignale von Knoten
6. Knotenwert = Gewichte * Eingabesignale
7. Fehlerrückführung
8. Gradientenabstiegsverfahren
9. Steigung mittels Differenzialrechnung ermitteln
10. Eingabedaten und Ausgabedaten skalieren
11. Geeignete Anfangsgewichte wählen

1. Neuronen und Aktivierungfunktion (Sigmoidfunktion)

Neuronen feuern, wenn sie einen gewissen Schwellwert erreicht haben. In der Mathematik spricht man dabei von einer Aktivierungsfunktion. Die meisten Bücher fangen mit einer Sprungfunktion an, welche bei einem gewissen Wert (etwa x = 3) von 0 auf 1 springt und so die Ausgabe des Neurons aktiviert. Beim nächsten Beispiel geht es dann meistens weiter mit der Sigmoidfunktion, weil diese für eine Aktivierungsfunktion besser geeignet ist – Denn die Natur macht keine Sprünge.

So sieht die Sigmoidfunktion übrigens aus. Man sieht schön, dass bei x = 0 der y-Wert 0.5 beträgt.

sigmoid.gif

Ein Neuron kann dabei viele Eingänge haben. Die Werte dieser Eingänge werden summiert und eben an die Sigmoidfunktion übergeben. Wird der Schwellwert überschritten, feuert das Neuron seine tödlicher Salve auf den Gegner… ach ne, was steht hier? „Das Neuron feuert ein Signal entlang des Axons zu den Terminalen, um es an die Dendriten der nächsten Neuronen weiterzugeben.“ … Hmm… Na gut, das klingt aber bei Weitem nicht so dramatisch.

2. Schichten oder Layers

In einem neuronalen Netz verwendet man mehrere Schichten, und jede Schicht enthält eine bestimmte Anzahl Knoten. Im einfachsten Fall mit drei Schichten hat man eine Eingangsschicht, eine versteckte Schicht und eine Ausgabeschicht.

neural_net.jpeg

3. Gewichte – Where the magic happens

Gewichte sind das absolut zentrale eines neuronalen Netzes (oder auch anderen Machine Learning Algorithmen wie dem Perzeptron). Dort passiert nämlich das, was man als Lernen bezeichnet.

Anmerkung: Im Bild oben von Punkt 2. repräsentiert jeder Pfeil von einem Knoten zum nächsten ein Gewicht.

Jeder Knoten in einer Schicht ist mit jedem Knoten der letzten und der folgenden Schicht verbunden. Wenn man das Netz nun trainiert, wird jedes Gewicht entweder verstärkt oder abgeschwächt, je nachdem, ob es für die aktuelle Aufgabe wichtig oder irrelevant ist.

Ein Gewicht ist dabei einfach eine Zahl. Und dabei ist es nur natürlich, dass ein Gewicht von etwa 15 wichtiger ist als ein Gewicht von 2. Diese Zahlen beziehungsweise Gewichte wurden ja schon bei Punkt 1. genannt als Eingabe bei der Sigmoidfunktion. Sie werden also zusammenaddiert und jeder Knoten hat dann quasi eine Gewichtssumme.

Bezeichnet werden die Gewichte üblicherweise mit w und dem entsprechenden Index des jeweiligen Gewichts. „W1,2“ etwa steht für das Gewicht vom ersten Knoten zum zweiten Knoten der Folgeschicht.

Wichtige Verständnisfrage: Wenn es drei Schichten hat und jeder Knoten der einen Schicht ist mit jedem Knoten der Folgeschicht verbunden – Sind es dann zwischen Schicht 1 und 2 diesselben Gewichte wie zwischen Schicht 2 und 3?
Antwort: Es sind nicht dieselben Gewichte. Jede Schicht hat eigene Gewichte zur Folgeschicht, je grösser also das Netz, desto mehr Gewichte hat man. Hat man beispielsweise ein Netz mit 3 Schichten à 3 Knoten, sind das insgesamt 18 Gewichte
(Alle 3 Knoten der ersten Schicht verbunden mit allen 3 Knoten der zweiten
Schicht = 9 Gewichte. Dann nochmals 9 Gewichte für die Knoten der zweiten zur dritten Schicht = 18 Gewichte total)

Das Lernen eines neuronalen Netzes ist also tatsächlich die Anpassung dieser Gewichte.

4. Matrizen und deren Multiplikation

Nun ja es wird halt alles in diesen ganz praktischen mathematischen Behältern namens Matrizen gespeichert. Und die werden halt miteinander multipliziert. Das ist aber keine Hexerei.

Einfach nicht vergessen: Entgegen dem natürlichen menschlichen Empfinden (wie etwa beim Lesen eines Buches) kommen zuerst die Reihen und dann die Spalten. Eine 5 * 2 Matrix halt also 5 Zeilen und 2 Spalten.

Die Matrixmultiplikation verwendet man etwa für das Multiplizieren der Eingangssignale und den Gewichten. Wie, Eingangssignale kamen in diesem Blogpost noch gar nicht vor? Verdammt! Das muss ich sofort ändern.

5. Eingangs- und Ausgangssignale der Knoten

Die Eingangssignale sind der Input in das neuronale Netz – Also die Daten, die wir analysieren müssen. Die Ausgangssignale sind die targets, also die Zielwerte, die das neuronale Netz vorhersagen soll.

Hat man etwa als Input Bilder von handschriftlich gemalten Zahlen, die als 28*28 Pixel-Bilder gespeichert sind, kann man für jeden Pixel den Grauwert (eine Zahl zwischen 0 und 255) nehmen und erhält so total 784 Eingabeknoten.

Will man mit diesen Eingabeknoten die Zahlen von 0 bis 9 vorhersagen, benötigt man 10 Ausgabeknoten ganz am Ende des neuronalen Netzes. Dabei darf man aber nicht vergessen, dass jeder Knoten in den mittleren versteckten Schichten auch Ausgabeknoten haben.

Der Ausgabewert dieser Knoten ist dann all das, was hineingegangen ist – und was dann noch in die Aktivierungs-/Sigmoidfunktion gequetscht wurde.

Und jeder Ausgabewert ist dann halt wiederum der Eingabewert der folgenden Schicht.

6. Knotenwert = Gewichte * Eingabesignale

Okay okay, ich wiederhole mich hier. Jetzt geht es nochmals um Gewichte und Eingabesignale? Wo ist hier die Struktur dieses Posts? Nun, die Struktur sagt: Ein Punkt wird abgeschlossen, ein neuer kriegt auch eine neue Nummer.

Ich habe geschrieben, dass wir aus unseren Daten die Eingangssignale bestimmen. Und der Knoten in der darauffolgenden Schicht macht dann irgendwas mit diesen Daten und auch noch mit den Gewichten, denn jeder Knoten der ersten Schicht ist mit jedem Knoten der zweiten Schicht verbunden.

Aber was wird gemacht? Ganz einfach:

x = (Input_1 * Gewicht_1) + (Input_2 * Gewicht_2) + ...

x ist dabei einfach der Wert, den wir zusammenrechnen, um zu sehen ob ein Neuron feuert. Man nennt diesen Knotenwert auch Nettoeingabe.

knotenwerte.png

Ich wiederhole mich bewusst immer wieder, denn nur mit etlichen Links auf die vorherigen Punkte kann ich das Gesamtbild zusammenfügen. Denken Sie nur daran: Sobald Sie bei einem Satz nicht verwirrt sind und stattdessen „Bah alter Käse das weiss ich schon lange“ sagen, haben sie das Prinzip dahinter verstanden.

7. Durch Fehler lernen

Hier hört man auch immer den Satz: Man will die Gewichte moderieren.

Schmeisst man alle Inputs in ein neuronales Netz kriegt man für einen Knoten am Ende einen berechneten Wert, etwa 5. Tatsächlich sollte aber 8 herausgekommen sein. Schön doof, oder?

Keineswegs! Die Differenz dieser beiden Werte 8 – 5 = 3 ist der Ausgabefehler und wird vom Ausgang des neuronalen Netzes wieder rückwärts in das Netz reingeworfen und auf die Knoten verteilt, so dass diese die Gewichte moderieren – also anpassen können.

Moderieren heisst dabei einfach, dass man nicht den vollen Wert 3 auf die Knoten in der vorherigen Schicht verteilt, sondern vielleicht nur die Hälfte. Das Ziel ist eines neuronalen Netzes ist es nämlich, sich langsam aber stetig den richtigen Werten anzupassen.

Hat ein Knoten etwa zwei Eingabesignale wird der Wert 3 nicht gleichmässig auf diese Knoten verteilt. Stattdessen wird das Gewicht der beiden Knoten noch berücksichtigt. Hat Knoten 1 ein Gewicht von 7 und Knoten 2 ein Gewicht von 14 kriegt Knoten 2 auch brav ein grösseres Stück vom Kuchen, da es anscheinend auch einen grösseren Hunger hat.

Die ganze Berechne das Netz und gehe dann wieder Rückwärts, um die Fehler auf die Knoten zu verteilen nennt man übrigens Fehlerrückführung (englisch Backpropagation).

8. Die Beziehung zwischen Gewichten und dem Fehler

Das GradientenmannistdiesesWortlangVerfahren habe ich Ihnen schon mal erklärt (Siehe Was ist das Gradientenabstiegsverfahren?). Es ist aber wirklich wichtig, dass Sie verstehen, dass man ein neuronales Netz nicht auf einen Schlag lösen kann. Ich meine, das wird Ihnen schon klar sein, immerhin reden wir von trainieren und Fehlerrückführung und so weiter. Es gibt aber (Mathe-) Probleme, die kann man auf einen Schlag lösen, und das geht bei neuronalen Netzen eben nicht. Stattdessen nähern wir uns immer mehr der optimalen Einstellung der Gewichte.

Und jetzt kommt eben das Gradientenverfahren ins Spiel. Sie können jetzt gerne überall im Netz die Theorie davon lesen. Wirklich wichtig ist nun folgendes: Wir machen das Verfahren auf einer Funktion, bei der der x-Wert alle Gewichte sind und der y-Wert der Fehler des neuronalen Netzes!

Anders gesagt: Der Fehler ist die Abhängige Variable y der Gewichte x und je nachdem, welche Werte all diese verschiedenen x’es haben, verändert sich der Fehler y zum Guten oder zum Schlechten – Wobei „gut“ ein tiefer Wert ist und „schlecht“ ein hoher Wert ist. Und mit „tief“ meine ich das absolute Minimum und das finden wir heraus mit dem Grassalbitursteinschlagverfahren ähm Gradientenabstiegsverfahren!

Sehen Sie diesen wichtigen Zusammenhang? Die ganze mathematische Theorie nützt einem nichts, wenn man sich diesem Zusammenhang zwischen Fehler und Gewichten bewusst ist.

Jedes Gewicht ist dabei eine Dimension, wenn man das tatsächlich streng mathematisch anschauen. Das ist aber dann schlussenlich nicht so relevant, weil wir uns etwa eine 10’000er Dimension eh nicht mehr vorstellen können.

9. Oh Shit – der schwere Teil

Dieser Punkt heisst korrekt: „Das Gradientenabstiegsverfahren und die Differenzialrechnung – Kann es Liebe sein?

Dieser Punkt ist mit Abstand der Schwierigste, weshalb ich dessen kurze Erklärung einfach noch ein wenig hinauszögern möchte. Habe ich Ihnen schon einmal erzählt, dass der Erfinder des Wortes Jogging genau beim Joggen an einem plötzlichen Herztod gestorben ist?

Nun gut. Tief durchatmen.

Ich beginne ganz einfach: Hat man eine Funktion, die wie ein U geformt ist, und man fängt links oben an einem Punkt an und will zum Minimum unten in der Mitte, dann läuft man nach unten. Die Steigung ist dabei negativ, denn sie zeigt nach rechts unten. Ist die Steigung negativ, laufen wir also nach rechts. Ist die Steigung positiv, laufen wir nach links.

Ah ja, und da haben wir es schon: Punkt 8. erzählt ja die hinreissende Geschichte zwischen Gewichten und dem Fehler, und wie findet man heraus, wie sich ein Ding y ändert je nachdem wie sich Dinge x ändern? Da macht man mit der Differenzialrechnung – Und das ergibt dann die gewünschte Steigung, die uns sagt, ob wir nach links oder rechts laufen müssen.

Wobei… ja gut, ich muss ehrlich sein… am Anfang hat mich das schon ein wenig verschreckt. Aber eigentlich ist es nicht sooo wild. Man macht die Ableitungen der Fehlerfunktion und diejenige der Sigmoidfunktion, verwendet dafür die Kettenregel… das ist wirklich interessant, wenn man das nachvollzieht, aber schlussendlich könnte man wohl auch überleben, wenn man einfach die vollendete Formel dafür verwendet, wie es tagtäglich tausende Studenten weltweit tun (ich nicht).

Ich finde es darum viel wichtiger, dass man überhaupt versteht, warum man die Steigung braucht. Und was der Link zwischen dem Fehler, den Gewichten und der Differentialrechnung ist.

Darum fasse ich das noch einmal kurz zusammen:

  • Die Steigung brauchen wir, um bei der Fehlerfunktion den Fehler zu minimieren. Die Steigung sagt uns, in welche Richtung man gehen muss, um zum Minimum = zum kleineren Fehler zu kommen.
  • Der Fehler ist die Abhängige Variable y und diese ist abhängig von allen Gewichten w (also in der Mathe jeweils die x-Achse). Die Differentialrechnung, also die Ableitung oder eben die Steigung, sagt uns: Wie verändert sich der Fehler wenn sich die Gewichte ändern?

Und wie genau verwenden wir die Steigung schlussendlich? Nun, wir haben eine Formel dafür und setzen dann in Python einfach die benötigten Werte ein. Ja ja ich weiss, diese Erklärung klingt jetzt wieder mal total bescheuert, aber es ist echt schwer, das Ganze in Worte zu fassen. Wir haben eine Formel und die verwenden wir, um die Gewichte anzupassen. Denn das ist ja das absolute Ziel des neuronalen Netzes, am Ende sinnvolle Gewichte zu haben.

10. Eingabedaten und Ausgabedaten skalieren

Die zur Verfügung stehenden Daten müssen zuerst entsprechend bearbeitet werden, bevor sie in das neuronale Netz eingespiesen werden. Fakt ist: Bei uns in der Vorlesung war einmal ein Mitarbeiter von Google, der auf diesem Gebiet gearbeitet hatte. Er hat uns dann auch klar gesagt, dass ein grosser Teil der Arbeit eines Data Scientists in der Vorbereitung der Daten liegt. Und das kann gut mehrere Wochen dauern pro Auftrag, bei dem man die Daten bearbeiten muss.

Kurze Repetition: Mit Eingabedaten meine ich die Werte, die in die Knoten reingehen (Die Eingaben in die Aktivierungsfunktion). Ausgabedaten sind die Daten, die dann aus den Knoten rauskommen, also die Ausgabe der Sigmoidfunktion.

Man muss immer daran denken, dass die Eingabedaten schlussendlich in den Knoten landen, wo sie dann in die Aktivierungsfunktion – oftmals in die Sigmoidfunktion gepresst werden. Grosse x-Werte streben bei der Sigmoidfunktion jeweils nach y = 1, während kleine x-Werte nach y = 0 streben (die Sigmoidfunktion gibt nur den Bereich von 0 bis 1 aus). Grosse und auch allzu kleine x-Werte sollte man dabei verhindern, da es sonst zu einer Sättigung des Netzes kommen kann und es nicht mehr optimal lernt.

Oftmals skaliert man die Eingabewerte auf den Bereich von 0.01 bis 1.01. Nullwerte sollte man ebenfalls verhindern, da dies die Lernfähigkeit für diesen Eingabewert völlig zerstört, da dann eine Multiplikation mit 0 geschieht.

11. Geeignete Anfangsgewichte wählen

Es wäre fatal, wenn man zu Beginn alle Gewichte mit Null oder mit dem gleichen Wert initialisieren würde, weil dann das Netz nicht gut lernen kann. Für die Knoten der zweiten Schicht multipliziert man ja jeden Eingabewert der ersten Schicht mit dem dazugehörigen Gewicht. Da das Gewicht aber immer gleich wäre, würde jeder Knoten der zweiten Schicht den gleichen Wert erhalten. Dann würde natürlich die Fehlerrückführung auch nicht funktionieren.

Die Werte sollten zufällig in einem bestimmten Bereich gewählt werden – Aber in welchem Bereich? Mathematiker haben die folgende Faustregel aufgestellt: Man nimmt die Anzahl Verknüpfungen, bildet davon die Wurzel und dann davon wiederum den Kehrwert ( = 1 durch x). Wenn zu einem Knoten also beispielsweise 3 Verknüpfungen führen, wäre der Bereich für die Gewichtsinitialisierungen von -1/Wurzel(3) bis +1/Wurzel(3).

Puh. Alles klar?

2 Gedanken zu „Neuronale Netze Tutorial – Übersicht der Konzepte

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 )

Facebook-Foto

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

Verbinde mit %s