Tag Archive for: artificial neural network

Einführung in die Welt der Autoencoder

An wen ist der Artikel gerichtet?

In diesem Artikel wollen wir uns näher mit dem neuronalen Netz namens Autoencoder beschäftigen und wollen einen Einblick in die Grundprinzipien bekommen, die wir dann mit einem vereinfachten Programmierbeispiel festigen. Kenntnisse in Python, Tensorflow und neuronalen Netzen sind dabei sehr hilfreich.

Funktionsweise des Autoencoders

Ein Autoencoder ist ein neuronales Netz, welches versucht die Eingangsinformationen zu komprimieren und mit den reduzierten Informationen im Ausgang wieder korrekt nachzubilden.

Die Komprimierung und die Rekonstruktion der Eingangsinformationen laufen im Autoencoder nacheinander ab, weshalb wir das neuronale Netz auch in zwei Abschnitten betrachten können.

 

 

 

Der Encoder

Der Encoder oder auch Kodierer hat die Aufgabe, die Dimensionen der Eingangsinformationen zu reduzieren, man spricht auch von Dimensionsreduktion. Durch diese Reduktion werden die Informationen komprimiert und es werden nur die wichtigsten bzw. der Durchschnitt der Informationen weitergeleitet. Diese Methode hat wie viele andere Arten der Komprimierung auch einen Verlust.

In einem neuronalen Netz wird dies durch versteckte Schichten realisiert. Durch die Reduzierung von Knotenpunkten in den kommenden versteckten Schichten werden die Kodierung bewerkstelligt.

Der Decoder

Nachdem das Eingangssignal kodiert ist, kommt der Decoder bzw. Dekodierer zum Einsatz. Er hat die Aufgabe mit den komprimierten Informationen die ursprünglichen Daten zu rekonstruieren. Durch Fehlerrückführung werden die Gewichte des Netzes angepasst.

Ein bisschen Mathematik

Das Hauptziel des Autoencoders ist, dass das Ausgangssignal dem Eingangssignal gleicht, was bedeutet, dass wir eine Loss Funktion haben, die L(x , y) entspricht.

L(x, \hat{x})

Unser Eingang soll mit x gekennzeichnet werden. Unsere versteckte Schicht soll h sein. Damit hat unser Encoder folgenden Zusammenhang h = f(x).

Die Rekonstruktion im Decoder kann mit r = g(h) beschrieben werden. Bei unserem einfachen Autoencoder handelt es sich um ein Feed-Forward Netz ohne rückkoppelten Anteil und wird durch Backpropagation oder zu deutsch Fehlerrückführung optimiert.

Formelzeichen Bedeutung
\mathbf{x}, \hat{\mathbf{x}} Eingangs-, Ausgangssignal
\mathbf{W}, \hat{\mathbf{W}} Gewichte für En- und Decoder
\mathbf{B}, \hat{\mathbf{B}} Bias für En- und Decoder
\sigma, \hat{\sigma} Aktivierungsfunktion für En- und Decoder
L Verlustfunktion

Unsere versteckte Schicht soll mit \latex h gekennzeichnet werden. Damit besteht der Zusammenhang:

(1)   \begin{align*} \mathbf{h} &= f(\mathbf{x}) = \sigma(\mathbf{W}\mathbf{x} + \mathbf{B}) \\ \hat{\mathbf{x}} &= g(\mathbf{h}) = \hat{\sigma}(\hat{\mathbf{W}} \mathbf{h} + \hat{\mathbf{B}}) \\ \hat{\mathbf{x}} &= \hat{\sigma} \{ \hat{\mathbf{W}} \left[\sigma ( \mathbf{W}\mathbf{x} + \mathbf{B} )\right]  + \hat{\mathbf{B}} \}\\ \end{align*}

Für eine Optimierung mit der mittleren quadratischen Abweichung (MSE) könnte die Verlustfunktion wie folgt aussehen:

(2)   \begin{align*} L(\mathbf{x}, \hat{\mathbf{x}}) &= \mathbf{MSE}(\mathbf{x}, \hat{\mathbf{x}}) = \|  \mathbf{x} - \hat{\mathbf{x}} \| ^2 &=  \| \mathbf{x} - \hat{\sigma} \{ \hat{\mathbf{W}} \left[\sigma ( \mathbf{W}\mathbf{x} + \mathbf{B} )\right]  + \hat{\mathbf{B}} \} \| ^2 \end{align*}

 

Wir haben die Theorie und Mathematik eines Autoencoder in seiner Ursprungsform kennengelernt und wollen jetzt diese in einem (sehr) einfachen Beispiel anwenden, um zu schauen, ob der Autoencoder so funktioniert wie die Theorie es besagt.

Dazu nehmen wir einen One Hot (1 aus n) kodierten Datensatz, welcher die Zahlen von 0 bis 3 entspricht.

    \begin{align*} [1, 0, 0, 0] \ \widehat{=}  \ 0 \\ [0, 1, 0, 0] \ \widehat{=}  \ 1 \\ [0, 0, 1, 0] \ \widehat{=}  \ 2 \\ [0, 0, 0, 1] \ \widehat{=} \  3\\ \end{align*}

Diesen Datensatz könnte wie folgt kodiert werden:

    \begin{align*} [1, 0, 0, 0] \ \widehat{=}  \ 0 \ \widehat{=}  \ [0, 0] \\ [0, 1, 0, 0] \ \widehat{=}  \ 1 \ \widehat{=}  \  [0, 1] \\ [0, 0, 1, 0] \ \widehat{=}  \ 2 \ \widehat{=}  \ [1, 0] \\ [0, 0, 0, 1] \ \widehat{=} \  3 \ \widehat{=}  \ [1, 1] \\ \end{align*}

Damit hätten wir eine Dimensionsreduktion von vier auf zwei Merkmalen vorgenommen und genau diesen Vorgang wollen wir bei unserem Beispiel erreichen.

Programmierung eines einfachen Autoencoders

 

Typische Einsatzgebiete des Autoencoders sind neben der Dimensionsreduktion auch Bildaufarbeitung (z.B. Komprimierung, Entrauschen), Anomalie-Erkennung, Sequenz-to-Sequenz Analysen, etc.

Ausblick

Wir haben mit einem einfachen Beispiel die Funktionsweise des Autoencoders festigen können. Im nächsten Schritt wollen wir anhand realer Datensätze tiefer in gehen. Auch soll in kommenden Artikeln Variationen vom Autoencoder in verschiedenen Einsatzgebieten gezeigt werden.

Kiano – visuelle Exploration mit Deep Learning

