Neuronale Netze einfach erklärt

Passend zu unserem "KI für ESP32"-Artikel in Make 1/22 liefern wir ein paar Grundlagen, mit denen sich der Aufbau neuronaler Netze besser verstehen lässt.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen
Lesezeit: 12 Min.
Von
  • Josef Müller
Inhaltsverzeichnis

Der Begriff neuronales Netzwerk zeigt zwar im Namen, an welche biologischen Analogie diese Struktur angelehnt ist. Dennoch hinkt der Vergleich mit den in Gehirnen vorhandenen Nervenzellen, wie insbesondere Gehirnforscher immer wieder betonen. Denn künstliche Neuronen sind im Prinzip nur mathematische Formeln, die von sehr einfachen Modellvorstellungen echter Neuronen inspiriert sind. Dennoch teilen künstliche neuronale Netze einige Eigenschaften, insbesondere die Verschaltung vieler mehr oder minder einfacher, dafür aber in großer Zahl eingesetzter Komponenten. Ähnlich zu Nervenzellen funktionieren sie über eine innere Aktivierung, also eine Schwelle, ab der sie an ihrem Ausgang einem Impuls abgeben. Der wird durch eine Vielzahl an Verbindungen an benachbarte Knoten weitergeleitet und beeinflusst dort deren Aktivität..

Im Vergleich zwischen Nervenzellen und künstlichen neuronalen Netzen sieht man sehr ähnliche Strukturen: Nervenzellen/Neuronen, die als Eingang und Ausgang dienen, Verbindungen zwischen beiden und einen vielschichtigen Informationsfluss über ein komplexes Netzwerk mit vielen Verbindungen. Bei künstlichen neuronalen Netzen gibt es eine klare Struktur mit Eingangsneuronen, internen Verarbeitungsschichten und Ausgangsneuronen. Dies gibt uns eine Vorstellung, woran die Begriffe und die Architektur von künstlichen neuronalen Netzen angelehnt sind.

Betrachtet man zunächst ein einzelnes künstliches Neuron (Knoten) genannt, so hat dieses einen Zustand (y), der einer reellen Zahl entspricht, typischerweise eine Gleitkommazahl. Dieser Aktivierungszustand beziehungsweise der Wert der Gleitkommazahl wird über Verbindungen zu den Eingängen von anderen Neuronen weitergegeben. Die Verbindung zwischen zwei Neuronen können wie bei echten Nervenzellen stärker oder schwächer ausgeprägt sein und dementsprechend mehr oder weniger von der Aktivierung des sendenden Neurons übertragen.

Bild 1:Ein künstliches Neuron funktioniert im Prinzip wie ein kleiner Taschenrechner.

Diese Verbindungsstärke wird in einem Gewichtungsfaktor (w) abgebildet, mit dem die Aktivierung multipliziert wird, bevor sie im empfangenden Neuron ankommt. Ein Neuron hat nun mehrere solcher Eingänge und die Eingangsaktivierung wird zunächst einfach aufsummiert (z) (Bild 1). Nun könnte man diese Eingangsaktivierung unverändert wieder an den Ausgang zu den nächsten Neuronen weiterleiten. Dann jedoch wäre das gesamte System vom Eingang bis zum Ausgang nur ein einfaches lineares Gleichungssystem und daher in der Funktionsweise sehr beschränkt. Um auch komplexere Funktionen über künstliche Neuronen realisieren zu können, wird der ursprüngliche Ausgang des Neurons an eine nichtlineare Transferfunktion weitgegeben, der sogenannten Aktivierungsfunktion (y=f(z)).

Typische Transferfunktionen sind: Sigmoidal-Funktion, Arctanhyperbolicus und auch die ReLu-Funktion (Rectified Linear unit). Letztere arbeitet quasi wie ein Gleichrichter und lässt nur positive Werte linear zum Ausgang passieren. Sie ist sehr einfach implementierbar und benötigt wenig Rechenleistung. Dies ist bei neuronalen Netzen mit teilweise vielen tausenden Neuronen ein wichtiger Aspekt. Zusätzlich gibt es noch einen Bias pro Neuron, mit dem sich ein Schwellwert festlegen lässt, ab der ein Wert am Ausgang erscheint.