Kiano – eine iOS-App zur visuellen Exploration und Suche der eigenen Fotos.

Menschen haben kein Problem, komplexe Bilder zu verstehen, es fällt ihnen aber schwer, gezielt Bilder in großen Bildersammlungen (wieder) zu finden. Da die Anzahl von Bildern, insbesondere auch auf Smartphones zusehends zunimmt – mehrere tausend Bilder pro Gerät sind keine Seltenheit, wird die Suche nach bestimmten Bildern immer schwieriger. Ist bei einem gesuchten Foto dessen Aufnahmedatum unbekannt, so kann es sehr lange dauern, bis es gefunden ist. Werden dem Nutzer zu viele Bilder auf einmal präsentiert, so geht der Überblick schnell verloren. Aus diesem Grund besteht eine typische Bildsuche heutzutage meist im endlosen Scrollen über viele Bildschirmseiten mit langen Bilderlisten.

Dieser Artikel stellt das Prinzip und die Funktionsweise der neuen iOS-App “Kiano” vor, die es Nutzern ermöglicht, alle ihre Bilder explorativ mittels visuellem Browsen zu erkunden. Der Name “Kiano” steht hierbei für “Keep Images Arranged & Neatly Organized”. Mit der App ist es außerdem möglich, zu einem Beispielbild gezielt nach ähnlichen Fotos auf dem Gerät zu suchen.

Um Bilder visuell durchsuch- und sortierbar zu machen, werden sogenannte Merkmalsvektoren bzw. Featurevektoren verwendet, die Aussehen und Inhalt von Bildern kompakt repräsentieren können. Zu einem Bild lassen sich ähnliche Bilder finden, indem die Bilder bestimmt werden, deren Featurevektoren eine geringe Distanz zum Featurevektor des Suchbildes haben.

Werden Bilder zweidimensional so angeordnet, dass die Featurevektoren benachbarter Bilder sehr ähnlich sind, so erhält man eine visuell sortierte Bilderlandkarte. Bei einer visuell sortierten Anordnung der Bilder fällt es Menschen deutlich leichter, mehr Bilder gleichzeitig zu erfassen, als dies im unsortierten Fall möglich wäre. Durch die graduelle Veränderung der Bildinhalte wird es möglich, über diese Karte visuell zu navigieren.

Generierung von Featurevektoren zur Bildbeschreibung

Convolutional Neural Networks (CNNs) sind nicht nur in der Lage, Bilder mit hoher Genauigkeit zu klassifizieren, d.h. zu erkennen, welches Objekt – entsprechend einer Menge von gelernten Objektkategorien auf einem Bild zu sehen ist, die Aktivierungen der Netzwerkschichten lassen sich auch als universelle Featurevektoren zur Bildbeschreibung nutzen. Während die vorderen Netzwerkschichten von CNNs einfache visuelle Bildmerkmale wie Farben und einfache Muster detektieren, repräsentieren die Ausgangsschichten des Netzwerks die semantischen Informationen bezüglich der gelernten Objektkategorien. Die Zwischenschichten des Netzwerks sind weniger von den Objektkategorien abhängig und können somit als generelle abstrakte Repräsentationen des Inhalts der Bilder angesehen werden. Hierbei ist es möglich, bereits fertig trainierte Klassifikationsnetzwerke für die Featureextraktion wiederzuverwenden. In der Visual Computing Gruppe der HTW Berlin wurden umfangreiche Evaluierungen durchgeführt, um zu bestimmen, welche Netzwerkschichten von welchen CNNs mit welchen zusätzlichen Transformationen zu verwenden sind, um aus Netzwerkaktivierungen Feature-Vektoren zu erzeugen, die sehr gut für die Suche nach beliebigen Bildern geeignet sind.

Beste Ergebnisse hinsichtlich der Suchgenauigkeit (der Mean Average Precision) wurden mit einem Deep Residual Learning Network (ResNet-200) erzielt. Die 2048 Aktivierungen vor dem vollvernetzten letzten Layer werden als initiale Featurevektoren verwendet, wobei sich die Suchgenauigkeit durch eine L1-Normierung, gefolgt von einer PCA-Transformation (Principal Component Analysis) sogar noch verbessern lässt. Hierdurch ist es möglich, die Featurevektoren auf eine Größe von nur 64 Bytes zu reduzieren. Leider ist die rechnerische Komplexität der Bestimmung dieser hochwertigen Featurevektoren zu groß, um sie auf mobilen Geräten verwenden zu können. Eine gute Alternative stellen die Mobilenets dar, die sich durch eine erheblich reduzierte Komplexität auszeichnen. Als Kompromiss zwischen Klassifikationsgenauigkeit und Komplexität wurde für die Kiano-App das Mobilenet_v2_0.5_128 verwendet. Die mit diesem Netzwerk bestimmten Featurevektoren wurden ebenfalls auf eine Größe von 64 Bytes reduziert.

Die aus CNNs erzeugten Featurevektoren sind gut für die Suche nach Bildern mit ähnlichem Inhalt geeignet. Für die Suche nach Bilder, mit ähnlichen visuellen Eigenschaften (z.B. die auftretenden Farben oder deren örtlichen Verteilung) sind diese Featurevektoren nur bedingt geeignet. Hierfür eignen sich klassische sogenannte “Low-Level”-Featurevektoren besser. Da für eine ansprechende und leicht erfassbare Bildsortierung auch eine Übereinstimmung dieser visuellen Bildattribute wichtig ist, kommt bei Kiano ein weiterer Featurevektor zum Einsatz, mit dem sich diese “primitiven” visuellen Bildattribute beschreiben lassen. Dieser Featurevektor hat eine Größe von 50 Bytes. Bei Kiano kann der Nutzer in den Einstellungen wählen, ob bei der visuellen Sortierung und Bildsuche größerer Wert auf den Bildinhalt oder die visuelle Erscheinung eines Bildes gelegt werden soll.

Visuelle Bildsortierung

Werden Bilder entsprechend ihrer Ähnlichkeiten sortiert angeordnet, so können mehrere hundert Bilder gleichzeitig wahrgenommen bzw. erfasst werden. Dies hilft, Regionen interessanter Bildern leichter zu erkennen und gesuchte Bilder schneller zu entdecken. Die Möglichkeit, viele Bilder gleichzeitig präsentieren zu können, ist neben Bildverwaltungssystemen besonders auch für E-Commerce-Anwendungen interessant.

Herkömmliche Dimensionsreduktionsverfahren, die hochdimensionale Featurevektoren auf zwei Dimensionen projizieren, sind für die Bildsortierung ungeeignet, da sie die Bilder so anordnen, dass Lücken und Bildüberlappungen entstehen. Sollen Bilder sortiert auf einem dichten regelmäßigen 2D-Raster angeordnet werden, kommen als Verfahren nur selbstorganisierende Karten oder selbstsortierende Karten in Frage.

Eine selbstorganisierende Karte (Self Organizing Map / SOM) ist ein künstliches neuronales Netzwerk, das durch unbeaufsichtigtes Lernen trainiert wird, um eine niedrigdimensionale, diskrete Darstellung der Daten des Eingangsraums als sogenannte Karte (Map) zu erzeugen. Im Gegensatz zu anderen künstlichen neuronalen Netzen, werden SOMs nicht durch Fehlerkorrektur, sondern durch ein Wettbewerbsverfahren trainiert, wobei eine Nachbarschaftsfunktion verwendet wird, um die lokalen Ähnlichkeiten der Eingangsdaten zu bewahren.

Eine selbstorganisierende Karte besteht aus Knoten, denen einerseits ein Gewichtsvektor der gleichen Dimensionalität wie die Eingangsdaten und anderseits eine Position auf der 2D-Karte zugeordnet sind. Die SOM-Knoten sind als zweidimensionales Rechteckgitter angeordnet. Das vom der SOM erzeugte Mapping ist diskret, da jeder Eingangsvektor einem bestimmten Knoten zugeordnet wird. Zu Beginn werden die Gewichtsvektoren aller Knoten mit Zufallswerten initialisiert. Wird ein hochdimensionaler Eingangsvektor in das Netz eingespeist, so wird dessen euklidischer Abstand zu allen Gewichtsvektoren berechnet. Der Knoten, dessen Gewichtsvektor dem Eingangsvektor am ähnlichsten ist, wird als Best Matching Unit (BMU) bezeichnet. Die Gewichte des BMU und seiner auf der Karte örtlich benachbarten Knoten werden an den Eingangsvektor angepasst. Dieser Vorgang wird iterativ wiederholt. Das Ausmaß dieser Anpassung nimmt im Laufe der Iterationen und der örtlichen Entfernung zum BMU-Knoten ab.

Um SOMs an die Bildsortierung anzupassen, sind zwei Modifikationen notwendig. Jeder Knoten darf nicht von mehr als einem Featurevektor (der ein Bild repräsentiert) ausgewählt werden. Eine Mehrfachauswahl würde zu einer Überlappung der Bilder führen. Aus diesem Grund muss die Anzahl der SOM-Knoten mindestens so groß wie die Anzahl der Bilder sein. Eine sinnvolle Erweiterung einer SOM verwendet ein Gitter, bei dem gegenüberliegende Kanten verbunden sind. Werden diese Torus-förmigen Karten für große SOMs verwendet, kann der Eindruck einer endlosen Karte erzeugt werden, wie es in Kiano umgesetzt ist. Ein Problem der SOMs ist ihre hohe rechnerische Komplexität, die quadratisch mit der Anzahl der zu sortierenden Bilder wächst, wodurch die maximale Anzahl an zu sortierenden Bildern beschränkt wird. Eine Lösung stellt eine selbstsortierende Karte (Self Sorting Map / SSM) dar, deren Komplexität nur n log(n) beträgt.

Selbstsortierende Karten beginnen mit einer zufälligen Positionierung der Bilder auf der Karte. Diese Karte wird dann in 4×4-Blöcke aufgeteilt und für jeden Block wird der Mittelwert der zugehörigen Featurevektoren bestimmt. Als nächstes werden aus 2×2 benachbarten Blöcken jeweils vier korrespondierende Bild-Featurevektoren untersucht und ihre zugehörigen Bilder gegebenenfalls getauscht. Aus den 4! = 24 Anordnungsmöglichkeiten wird diejenige gewählt, die die Summe der quadrierten Differenzen zwischen den jeweiligen Featurevektoren und den Featuremittelwerten der Blöcke minimiert. Nach mehreren Iterationen wird jeder Block in vier kleinere Blöcke halber Breite und Höhe aufgeteilt und wiederum in der beschriebenen Weise überprüft, wie die Bildpositionen dieser kleineren Blöcke getauscht werden sollten. Dieser Vorgang wird solange wiederholt, bis die Blockgröße auf 1×1 Bild reduziert ist.

In der Visual-Computing Gruppe der HTW Berlin wurde untersucht, wie die Sortierqualität des SSM-Algorithmus verbessert werden kann. Anstatt die Mittelwerte der Featurevektoren als konstanten Durchschnittsvektor für den gesamten Block zu berechnen, verwenden wir gleitende Tiefpassfilter, die sich effizient mittels Integralbildern berechnen lassen. Hierdurch entstehen weichere Übergänge auf der sortierten Bilderkarte. Weiterhin wird die Blockgröße nicht für mehrere Iterationen konstant gehalten, sondern kontinuierlich zusammen mit dem Radius des Filterkernels reduziert. Durch die Verwendung von optimierten Algorithmen von “Linear Assignment” Algorithmen wird es weiterhin möglich, den optimalen Positionstausch nicht nur für jeweils vier Featurevektoren bzw. Bildern sondern für eine deutlich größere Anzahl zu überprüfen. All diese Maßnahmen führen zu einer deutlich verbesserten Sortierungsqualität bei gleicher Komplexität.

Effiziente Umsetzung für iOS

Wie so oft, liegen die softwaretechnischen Herausforderungen an ganz anderen Stellen, als man zunächst vermutet. Für eine effiziente Implementierung der zuvor beschriebenen Algorithmen, insbesondere der SSM, stellte es sich heraus, dass die Programmiersprache Swift, in der iOS Apps normaler Weise entwickelt werden, erheblich mehr Rechenzeit benötigt, als eine Umsetzung in der Sprache C. Im Zuge der stetigen Weiterentwicklung von Swift und dessen Compiler mag sich die Lücke zu C zwar immer weiter schließen, zum Zeitpunkt der Umsetzung war die Implementierung in C aber um einen Faktor vier schneller als in Swift. Hierbei liegt die Vermutung nahe, dass der Zugriff auf und das Umsortieren von Featurevektoren als native C-Arrays deutlich effektiver passiert, als bei der Verwendung von Swift-Arrays. Da Swift-Arrays Value-Type sind, kommt es in Swift vermutlich zu unnötigen Kopieroperationen der Fließkommazahlen in den einzelnen Featurevektoren.