Einfache neuronale Netze bestehen aus drei Arten von sogenannten Schichten (engl. Layer): einem Eingangs-Layer, einer bis mehrere internen Schichten und einer Ausgangsschicht (Bild 2). Am Eingang-Layer werden die Eingangsinformation eingespeist. Wenn man etwa ein 10x10 Pixel großes Schwarzweißbild hat, so würde man 100 Eingangsneuronen wählen. An jedes dieser Neuronen würde genau ein Neuron angeschlossen werden, an dem die Bildinformation schwarz/weiß als 1/0 anliegt.

Bild 2: Ein einfaches neuronales Netz mit Eingangsschicht, internal bzw hidden Layer und einer Ausgabeschicht

Diese ersten Neuronen sind über individuelle Verbindungstärken (Gewichte) mit den Neuronen im ersten internen Layer verbunden. Interne Layer werden oft auch hidden Layer genannt. In erster Näherung ist jeder Ausgang eines Eingangsneuron mit allen Eingängen der Neuronen im hidden Layer verbunden. Die dort berechnete jeweilige Aktivität wird an weitere interne Layer oder schließlich an den Ausgang-Slayer weitergegeben. Dort kann man dann das Ergebnis auslesen.

Die Anzahl der Ausgangsneuronen hängt von der Aufgabenstellung ab. Möchte man, dass das Netzwerk Hunde und Katzen auf dem 10x10 Pixel großen Bild unterscheidet, so wählt man zwei Ausgangsneuronen, wobei eines aktiv sein soll, wenn ein Hund auf dem Bild ist und das zweite, wenn eine Katze zu sehen ist.

Die Frage ist jetzt, woher weiß das Netz, ob ein Hund oder eine Katze auf dem Bild ist? Wo ist diese Information gespeichert? Hier kommt den Gewichten zwischen den einzelnen Neuronen die entscheidende Rolle zu. Diese bestimmen, wieviel Informationen wohin weitergegeben werden und was letztlich nach vielen Rechenschritten an den Ausgangsneuronen ausgegeben wird. Die Gewichte enthalten quasi die Koeffizienten (oder Parameter) des Algorithmus, während der Algorithmus durch die Art der Vernetzung der Neuronen und deren Aktivierungsfunktion festgelegt ist. Die spannende Frage ist: wie werden die Gewichte ermittelt?

Genau dies geschieht im sogenannten Training, welches im Folgenden vorgestellt wird. Gehen wir zunächst einmal davon aus, dass alle Gewichte zufällig initialisiert wurden. Das bedeutet auch, das bei einem Eingangsbild auch ein zuerst zufälliges Ergebnis an den Ausgangsneuronen ankommt. In unserem Beispiel von Hund und Katze zeigen die Ausgangsneuronen also einen beliebigen Wert zwischen 0 und 1 an.

Jetzt benötigen wir die gelabelten Trainingsdaten, die wir uns im ersten Teil des Make-Artikels "KI für den ESP32" erzeugt haben. Wenn ich ein solches bekanntes Bild berechnen lasse, dann weiß ich, welches der korrekte Ausgangswert sein soll. Wenn man den errechneten Wert (zunächst erstmal zufällig) mit dem korrekten Ausgangswert vergleiche, kann ich die Abweichung beziehungsweise den Fehler berechnen. Anhand der Abweichungen kann man zunächst die Gewichte zwischen der letzten internen Schicht und den Ausgangsneuronen so anpassen, dass dieser Fehler kleiner wird.