Die Berechnung des Mobilenet-Anteils der Featurevektoren konnte sehr komfortabel mit Apples CoreML Machine Learning Framework umgesetzt werden. Hierbei ist zu beachten, dass es sich wie oben beschrieben, nicht um eine Klassifikation handelt, sondern um das Abgreifen der Aktivierungen einer tieferen Schicht. Für Klassifikationen findet man praktisch sofort nutzbare Beispiele, für den Zugriff auf die Aktivierungen waren jedoch Anpassungen notwendig, die bei der Portierung eines vortrainierten Mobilenet nach CoreML vorgenommen wurden. Das stellte sich als erheblich einfacher heraus, als der Versuch, auf die tieferen Schichten eines Klassifizierungsnetzes in CoreML zuzugreifen.

Für die Verwaltung der Bilder, ihrer Featurevektoren und ihrer Position in der sortieren Karte wird in Kiano eine eigene Datenstruktur verwendet, die es zu persistieren gilt. Es ist dem Nutzer ja nicht zuzumuten, bei jedem Start der App auf die Berechnung aller Featurevektoren zu warten. Die Strategie ist es hierbei, bereits bekannte Bilder zu identifizieren und deren Features nur dann neu zu berechnen, falls sich das Bild verändert hat. Die über Appels Photos Framework zur Verfügung gestellten local Identifier identifizieren dabei die Bilder. Veränderungen werden über das Modifikationsdatum eines Bildes detektiert. Die größte Herausforderung ist hierbei das Zeichnen der Karte. Die Benutzerinteraktion soll schnell und flüssig erscheinen, auf Animationen wie das Nachlaufen der Karte beim Verschieben möchte man nicht verzichten. Die Umsetzung geschieht hierbei nicht in OpenGL ES, welches ab iOS 12 ohnehin als deprecated bezeichnet wird. Auf der anderen Seite wird aber auch nicht der „Standardweg“ des Überschreibens der draw-Methode einer Ableitung von UIView gewählt. Letztes führt bekanntlich zu Performanceeinbußen. Insbesondere deshalb, weil das System sehr oft Backing-Images der Ansichten erstellt. Um die Kontrolle über das Neuzeichnen zu behalten, wird in Kiano ein eigenes Backing-Image implementiert, das auf Ebene des Core Animation Frameworks dem View als Layer zugweisen wird. Diesem Layer kann dann sehr komfortabel eine 3D-Transformation zugewiesen werden und man profitiert von der GPU-Beschleunigung, ohne OpenGL ES direkt verwenden zu müssen.

 

Trotz der Verwendung eines Core Animation Layers ist das Zeichnen der Karte immer noch sehr zeitaufwendig. Das liegt an der Tatsache, dass je nach Zoomstufe tausende von Bildern darzustellen sind, die alle über das Photos Framework angefordert werden müssen. Das Nadelöhr ist dann weniger das Zeichnen, als die Zeit, die vergeht, bis einem das Bild zur Verfügung gestellt wird. Diese Vorgänge sind praktisch alle nebenläufig. Zur Erinnerung: Ein Foto kann in der iCloud liegen und zum Zeitpunkt der Anfrage noch gar nicht (oder noch nicht in geeigneter Auflösung) heruntergeladen sein. Netzwerkbedingt gibt es keine Vorhersage, wann oder ob überhaupt das Bild zur Verfügung gestellt wird. In Kiano werden zum einen Bilder in sehr kleiner Auflösung gecached, zum anderen wird beim Navigieren auf der Karte im Hintergrund ein neues Kartenteil als Backing-Image vorbereitet, das dem Nutzer nach Fertigstellung angezeigt wird. Die vorberechneten Kartenteile sind dabei drei Mal so breit und drei Mal so hoch wie das Display, so dass man diese „Hintergrundaktivität“ beim Verschieben der Karte in der Regel nicht bemerkt. Nur wenn die Bewegung zu schnell wird oder die Bilder zu langsam „geliefert“ werden, erkennt man schwarze Flächen, die sich dann verzögert mit Bildern füllen.

Vergleichbares passiert beim Hineinzoomen in die Karte. Der Nutzer sieht zunächst eine vergrößerte und damit unscharfe Version des aktuellen Kartenteils, während im Hintergrund ein Kartenteil in höherer Auflösung und mit weniger Bildern vorbereitet wird. In der Summe geht Kiano hier einen Kompromiss ein. Die Pixeldichte der Geräte würde eine schärfere Darstellung der Bilder auf der Karte erlauben. Allerdings müssten dann die Bilder in so höher Auflösung angefordert werden, dass eine flüssige Kartennavigation nicht mehr möglich wäre. So sieht der Nutzer in der Regel eine Karte mit Bildern in halber Auflösung gemessen an den physikalischen Pixeln seines Displays.

Ein anfangs unterschätzter Arbeitsaufwand bei der Umsetzung von Kiano liegt darin begründet, dass sich die Photo Library des Nutzers jederzeit während der Benutzung der App verändern kann. Bilder können durch Synchronisationen mit der iCloud oder mit iTunes verschwinden, sich in andere Alben bewegen, oder neue können auftauchen. Der Nutzer kann Bildschirmfotos machen. Das Photos Framework stellt komfortable Benachrichtigungen für solche Events zur Verfügung. Der Implementierung obliegt es dabei aber herauszubekommen, ob die Karte neu zu sortieren ist oder nicht, ob das gerade anzeigte Bild überhaupt noch existiert und was zu tun ist, wenn es verschwunden ist.

Zusammenfassend kann man feststellen, dass natürlich die Umsetzung der Algorithmen und die Darstellung dessen auf einer Karte zu den spannendsten Teilen der Arbeiten an Kiano zählen, dass aber der Umgang mit einer sich dynamisch ändernden Datenbasis nicht unterschätzt werden sollte.

Autoren

Prof. Dr. Klaus JungProf. Dr. Klaus Jung studierte Physik an der TU Berlin, wo er im Bereich der Mathematischen Physik promovierte. Bis 2008 arbeitete er als Leiter F&E bei der Firma LuraTech im Bereich der Dokumentenverarbeitung und Langzeitarchivierung. In der JPEG-Gruppe leitete er die deutsche Delegation bei der Standardisierung von JPEG2000. Seit 2008 ist er Professor für Medieninformatik an der HTW Berlin mit dem Schwerpunkt „Visual Computing“.

Prof. Dr. Kai Uwe Barthel

Prof. Dr. Kai Uwe Barthel studierte Elektrotechnik an der TU Berlin, bevor er Assistent am Institut für Nachrichtentechnik wurde und im Bereich Bildkompression promovierte. Seit 2001 ist er Professor der HTW Berlin. Hauptforschungsbereiche sind visuelle Bildsuche und automatisches Bildverstehen. 2009 gründete er die pixolution GmbH www.pixolution.de, ein Unternehmen, das Technologien für die visuelle Bildsuche anbietet.

Funktionsweise künstlicher neuronaler Netze

Künstliche neuronale Netze sind ein Spezialbereich des maschinellen Lernens, der sogar einen eigenen Trendbegriff hat: Deep Learning.
Doch wie funktioniert ein künstliches neuronales Netz überhaupt? Und wie wird es in Python realisiert? Dies ist Artikel 2 von 6 der Artikelserie –Einstieg in Deep Learning.

Gleich vorweg, wir beschränken uns hier auf die künstlichen neuronalen Netze des überwachten maschinellen Lernens. Dafür ist es wichtig, dass das Prinzip des Trainings und Testens von überwachten Verfahren verstanden ist. Künstliche neuronale Netze können aber auch zur unüberwachten Dimensionsreduktion und zum Clustering eingesetzt werden. Das bekannteste Verfahren ist das AE-Net (Auto Encoder Network), das hier aus der Betrachtung herausgenommen wird.

Beginnen wir mit einfach künstlichen neuronalen Netzen, die alle auf dem Perzeptron als Kernidee beruhen. Das Vorbild für künstliche neuronale Netze sind natürliche neuronale Netze, wie Sie im menschlichen Gehirn zu finden sind.

Perzeptron

Das Perzeptron (engl. Perceptron) ist ein „Klassiker“ unter den künstlichen neuronalen Netzen. Wenn von einem neuronalen Netz gesprochen wird, ist meistens ein Perzeptron oder eine Variation davon gemeint. Perzeptrons sind mehrschichtige Netze ohne Rückkopplung, mit festen Eingabe- und Ausgabeschichten. Es gibt keine absolut einheitliche Definition eines Perzeptrons, in der Regel ist es jedoch ein reines FeedForward-Netz mit einer Input-Schicht (auch Abtast-Schicht oder Retina genannt) mit statisch oder dynamisch gewichteten Verbindungen zur Ausgabe-Schicht, die (als Single-Layer-Perceptron) aus einem einzigen Neuron besteht. Das eine Neuron setzt sich aus zwei mathematischen Funktionen zusammen: Einer Berechnung der Nettoeingabe und einer Aktivierungsfunktion, die darüber entscheidet, ob die berechnete Nettoeingabe im Brutto nun “feuert” oder nicht. Es ist in seiner Ausgabe folglich binär: Man kann es sich auch als kleines Lämpchen vorstellen, so dass abhängig von den Eingabewerten und den Gewichtungen eine Nettoeingabe (Summe) bildet und eine Sprungfunktion darüber entscheidet, ob am Ende das Lämpchen leuchtet oder nicht. Dieses Konzept der Ausgabeerzeugung wird Forward-Propagation genannt.

Single-Layer-Perceptron

Auch wenn “Netz” für ein einzelnes Perzeptron mit seinem einen Neuron etwas übertrieben wirken mag, ist es doch die Grundlage für viele größere und mehrschichtige Netze.

Betrachten wir nun die Mathematik der Forward-Propagation.

Wir haben eine Menge an Eingabewerten x_0, x_1 \dots x_n. Wobei für x_0 als Bias-Input stets gilt: x_0 = 1,0. Der Bias-Input ist nur ein Platzhalter für das wichtige Bias-Gewicht.

    \[ x = \begin{bmatrix} x_0\\ x_1\\ x_2\\ x_3\\ \vdots\\ x_n \end{bmatrix} \]


Für jede Eingabevariable wird eine Gewichtsvariable benötigt: w_0, w_1 \dots w_n

    \[ w = \begin{bmatrix} w_0\\ w_1\\ w_2\\ w_3\\ \vdots\\ w_n \end{bmatrix} \]

Jedes Produkt aus Eingabewert und Gewichtung soll in Summe die Nettoeingabe z bilden. Hier zeigt sich z als lineare mathematische Funktion, die zwei-dimensional leicht als z = w_0 + w_1 \cdot x_1 mit w_0 als Y-Achsenschnitt wenn x_1 = 0.

    \[ z = w_0 \cdot x_0 + w_1 \cdot x_1 + \dots + w_n \cdot x_n \]

Die lineare Funktion wird nur durch die Sprungfunktion als sogenannte Aktivierungsfunktion zu einer binären Klasseneinteilung (siehe hierzu: Machine Learning – Regression vs Klassifikation), denn wenn z einen festzulegenden Schwellwert \theta überschreitet, liefert die Sprungfunktion \phi mit der Eingabe z einen anderen Wert als wenn dieser Schwellwert nicht überschritten wird.

(1)   \begin{equation*} \phi(z) = \begin{cases} 1 & \text{wenn } z \le \theta \\ -1 & \text{wenn } z < \theta \\ \end{cases} \end{equation*}

Die Definition dieser Aktivierungsfunktion ist der Kern der Klassifikation und viele erweiterte künstliche neuronale Netze unterscheiden sich im Wesentlichen vom Perzeptron dadurch, dass die Aktivierungsfunktion komplexer ist, als eine reine Sprungfunktion, beispielsweise als Sigmoid-Funktion (basierend auf der logistischen Funktion) oder die Tangens hyperbolicus (tanh) -Funktion. Mehr darüber dann im nächsten Artikel dieser Artikelserie, bleiben wir also bei der einfachen Sprungfunktion.

Künstliche neuronale Netze sind im Grunde nichts anderes als viel-dimensionale, mathematische Funktionen, die durch Schaltung als Neuronen nebeneinander (Neuronen einer Schicht) und hintereinander (mehrere Schichten) eine enorme Komplexität erfassen können. Die Gewichtungen sind dabei die Stellschraube, die die Form der mathematischen Funktion gestaltet, aus Geraden und Kurven, um eine Punktwolke zu beschreiben (Regression) oder um Klassengrenzen zu identifizieren (Klassifikation).

Eine andere Sichtweise auf künstliche neuronale ist die des Filters: Ein künstliches neuronales Netz nimmt alle Eingabe-Variablen entgegen (z. B. alle Pixel eines Bildes) und über ein Training werden die Gewichtungen (die Form des Filters) so gestaltet, dass der Filter immer zu richtigen Klasse (im Kontext der Bildklassifikation: die Objektklasse) führt.