Man kann auch die Fehler der letzten internen Neuronen berechnen und auf diesem Weg die Gewichte zur vorletzten Schicht anpassen und deren Fehler berechnen usw. Dies wiederholt man solange, bis man an den Eingangsneuron angekommen ist. Man durchläuft also das neuronale Netz entgegen der ursprünglichen Berechnung und korrigiert die Verbindungsgewichte. Man spricht hier von Backpropagation.

Wenn man diesen Ablauf nun auf mehrere Bilder immer wieder iterativ anwendet, so werden die Verbindungsgewichte zwischen den Neuronen immer weiter so angepasst, dass der Gesamtfehler (gemittelt über alle Bilder) immer kleiner wird. Prinzipiell kann der Fehler aber auch größer werden.

Für die Fehlerberechnung benutzt man bei neuronalen Netzen eine sogenannte Verlustfunktion (engl. loss), für die verschiedene Verfahren zur Verfügung stehen, beispielsweise so kryptisch klingende wie Cross Entropy, Mean Squared Error und Poisson. Das muss man zum Glück nicht alles verstehen, aber gehört haben sollte man davon. Bei allen Verfahren geht es darum, sich mit der jeweiligen Verlustfunktion und den ermittelten Fehlerwerten einem Minimum zu nähern.

Bild3: Das Minimum findet man über die erste Ableitung (Steigung) der Fehlerfunktion. Ist die Ableitung negativ, steht man noch vor dem Optimum, ist sie positiv, ist man zu weit gegangen.

Bildlich kann man sich das ungefähr so vorstellen, als wenn man in einem Gebirge steht, der Fehler quasi den Höhenmetern entspricht und man an den tiefsten Punkt kommen möchte. Als Testfall könnte man eine Murmel nehmen und sehen, in welche Richtung sie am jeweiligen Standort rollt, dort geht es runter. Dann macht einen Schritt in diese Richtung und überprüft die Richtung erneut. So kommt man immer tiefer, bis man an einer Talsohle (= Minimum) angekommen ist (Bild 3). Dann hat man das Ziel erreicht. Die Testmurmeln sind in unserem Fall die Bilder und der Schritt entspricht dem Anpassen der Gewichte, so dass der Gesamtfehler eben kleiner wird.

Dieser Vergleich zeigt auch eines der Probleme mit diesem Vorgehen und mit dem Ergebnis eines Trainings grundsätzlich: man weiß eigentlich nie, in welcher Talsohle man genau angekommen ist und ob dies auch die tiefste Talsohle ist oder es nicht einen Bergrücken weiter ein noch tieferes Tal (= andere Verteilung der Gewichte) gibt, die noch bessere Gesamtergebnisse liefert.

Das Murmelspielen, Steuern der Schrittweite (Lernrate) und Auswerten der Fehler übernimmt ein Optimierungsalgorithmus. Sie können es sich denken, auch hier gibt es wieder mehrere Ansätze und die haben verschiedene Strategien um einerseits schnell, andererseits aber auch nicht in lokalen Minima zu landen.

Für Bilderkennungsaufgaben gibt es noch eine besondere Art von neuronalen Netzen: Convolutionale Neuronale Netze (CNN). Diese Art hat eine spezielle Struktur, die sich als besonders vorteilhaft bei der Verarbeitung von Bilddaten erwiesen hat. Konvolutionale Operationen oder auch Faltung genannt sind eng mit der klassischen Bildverarbeitung verbunden und daher gedanklich auch recht gut zugänglich. Wer in einem Bildbearbeitungsprogramm schon mal einen Weichzeichner oder auch einen Kantenfindungsalgorithmus angewendet hat, verwendet genau solche Operationen.

Faltung (Convolution) hebt lokale Eigenschaften eines Bildbereichs und seiner Umgebung hervor, so dass sich Muster (z.B. eine Kante) leichter erkennen und durch neuronalen Netze besser verarbeiten lassen. Beispielsweise kann man auf diese Weise aus dem Bild einer 7-Segment LCD-Anzeige 7 Kanten herausfiltern.