Kommen wir nochmal kurz zurück zu der Berechnung der Nettoeingabe z. Da diese Schreibweise…

    \[ z = w_0 \cdot x_0 + w_1 \cdot x_1 + \dots + w_n \cdot x_n \]

… recht anstrengend ist, schreiben Fortgeschrittene der linearen Algebra lieber z = w^T \cdot x.

    \[ z = w^T \cdot x \]

Das hochgestellte T steht dabei für transponieren. Transponieren bedeutet, dass Spalten zu Zeilen werden – oder umgekehrt.

Beispielsweise befüllen wir zwei Vektoren x und w mit beispielhaften Inhalten:

Eingabewerte:

    \[ x = \begin{bmatrix} 5\\ 12\\ 30\\ 2 \end{bmatrix} \]

Gewichtungen:

    \[ w = \begin{bmatrix} 1\\ 2\\ 5\\ 12 \end{bmatrix} \]

Kann nun die Nettoeingabe z berechnet werden, denn der Gewichtungsvektor wird vom Spaltenvektor zum Zeilenvektor. So kann – mathematisch korrekt dargestellt – jedes Element des einen Vektors mit dem zugehörigen Element des anderen Vektors multipliziert werden, die dabei entstehenden Ergebniswerte werden summiert.

    \[ z = w^T \cdot x = \big[1\text{ }2\text{ }5\text{ }12\big] \cdot \begin{bmatrix} 5\\ 12\\ 30\\ 2 \end{bmatrix} = 1 \cdot 5 + 2 \cdot 12 + 5 \cdot 30 + 12 \cdot 2 = 203 \]


Zurück zur eigentlichen Aufgabe des künstlichen neuronalen Netzes: Klassifikation! (Regression, Clustering und Dimensionsreduktion blenden wir ja in diesem Artikel als Aufgabe aus 🙂

Das Perzeptron soll zwei Klassen trennen. Dafür sollen alle Eingaben richtig gewichtet werden, so dass die entstehende Nettoeingabe z die Sprungfunktion dann aktiviert, wenn der Datensatz nicht für die eine, sondern für die andere Klasse ausweist.

Da wir es mit einer linearen Funktion z zutun haben, ist die Konvergenz (= Passgenauigkeit des Models mit der Realität) eines Single-Layer-Perzeptrons nur für lineare Trennbarkeit möglich!

Training des Perzeptron-Netzes

Die Aufgabe ist nun, die richtigen Gewichte zu finden – und nicht nur irgendwelche richtigen, sondern genau die optimalen. Die Frage, die sich für jedes künstliche neuronale Netz stellt, ist die nach den richtigen Gewichtungen. Das Training eines Perzeptron ist vergleichsweise einfach, gerade weil es binär ist. Denn binär bedeutet auch, dass wenn eine falsche Antwort gegeben wurde, muss das jeweils andere mögliche Ergebnis korrekt sein.

Das Training eines Perzeptrons funktioniert wie folgt:

  1. Setze alle Gewichtungen auf den Wert 0,00
  2. Mit jedem Datensatz des Trainings
    1. Berechne den Ausgabewert \^{y}
    2. Vergleiche den Ausgabewert \^{y} mit dem tatsächlichen Ergebnis y
    3. Aktualisiere die Gewichtungen entgegen des Fehlers: w_i = w_i + \Delta w_i

Wobei die Gewichtsanpassung \Delta w_i entgegen des Fehlers (bzw. hin zur jeweils anderen möglichen Antwort) geschieht:

\Delta w_i = (\^{y}_j - y_j ) \cdot x_i

Anmerkung für die Experten: Die Schrittweite \eta blenden wir hier einfach mal aus. Bitte einfach von \eta = 1.0 ausgehen.

\Delta w_i ist die Differenz aus der Prädiktion und dem tatsächlichen Ergebnis (Klasse). Alle Gewichtungen werden mit jedem Fehler gleichzeitig aktualisiert. Sind alle Gewichtungen aktualisiert, kommt der nächste Durchlauf (erneuter Vergleich zwischen \^{y} und y), nicht zu vergessen ist dabei natürlich die Abhängigkeit von den Eingabewerten x:

\Delta w_0 = (\^{y}_j - y_j ) \cdot x_0

\Delta w_2 = (\^{y}_j - y_j ) \cdot x_1

\Delta w_2 = (\^{y}_j - y_j) \cdot x_2

\Delta w_n = (\^{y}_j - y_j) \cdot x_n

Training eines Perzeptrons

Das Training im überwachten Lernen basiert immer auf der Idee, den Ausgabe-Fehler (die Differenz zwischen Prädiktion und tatsächlich korrektem Ergebnis) zu betrachten und die Klassifikationslogik an den richtigen Stellschrauben (bei neuronalen Netzen sind das die Gewichtungen) entgegen des Fehlers anzupassen.

Richtige Klassifikations-Situationen können True-Positives und True-Negatives darstellen, die zu keiner Gewichtsanpassung führen sollen:

True-Positive -> Klassifikation: 1 | korrekte Klasse: 1

\Delta w_i = (\^{y}_j - y_j) \cdot x_i = (1 - 1) \cdot x_i = 0

True-Negative-> Klassifikation: -1 | korrekte Klasse: -1

\Delta w_i = (\^{y}_j - y_j) \cdot x_i = (-1 - -1) \cdot x_i = 0

Falsche Klassifikationen erzeugen einen Fehler, der zu einer Gewichtsanpassung entgegen des Fehlers führen soll:

False-Positive -> Klassifikation: 1 | korrekte Klasse: -1

\Delta w_i = (\^{y}_j - y_j) \cdot x_i = (1 - -1) \cdot x_i = 2 \cdot x_i

False-Negative -> Klassifikation: -1 | korrekte Klasse: 1

\Delta w_i = (\^{y}_j - y_j) \cdot x_i = (-1 - 1) \cdot x_i = -2 \cdot x_i

Imaginäres Trainingsbeispiel eines Single-Layer-Perzeptrons (SLP)

Nehmen wir an, dass x_1 = 0,5 ist und das SLP irrtümlicherweise die Klasse \^{y_1} = -1 ausgewiesen hat, obwohl die korrekte Klasse y_1 = +1 wäre. (Und die Schrittweite lassen wir bei \eta = 1,0)

Dann passiert folgendes:

\Delta w_1 = (\^{y}_1 - y_1) \cdot x_1 = (-1 - 1) \cdot 0,5 = -2,0 \cdot 0,5 = -1,0

Die Gewichtung w_1 verringert sich entsprechend w_1 = w_1 + \Delta w_1 = w_1 - 1,0 und somit wird die Wahrscheinlichkeit größer, dass wenn bei der nächsten Iteration (j=1) wieder die Klasse +1 korrekt sei,  den Schwellwert \phi(z) zu unterschreiten und auf eben diese korrekte Klasse zu stoßen.

Die Aktualisierung der Gewichtung \Delta w_i ist proportional zu x_i. So würde beispielsweise ein neues x_1=2,0 (bei Iteration j=2) zu einer irrtümlichen Klassifikation \^(y_2) = -1 (y_2 = +1) führen, würde die Entscheidungsgrenze zur korrekten Prädiktion der Klasse beim nächsten Durchlauf (j = 3) an w_1 noch weiter in die gleiche Richtung verschoben werden:

\Delta w_1 = (\^{y}_2 - y_2) \cdot x_1 = (-1 - 1) \cdot 2,0 = -2,0 \cdot 2,0 = -4,0

Mehr zum Training von künstlichen neuronalen Netzen ist im nächsten Artikel dieser Artikelserie zu erfahren.

Single-Layer-Perzeptrons (SLP) – Beispiel mit der boolischen Trennung

Verlassen wir nun das Training des Perzeptrons und gehen einfach mal davon aus, dass die idealen Gewichte schon gefunden wurden und schauen uns nun an, was ein Perzeptron alles (nicht) kann. Denn nicht vergessen, es soll eigentlich Klassen unterscheiden bzw. die dafür nötigen Entscheidungsgrenzen finden.

Boolische Operatoren unterscheiden Fälle nach boolischen Werten. Sie sind ein beliebtes “Hello World” für die Einarbeitung in die lineare Entscheidungslogik eines Perzeptrons. Es gibt drei grundlegende boolische Vergleichsoperatoren: AND, OR und XOR

  x1     x2   AND OR XOR
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

Ein Perzeptron zur Lösung dieser Aufgabe bräuchte also zwei Dimensionen (+ Bias): x_1 und x_2
Und es müsste Gewichtungen haben, die dafür sorgen, dass die Vorhersage entsprechend der Logik AND, OR oder XOR mit \^{y} = \phi(z) = \phi (w_0 \cdot 1 + w_1 \cdot x_1 + w_2 \cdot x_2) funktioniert.

Dabei ist es wichtig, dass wir auch phi \phi als Sprungfunktion definieren. Sie könnte beispielsweise so aussehen, dass sie auf den Wert \phi(z) = 1 springt, wenn z > 0 ist, ansonsten aber \phi(z) = 0 bleibt.

Das Netz und die Gewichtungen (w-Setup) könnten für die AND- und die OR-Logik so aussehen:

Die Gewichtungen funktionieren beim SLP problemlos, denn wir haben es mit linear trennbaren Problemen zutun:

Kleiner Test gefällig? So nehmen wir uns erstmal die AND-Logik vor:

  • Wenn x1 = 0 und x2 = 0 ist, gilt: z = -1,5 \cdot 1 + 1 \cdot 0 + 1 \cdot 0 = - 1,5,
    wie erhalten als Prädiktion \phi(z) = \phi(-1,5) = 0
  • Wenn x1 = 1 und x2 = 0 ist, gilt: z = -1,5 \cdot 1 + 1 \cdot 1 + 1 \cdot 0 = - 0,5,
    wie erhalten als Prädiktion \phi(z) = \phi(-0,5) = 0
  • Wenn x1 = 1 und x2 = 1 ist, gilt: z = -1,5 \cdot 1 + 1 \cdot 1 + 1 \cdot 1 = + 0,5,
    wie erhalten als Prädiktion \phi(z) = \phi(0,5) = 1

Scheint zu funktionieren!

Und dann die OR-Logik mit

  • Wenn x1 = 0 und x2 = 0 ist, gilt: z = -0,5 \cdot 1 + 1 \cdot 0 + 1 \cdot 0 = - 0,5,
    wie erhalten als Prädiktion \phi(z) = \phi(-0,5) = 0
  • Wenn x1 = 1 und x2 = 0 ist, gilt: z = -0,5 \cdot 1 + 1 \cdot 1 + 1 \cdot 0 = + 0,5,
    wie erhalten als Prädiktion \phi(z) = \phi(0,5) = 1
  • Wenn x1 = 1 und x2 = 1 ist, gilt: z = -0,5 \cdot 1 + 1 \cdot 1 + 1 \cdot 1 = + 1,5,
    wie erhalten als Prädiktion \phi(z) = \phi(1,5) = 1

Super! Jedoch stellt sich nun die Frage, wie das XOR-Problem zu lösen ist, denn das bedingt sowohl die Grenzen von AND als auch jene des OR-Operators.

Multi-Layer-Perzeptron (MLP) bzw. (Deep) Feed Forward (FF) Net

Denn ein XOR kann mathematisch auch so korrekt beschrieben werden: x_1 \text{ xor } x_2 = (x_1 \text{ and } \neg x_2) \text{ or } (\neg x_1 \text{ and } x_2)

Testen wir es aus!

  • Wenn x1 = 0 und x2 = 0 ist, gilt:
    z_1 = w_{10} \cdot 1 + w_{11} \cdot x1 + w_{12} \cdot  x2 = -0.5 \cdot 1 + 1,0 \cdot 0 - 1,0 \cdot 0 = -0,5 und somit \phi(z_1) = \phi(-0,5) = 0
    z_2 = w_{20} \cdot 1 + w_{21} \cdot x1 + w_{22} \cdot  x2 = -0.5 \cdot 1 - 1,0 \cdot 0 + 1,0 \cdot 0 = -0,5 und somit \phi(z_2) = \phi(-0,5) = 0
    z_3 = w_{30} \cdot 1 + w_{31} \cdot \phi(z_1) + w_{32} \cdot \phi(z_2) = -0,5 \cdot 1 + 1,0 \cdot 0 + 1,0 \cdot 0 = -0,5 und somit \phi(z_3) = \phi(-0,5) = 0
  • Wenn x1 = 1 und x2 = 0 ist, gilt:
    z_1 = w_{10} \cdot 1 + w_{11} \cdot x1 + w_{12} \cdot  x2 = -0.5 \cdot 1 + 1,0 \cdot 1 - 1,0 \cdot 0 = 0,5 und somit \phi(z_1) = \phi(0,5) = 1
    z_2 = w_{20} \cdot 1 + w_{21} \cdot x1 + w_{22} \cdot  x2 = -0.5 \cdot 1 - 1,0 \cdot 1 + 1,0 \cdot 0 = -1,5 und somit \phi(z_2) = \phi(-1,5) = 0
    z_3 = w_{30} \cdot 1 + w_{31} \cdot \phi(z_1) + w_{32} \cdot \phi(z_2) = -0,5 \cdot 1 + 1,0 \cdot 1 + 1,0 \cdot 0 = 0,5 und somit \phi(z_3) = \phi(0,5) = 1
  • Wenn x1 = 0 und x2 = 1 ist, gilt:
    z_1 = w_{10} \cdot 1 + w_{11} \cdot x1 + w_{12} \cdot  x2 = -0.5 \cdot 1 + 1,0 \cdot 0 - 1,0 \cdot 1 = -1,5 und somit \phi(z_1) = \phi(-1,5) = 0
    z_2 = w_{20} \cdot 1 + w_{21} \cdot x1 + w_{22} \cdot  x2 = -0.5 \cdot 1 - 1,0 \cdot 0 + 1,0 \cdot 1 = 0,5 und somit \phi(z_2) = \phi(0,5) = 1
    z_3 = w_{30} \cdot 1 + w_{31} \cdot \phi(z_1) + w_{32} \cdot \phi(z_2) = -0,5 \cdot 1 + 1,0 \cdot 0 + 1,0 \cdot 1 = 0,5 und somit \phi(z_3) = \phi(0,5) = 1
  • Wenn x1 = 1 und x2 = 1 ist, gilt:
    z_1 = w_{10} \cdot 1 + w_{11} \cdot x1 + w_{12} \cdot  x2 = -0.5 \cdot 1 + 1,0 \cdot 1 - 1,0 \cdot 1 = -1,5 und somit \phi(z_1) = \phi(-0,5) = 0
    z_2 = w_{20} \cdot 1 + w_{21} \cdot x1 + w_{22} \cdot  x2 = -0.5 \cdot 1 - 1,0 \cdot 1 + 1,0 \cdot 1 = 0,5 und somit \phi(z_2) = \phi(-0,5) = 0
    z_3 = w_{30} \cdot 1 + w_{31} \cdot \phi(z_1) + w_{32} \cdot \phi(z_2) = -0,5 \cdot 1 + 1,0 \cdot 0 + 1,0 \cdot 0 = -0,5 und somit \phi(z_3) = \phi(-0,5) = 0

Es funktioniert!

Mehrfachklassifikation mit dem Perzeptron

Ein Perzeptron-Netz klassifiziert binär, die Ausgabe beschränkt sich auf 1 oder -1 bzw. 0 oder 1.

Jedoch wird in der Praxis oftmals eine One-vs-All (OvA) bzw. One-vs-Rest (OvR) Klassifikation implementiert. In diesem Fall steht die 1 für die Erkennung einer konkreten Klasse, während alle anderen übrigen Klassen als negativ betrachtet werden.

Um jede Klasse erkennen zu können, werden n Klassifizierer (= n Perzeptron-Netze) benötigt. Jedes Perzeptron-Netz ist auf die Erkennung einer bestimmten Klasse trainiert.

Adaline – Oder: die Limitation des Perzeptrons

Das Perzeptron wird nur über eine Sprungfunktion aktiviert. Das schränkt die Feinabstimmung des Trainings enorm ein. Besser sind Aktivierungen über stetige Funktionen, die dann nämlich differenzierbar (ableitbar) sind. Das ergibt eine konvexe Fehlerfunktion mit einem eindeutigen Minimum. Der Adaline-Algorithmus (ADAptive Linear NEuron) erweitert die Idee des Perzeptrons um genau diese Idee. Der wesentliche Fortschritt der Adaline-Regel gegenüber der des Perzeptrons ist demnach, dass die Aktualisierung der Gewichtungen nicht wie beim Perzeptron auf einer einfachen Sprungfunktion, sondern auf einer linearen, stetigen Aktivierungsfunktion beruht.

Single-Layer-Adaline

Wie ein künstliches neuronales Netz mit der Kategorie Adaline trainiert werden kann, wird im nächsten Artikel dieser Artikelserie erläutert.

Weiterführende Netz-Konzepte (CNN und RNN)

Wer bereits mit Frameworks wie TensorFlow in das Deep Learning eingestiegen ist, hat möglicherweise schon erweiterte Konzepte der künstlichen neuronalen Netze kennen gelernt. Die CNNs (Convolutional Neuronal Network) sind im Moment die Wahl für die Verarbeitung von hochdimensionalen Aufgaben, beispielsweise die Bilderkennung (Computer Vision) und Texterkennung (NLP). Das CNN erweitert die Möglichkeiten mit neuronalen Netzen deutlich, indem ein Netz zur Dimensionsreduktion vorgeschaltet wird, im Kern steckt jedoch weiterhin die Idee der MLPs. Beim Einsatz in der Bilderkennung funktionieren CNNs vereinfacht gesprochen so, dass der vorgeschaltete Netzbereich die Millionen Bildpixel sektorweise ausliest (Convolution, Faltung durch Auslesen über Sektoren, die sich gegenseitig überlappen), verdichtet (Pooling, beispielsweise über nicht-lineare Funktionen wie max()) und dann – nach diesem Prozedere – ähnlich eim MLP klassifiziert.

 

Eine andere erweiterte Form sind RNNs (Recurrent Neuronal Network), die ebenfalls auf der Idee des MLPs basieren, dieses Konzept jedoch dank Rückverbindungen (Neuronen senden an vorherige Schichten) und Selbstverbindungen (Neuronen senden an sich selbst) wiederum auf den Kopf stellen.

 

Dennoch ist es für das tiefere Verständnis von CNNs und RNNs essenziell, dass vorher das Konzept des MLPs verstanden ist. Es ist die einfachste Form der auch heute noch am meisten eingesetzten und sehr mächtigen Netz-Topologien.

Im Jahr 2016 hatte Fjodor van Veen von asimovinstitute.org hatte – dankenswerterweise – mal eine Zusammenstellung von Netz-Topologien erstellt, auf die ich heute noch immer mal wieder einen Blick werfe:

Künstliche neuronale Netze – Topologie-Übersicht von Fjodor van Veen

Buchempfehlungen

Die folgenden Bücher nutze ich für mein Selbststudium von Machine Learning und Deep Learning und sind teilweise Gedankenvorlagen auch für diesen Artikel gewesen:

 

Machine Learning mit Python und Scikit-Learn und TensorFlow: Das umfassende Praxis-Handbuch für Data Science, Predictive Analytics und Deep Learning (mitp Professional) Deep Learning mit Python und Keras: Das Praxis-Handbuch vom Entwickler der Keras-Bibliothek(mitp Professional)