Bild 4, Faltung: Auf Ausschnitte eines Bildes wird ein Faltungsoperator angewendet. In der Folge entsteht ein neues Bild mit einer Kante, einem Feature.

Bildlich gesprochen entspricht die Faltung bei der Bildverarbeitung der Multiplikation eines Bildausschnitts mit einer Matrix. Die Matrix schiebt man man zeilen- und spaltenweise über das Bild und multipliziert und summiert die Pixel. Die Matrix wird auch Faltungsoperator oder Filterkern (engl. Kernel) genannt (Bild 4).

Im Beispiel in Abbildung 5 entspricht dieser einer 3x3-Matrix. Das Ergebnis kann man ebenfalls als Bild darstellen. Wenn man sich den Operator genauer ansieht, so erkennt man, dass der Mittelwert über alle Einträge eine 0 ergibt. Das bedeutet, dass er in einem homogenen Bildbereich eine 0 als Ergebnis liefert.

Läuft der Operator jedoch über eine Kante im Bild, so gibt sich dort ein von 0 verschiedener Wert. Man erkennt im Ergebnis sehr deutlich die Kante vom schwarzen in den weißen Bildbereich, alle anderen Bereiche liefern eine 0. Dieser Filter erkennt also Kanten im Bild. Je nach Koeffizienten (im Filterkernel) lassen sich bestimmte Eigenschaften oder auch Features im Bild hervorheben. Daher wird das Ergebnis auch als Feature Map bezeichnet.

Wenn man sich vorstellt, dass eine Vielzahl solcher Filter zum Beispiel horizontale und vertikale Linien oder ähnliches finden, dann können die nachgelagerten Schichten diese Informationen zu Bildinformationen kombinieren (Bild 5) und damit komplexere Strukturen erkennen – zum Beispiel ergeben zwei horizontale und zwei vertikale Linien an einer bestimmten Position ein Viereck, eine horizontale und zwei schräge Linien wiederum entsprechen einem Dreieck und runde Linien wiederum einem Kreis. Eine Bildvorverarbeitung mit solchen Filtern hat sich als sehr vorteilhaft für die Bilderkennung mit neuronalen Netzen herausgestellt und ist ein Standardaufbau in bildverarbeiteten neuronalen Netzen.

Bild 5: Durch Feature Extraction entstehen Feature Maps, mit denen ein neuronales Netz effizienter arbeiten kann.

In Kombination mit der Faltung wird in der Regel auch ein sogenannter Pooling Layer angewendet. Dies ist nichts anderes, als die Verkleinerung des Outputs durch das Ausblenden von Neuronen. Häufig wird MaxPooling für die Verkleinerung angewendet. Dabei wird von einer festgelegten Anzahl von Neuronen nur der Maximalwert weitergegeben. So kann man durch einen 2x2 MaxPolling Layer die Anzahl der Ausgangsneuronen für den nächsten Schritt um 75% (Bild 6) verringern. Auch hier iteriert der Operator über die Eingangsmatrix.

Bildlich kann man sich das in etwa so vorstellen, wie wenn die Position der Features nicht so wesentlich ist, wie die Tatsache, dass überhaupt eines vorhanden ist. Diese Art der Komplexitätsreduktion hat auch Vorteile für die Stabilität von neuronalen Netzen.

Bild 6: Das Pooling verdichtet quasi die Informationen, so dass man im folgenden weniger Neuronen benötigt und das Netz schneller wird.

In unserem Artikel in Make 1/22 "KI für den ESP32, Teil 2" zeigen wir, wie man die einzelnen Layer mit Tensorflow/Keras kombiniert und ein neuronales Netz zur Erkennung von Ziffern modelliert und trainiert. In dem Video dazu, erklären wir die einzelnen Jupyter-Notebooks Schritt für Schritt und visualisieren die Faltung in einer Demo. (dab)