Ein Deep-Learning-Tutorial: Von Perceptrons zu Deep Networks
Veröffentlicht: 2022-03-11In den letzten Jahren hat der Bereich der künstlichen Intelligenz einen Aufschwung erlebt. Es ist über die akademische Welt hinaus verbreitet, wobei große Akteure wie Google, Microsoft und Facebook ihre eigenen Forschungsteams aufbauen und einige beeindruckende Akquisitionen tätigen.
Dies kann zum Teil auf die Fülle von Rohdaten zurückgeführt werden, die von Benutzern sozialer Netzwerke generiert werden, von denen viele analysiert werden müssen, auf den Aufstieg fortschrittlicher Data-Science-Lösungen sowie auf die billige Rechenleistung, die über GPGPUs verfügbar ist.
Aber abgesehen von diesen Phänomenen wurde dieses Wiederaufleben nicht zuletzt durch einen neuen Trend in der KI angetrieben, insbesondere im maschinellen Lernen, bekannt als „Deep Learning“. In diesem Tutorial stelle ich Ihnen die Schlüsselkonzepte und Algorithmen hinter Deep Learning vor, beginnend mit der einfachsten Kompositionseinheit und dem Aufbau bis hin zu den Konzepten des maschinellen Lernens in Java.
(Zur vollständigen Offenlegung: Ich bin auch der Autor einer Java-Deep-Learning-Bibliothek, die hier verfügbar ist, und die Beispiele in diesem Artikel werden mithilfe der obigen Bibliothek implementiert. Wenn sie Ihnen gefällt, können Sie sie unterstützen, indem Sie ihr einen Stern auf GitHub geben , wofür ich dankbar wäre. Eine Gebrauchsanweisung ist auf der Homepage verfügbar.)
Ein 30-Sekunden-Tutorial zum maschinellen Lernen
Falls Sie nicht vertraut sind, sehen Sie sich diese Einführung in maschinelles Lernen an:
Das allgemeine Verfahren ist wie folgt:
- Wir haben einen Algorithmus, der eine Handvoll beschrifteter Beispiele enthält, sagen wir 10 Bilder von Hunden mit der Bezeichnung 1 („Hund“) und 10 Bilder von anderen Dingen mit der Bezeichnung 0 („Kein Hund“) – beachten Sie, dass wir hauptsächlich festhalten zu überwachter, binärer Einstufung für diesen Posten.
- Der Algorithmus „lernt“, Bilder von Hunden zu identifizieren, und hofft, wenn er mit einem neuen Bild gefüttert wird, das richtige Etikett zu erzeugen (1, wenn es sich um ein Bild eines Hundes handelt, andernfalls 0).
Diese Einstellung ist unglaublich allgemein: Ihre Daten könnten Symptome sein und Ihre Etiketten Krankheiten; oder Ihre Daten könnten Bilder von handgeschriebenen Zeichen sein und Ihre Etiketten die tatsächlichen Zeichen, die sie darstellen.
Perceptrons: Frühe Deep-Learning-Algorithmen
Einer der frühesten überwachten Trainingsalgorithmen ist der des Perzeptrons, eines grundlegenden neuronalen Netzwerkbausteins.
Angenommen, wir haben n Punkte in der Ebene, die mit „0“ und „1“ gekennzeichnet sind. Uns wird ein neuer Punkt gegeben und wir wollen seine Bezeichnung erraten (dies ist vergleichbar mit dem obigen „Hund“- und „Nicht-Hund“-Szenario). Wie machen wir es?
Ein Ansatz könnte darin bestehen, den nächsten Nachbarn zu betrachten und die Bezeichnung dieses Punkts zurückzugeben. Eine etwas intelligentere Vorgehensweise wäre jedoch, eine Linie auszuwählen, die die beschrifteten Daten am besten trennt, und diese als Klassifikator zu verwenden.
In diesem Fall würden alle Eingabedaten als Vektor x = ( x_1, x_2 ) dargestellt und unsere Funktion wäre so etwas wie „0“ wenn unterhalb der Linie, „1“ wenn darüber“.
Um dies mathematisch darzustellen, sei unser Trennzeichen durch einen Vektor von Gewichten w und einen vertikalen Offset (oder Bias) b definiert. Dann würde unsere Funktion die Eingaben und Gewichte mit einer gewichteten Summenübertragungsfunktion kombinieren:
Das Ergebnis dieser Übertragungsfunktion würde dann in eine Aktivierungsfunktion eingespeist, um eine Markierung zu erzeugen. Im obigen Beispiel war unsere Aktivierungsfunktion ein Schwellenwert (z. B. 1, wenn größer als ein bestimmter Wert):
Training des Perzeptrons
Das Training des Perzeptrons besteht darin, es mit mehreren Trainingsmustern zu füttern und die Ausgabe für jedes davon zu berechnen. Nach jeder Probe werden die Gewichte w so angepasst, dass der Ausgabefehler minimiert wird, der als Differenz zwischen den gewünschten (Soll-) und den tatsächlichen Ausgaben definiert ist. Es gibt andere Fehlerfunktionen, wie den mittleren quadratischen Fehler, aber das Grundprinzip des Trainings bleibt dasselbe.
Nachteile eines einzelnen Perzeptrons
Der Single-Perceptron-Ansatz für Deep Learning hat einen großen Nachteil: Er kann nur linear trennbare Funktionen lernen. Wie groß ist dieser Nachteil? Nehmen Sie XOR, eine relativ einfache Funktion, und beachten Sie, dass sie nicht durch ein lineares Trennzeichen klassifiziert werden kann (beachten Sie den fehlgeschlagenen Versuch unten):
Um dieses Problem anzugehen, müssen wir ein mehrschichtiges Perzeptron verwenden, das auch als Feedforward-Neuronalnetzwerk bekannt ist: Wir werden tatsächlich eine Reihe dieser Perzeptronen zusammensetzen, um einen leistungsfähigeren Lernmechanismus zu schaffen.
Feedforward Neural Networks für Deep Learning
Ein neuronales Netzwerk ist eigentlich nur eine Zusammensetzung von Perzeptronen, die auf unterschiedliche Weise verbunden sind und mit unterschiedlichen Aktivierungsfunktionen arbeiten.
Zunächst betrachten wir das neuronale Feedforward-Netzwerk, das die folgenden Eigenschaften hat:
- Eine Eingabe, Ausgabe und eine oder mehrere verborgene Schichten. Die obige Abbildung zeigt ein Netzwerk mit einer Eingangsschicht mit 3 Einheiten, einer verborgenen Schicht mit 4 Einheiten und einer Ausgangsschicht mit 2 Einheiten (die Begriffe Einheiten und Neuronen sind austauschbar).
- Jede Einheit ist ein einzelnes Perzeptron wie das oben beschriebene.
- Die Einheiten der Eingabeschicht dienen als Eingaben für die Einheiten der verborgenen Schicht, während die Einheiten der verborgenen Schicht Eingaben für die Ausgabeschicht sind.
- Jede Verbindung zwischen zwei Neuronen hat ein Gewicht w (ähnlich den Perzeptron-Gewichten).
- Jede Einheit der Schicht t ist normalerweise mit jeder Einheit der vorherigen Schicht t - 1 verbunden (obwohl Sie sie trennen könnten, indem Sie ihre Gewichtung auf 0 setzen).
- Um Eingabedaten zu verarbeiten, „klemmen“ Sie den Eingabevektor an die Eingabeschicht und legen die Werte des Vektors als „Ausgaben“ für jede der Eingabeeinheiten fest. In diesem speziellen Fall kann das Netzwerk einen dreidimensionalen Eingangsvektor verarbeiten (aufgrund der 3 Eingangseinheiten). Wenn Ihr Eingabevektor beispielsweise [7, 1, 2] ist, setzen Sie den Ausgang der oberen Eingabeeinheit auf 7, die mittlere Einheit auf 1 und so weiter. Diese Werte werden dann unter Verwendung der gewichteten Summenübertragungsfunktion für jede verborgene Einheit (daher der Begriff Vorwärtsausbreitung) an die verborgenen Einheiten weitergeleitet, die ihrerseits ihre Ausgaben (Aktivierungsfunktion) berechnen.
- Die Ausgabeschicht berechnet ihre Ausgaben auf die gleiche Weise wie die verborgene Schicht. Das Ergebnis der Ausgabeschicht ist die Ausgabe des Netzwerks.
Jenseits der Linearität
Was wäre, wenn jedes unserer Perzeptrons nur eine lineare Aktivierungsfunktion verwenden dürfte? Dann wird die endgültige Ausgabe unseres Netzwerks immer noch eine lineare Funktion der Eingaben sein, nur angepasst mit einer Tonne verschiedener Gewichtungen, die im gesamten Netzwerk gesammelt werden. Mit anderen Worten, eine lineare Zusammensetzung einer Reihe linearer Funktionen ist immer noch nur eine lineare Funktion. Wenn wir uns auf lineare Aktivierungsfunktionen beschränken, dann ist das neuronale Feedforward-Netzwerk nicht leistungsfähiger als das Perzeptron, egal wie viele Schichten es hat.
Aus diesem Grund verwenden die meisten neuronalen Netze nichtlineare Aktivierungsfunktionen wie Logistik, Tanh, Binär oder Gleichrichter. Ohne sie kann das Netzwerk nur Funktionen lernen, die Linearkombinationen seiner Eingaben sind.
Training von Perzeptronen
Der gebräuchlichste Deep-Learning-Algorithmus für das überwachte Training der mehrschichtigen Perzeptrons ist als Backpropagation bekannt. Das grundsätzliche Vorgehen:
- Eine Trainingsprobe wird präsentiert und durch das Netzwerk weitergeleitet.
Der Ausgabefehler wird berechnet, typischerweise der mittlere quadratische Fehler:
Wobei t der Zielwert und y die tatsächliche Netzwerkleistung ist. Andere Fehlerberechnungen sind ebenfalls akzeptabel, aber die MSE ist eine gute Wahl.
Netzwerkfehler werden mithilfe einer Methode minimiert, die als stochastischer Gradientenabstieg bezeichnet wird.
Der Gradientenabstieg ist universell, aber im Fall von neuronalen Netzwerken wäre dies ein Diagramm des Trainingsfehlers als Funktion der Eingabeparameter. Der optimale Wert für jedes Gewicht ist derjenige, bei dem der Fehler ein globales Minimum erreicht. Während der Trainingsphase werden die Gewichte in kleinen Schritten (nach jedem Trainingsmuster oder einem Mini-Batch von mehreren Mustern) so aktualisiert, dass sie immer versuchen, das globale Minimum zu erreichen – aber das ist keine leichte Aufgabe, wie Sie enden oft in lokalen Minima, wie dem auf der rechten Seite. Wenn das Gewicht beispielsweise einen Wert von 0,6 hat, muss es in Richtung 0,4 geändert werden.
Diese Abbildung stellt den einfachsten Fall dar, bei dem der Fehler von einem einzigen Parameter abhängt. Netzwerkfehler hängen jedoch von jedem Netzwerkgewicht ab und die Fehlerfunktion ist viel, viel komplexer.
Glücklicherweise bietet Backpropagation eine Methode zum Aktualisieren jeder Gewichtung zwischen zwei Neuronen in Bezug auf den Ausgabefehler. Die Ableitung selbst ist ziemlich kompliziert, aber die Gewichtsaktualisierung für einen bestimmten Knoten hat die folgende (einfache) Form:
Wobei E der Ausgabefehler ist und w_i die Gewichtung der Eingabe i für das Neuron ist.
Im Wesentlichen besteht das Ziel darin, sich in Bezug auf das Gewicht i in Richtung des Gradienten zu bewegen. Der Schlüsselbegriff ist natürlich die Ableitung des Fehlers, die nicht immer einfach zu berechnen ist: Wie würden Sie diese Ableitung für ein zufälliges Gewicht eines zufälligen versteckten Knotens inmitten eines großen Netzwerks finden?
Die Antwort: durch Backpropagation. Die Fehler werden zuerst an den Ausgabeeinheiten berechnet, wo die Formel ziemlich einfach ist (basierend auf der Differenz zwischen den Ziel- und vorhergesagten Werten), und dann auf clevere Weise durch das Netzwerk zurückgereicht, was es uns ermöglicht, unsere Gewichte während des Trainings und effizient zu aktualisieren (hoffentlich) ein Minimum erreichen.
Versteckte Schicht
Von besonderem Interesse ist die verborgene Schicht. Durch das universelle Approximationstheorem kann ein einzelnes Hidden-Layer-Netzwerk mit einer endlichen Anzahl von Neuronen trainiert werden, um eine willkürlich zufällige Funktion zu approximieren. Mit anderen Worten, eine einzelne verborgene Schicht ist leistungsfähig genug, um jede Funktion zu lernen. Allerdings lernen wir in der Praxis oft besser mit mehreren versteckten Schichten (also tieferen Netzen).
In der verborgenen Schicht speichert das Netzwerk seine interne abstrakte Darstellung der Trainingsdaten, ähnlich wie ein menschliches Gehirn (stark vereinfachte Analogie) eine interne Darstellung der realen Welt hat. Im weiteren Verlauf des Tutorials sehen wir uns verschiedene Möglichkeiten an, mit der verborgenen Ebene herumzuspielen.
Ein Beispielnetzwerk
Sie können ein einfaches neuronales Feedforward-Netzwerk (4-2-3-Schichten) sehen, das den in Java implementierten IRIS-Datensatz hier durch die testMLPSigmoidBP- Methode klassifiziert. Der Datensatz enthält drei Klassen von Irispflanzen mit Merkmalen wie Kelchblattlänge, Blütenblattlänge usw. Dem Netzwerk werden 50 Proben pro Klasse zur Verfügung gestellt. Die Merkmale werden an die Eingabeeinheiten geklemmt, während jede Ausgabeeinheit einer einzelnen Klasse des Datensatzes entspricht: „1/0/0“ zeigt an, dass die Pflanze zur Klasse Setosa gehört, „0/1/0“ zeigt Versicolour an und „ 0/0/1“ zeigt Virginia an). Der Klassifizierungsfehler beträgt 2/150 (dh es werden 2 Proben von 150 falsch klassifiziert).
Das Problem mit großen Netzwerken
Ein neuronales Netzwerk kann mehr als eine verborgene Schicht haben: In diesem Fall „bauen“ die höheren Schichten neue Abstraktionen auf den vorherigen Schichten auf. Und wie bereits erwähnt, lernt man in der Praxis oft besser mit größeren Netzwerken.
Die Erhöhung der Anzahl verborgener Schichten führt jedoch zu zwei bekannten Problemen:
- Verschwindende Gradienten: Wenn wir immer mehr verborgene Schichten hinzufügen, wird Backpropagation immer weniger nützlich, um Informationen an die unteren Schichten weiterzugeben. Wenn Informationen zurückgeleitet werden, beginnen die Gradienten tatsächlich zu verschwinden und werden relativ zu den Gewichten der Netzwerke klein.
- Overfitting: vielleicht das zentrale Problem beim maschinellen Lernen. Kurz gesagt beschreibt Overfitting das Phänomen, dass die Trainingsdaten zu genau angepasst werden, möglicherweise mit zu komplexen Hypothesen. In einem solchen Fall passt Ihr Lernender wirklich gut zu den Trainingsdaten, wird aber bei realen Beispielen viel, viel schlechter abschneiden.
Sehen wir uns einige Deep-Learning-Algorithmen an, um diese Probleme anzugehen.
Autoencoder
Die meisten Einführungskurse in maschinelles Lernen hören in der Regel bei Feedforward-Neuronalen Netzen auf. Aber der Raum möglicher Netze ist viel reichhaltiger – also machen wir weiter.
Ein Autoencoder ist typischerweise ein neuronales Feedforward-Netzwerk, das darauf abzielt , eine komprimierte, verteilte Darstellung (Codierung) eines Datensatzes zu lernen.
Konzeptionell wird das Netzwerk darauf trainiert, die Eingabe „neu zu erstellen“, dh die Eingabe- und die Zieldaten sind gleich. Mit anderen Worten: Sie versuchen, dasselbe auszugeben, was Sie eingegeben haben, aber auf irgendeine Weise komprimiert. Dies ist ein verwirrender Ansatz, also schauen wir uns ein Beispiel an.
Komprimieren der Eingabe: Graustufenbilder
Angenommen, die Trainingsdaten bestehen aus 28 x 28 Graustufenbildern und der Wert jedes Pixels wird an ein Neuron der Eingabeschicht geklemmt (dh die Eingabeschicht hat 784 Neuronen). Dann hätte die Ausgabeschicht die gleiche Anzahl von Einheiten (784) wie die Eingabeschicht und der Zielwert für jede Ausgabeeinheit wäre der Graustufenwert eines Pixels des Bildes.
Die Intuition hinter dieser Architektur ist, dass das Netzwerk keine „Zuordnung“ zwischen den Trainingsdaten und ihren Labels lernt, sondern stattdessen die interne Struktur und die Merkmale der Daten selbst lernt. (Aus diesem Grund wird die verborgene Schicht auch als Merkmalsdetektor bezeichnet.) Normalerweise ist die Anzahl der verborgenen Einheiten kleiner als die Ein-/Ausgabeschichten, was das Netzwerk zwingt, nur die wichtigsten Merkmale zu lernen, und eine Dimensionsreduktion erreicht.
Tatsächlich möchten wir, dass einige kleine Knoten in der Mitte die Daten wirklich auf konzeptioneller Ebene lernen und eine kompakte Darstellung erzeugen, die in gewisser Weise die Kernmerkmale unserer Eingabe erfasst.
Grippe-Erkrankung
Um Autoencoder weiter zu demonstrieren, schauen wir uns eine weitere Anwendung an.
In diesem Fall verwenden wir einen einfachen Datensatz, der aus Grippesymptomen besteht (Dank an diesen Blogbeitrag für die Idee). Bei Interesse finden Sie den Code für dieses Beispiel in der Methode testAEBackpropagation .
So gliedert sich der Datensatz:
- Es gibt sechs Binäreingangsfunktionen.
- Die ersten drei sind Symptome der Krankheit. Zum Beispiel zeigt 1 0 0 0 0 0 an, dass dieser Patient hohe Temperatur hat, während 0 1 0 0 0 0 Husten anzeigt, 1 1 0 0 0 0 Husten und hohe Temperatur anzeigt usw.
- Die letzten drei Merkmale sind „Gegen“-Symptome; Wenn ein Patient eines davon hat, ist es weniger wahrscheinlich, dass er oder sie krank ist. Beispielsweise zeigt 0 0 0 1 0 0 an, dass dieser Patient einen Grippeimpfstoff hat. Kombinationen der beiden Merkmale sind möglich: 0 1 0 1 0 0 zeigt einen geimpften Patienten mit Husten an und so weiter.
Wir betrachten einen Patienten als krank, wenn er oder sie mindestens zwei der ersten drei Merkmale aufweist, und als gesund, wenn er oder sie mindestens zwei der zweiten drei Merkmale aufweist (wobei die Bindungen zugunsten der gesunden Patienten brechen), z.
- 111000, 101000, 110000, 011000, 011100 = krank
- 000111, 001110, 000101, 000011, 000110 = gesund
Wir trainieren einen Autoencoder (unter Verwendung von Backpropagation) mit sechs Eingabe- und sechs Ausgabeeinheiten, aber nur zwei versteckten Einheiten .
Nach mehreren hundert Iterationen beobachten wir, dass, wenn jede der „kranken“ Proben dem maschinellen Lernnetzwerk präsentiert wird, eine der beiden versteckten Einheiten (die gleiche Einheit für jede „kranke“ Probe) immer einen höheren Aktivierungswert aufweist als die andere. Im Gegenteil, wenn eine „gesunde“ Probe präsentiert wird, hat die andere versteckte Einheit eine höhere Aktivierung.
Zurück zum maschinellen Lernen
Im Wesentlichen haben unsere beiden versteckten Einheiten eine kompakte Darstellung des Grippesymptomdatensatzes gelernt . Um zu sehen, wie sich dies auf das Lernen bezieht, kehren wir zum Problem der Überanpassung zurück. Indem wir unser Netz darauf trainieren, eine kompakte Darstellung der Daten zu lernen, bevorzugen wir eine einfachere Darstellung statt einer hochkomplexen Hypothese, die die Trainingsdaten überpasst.
Indem wir diese einfacheren Darstellungen bevorzugen, versuchen wir gewissermaßen, die Daten in einem wahrhaftigeren Sinne zu lernen.
Eingeschränkte Boltzmann-Maschinen
Der nächste logische Schritt besteht darin, sich Restricted Boltzmann Machines (RBM) anzusehen, ein generatives stochastisches neuronales Netzwerk, das eine Wahrscheinlichkeitsverteilung über seinen Satz von Eingaben lernen kann .

RBMs bestehen aus einer verborgenen, einer sichtbaren und einer Bias-Schicht. Im Gegensatz zu den Feedforward-Netzwerken sind die Verbindungen zwischen den sichtbaren und verborgenen Schichten ungerichtet (die Werte können sowohl von sichtbar nach verborgen als auch von verborgen nach sichtbar übertragen werden) und vollständig verbunden (jede Einheit einer bestimmten Schicht ist mit ihr verbunden). jede Einheit in der nächsten – wenn wir einer Einheit in einer beliebigen Schicht erlauben würden, sich mit einer anderen Schicht zu verbinden, dann hätten wir eine Boltzmann-Maschine (statt einer eingeschränkten Boltzmann -Maschine).
Das Standard-RBM hat binäre versteckte und sichtbare Einheiten: Das heißt, die Einheitenaktivierung ist 0 oder 1 unter einer Bernoulli-Verteilung, aber es gibt Varianten mit anderen Nichtlinearitäten.
Während Forscher schon seit einiger Zeit über RBMs Bescheid wissen, hat die kürzliche Einführung des unüberwachten Trainingsalgorithmus für kontrastive Divergenz neues Interesse geweckt.
Kontrastive Divergenz
Der einstufige kontrastive Divergenzalgorithmus (CD-1) funktioniert wie folgt:
- Positive Phase :
- Eine Eingangsprobe v wird an die Eingangsschicht geklemmt.
- v wird auf ähnliche Weise wie die Feedforward-Netzwerke an die verborgene Schicht weitergegeben. Das Ergebnis der Aktivierungen der versteckten Schicht ist h .
- Negative Phase :
- Propagiere h zurück zur sichtbaren Schicht mit Ergebnis v' (die Verbindungen zwischen der sichtbaren und der verborgenen Schicht sind ungerichtet und erlauben daher eine Bewegung in beide Richtungen).
- Propagieren Sie das neue v' zurück zur verborgenen Schicht mit dem Aktivierungsergebnis h' .
Gewichtsaktualisierung :
Dabei ist a die Lernrate und v , v' , h , h' und w sind Vektoren.
Die Intuition hinter dem Algorithmus ist, dass die positive Phase ( h gegeben v ) die interne Darstellung der Daten der realen Welt im Netzwerk widerspiegelt. Währenddessen stellt die negative Phase einen Versuch dar, die Daten basierend auf dieser internen Repräsentation ( v' gegeben h ) neu zu erstellen. Das Hauptziel ist, dass die generierten Daten so nah wie möglich an der realen Welt sind , und dies spiegelt sich in der Gewichtsaktualisierungsformel wider.
Mit anderen Worten, das Netz hat eine Vorstellung davon, wie die Eingabedaten dargestellt werden können, also versucht es, die Daten basierend auf dieser Vorstellung zu reproduzieren. Wenn seine Reproduktion nicht realitätsnah genug ist, nimmt er eine Anpassung vor und versucht es erneut.
Rückkehr zur Grippe
Um die kontrastive Divergenz zu demonstrieren, verwenden wir denselben Symptomdatensatz wie zuvor. Das Testnetz ist ein RBM mit sechs sichtbaren und zwei versteckten Einheiten. Wir trainieren das Netzwerk mit kontrastiver Divergenz, wobei die Symptome v an die sichtbare Schicht geklemmt werden. Während des Tests werden die Symptome erneut der sichtbaren Schicht präsentiert; dann werden die Daten an die verborgene Schicht weitergegeben. Die verborgenen Einheiten stellen den Kranken-/Gesundheitszustand dar, eine dem Autoencoder sehr ähnliche Architektur (Propagierung von Daten von der sichtbaren zur verborgenen Schicht).
Nach mehreren hundert Iterationen können wir das gleiche Ergebnis wie bei Autoencodern beobachten: Eine der versteckten Einheiten hat einen höheren Aktivierungswert, wenn eines der „kranken“ Samples präsentiert wird, während die andere immer aktiver für die „gesunden“ Samples ist.
Sie können dieses Beispiel in der Methode testContrastiveDivergence in Aktion sehen.
Tiefe Netzwerke
Wir haben jetzt gezeigt, dass die verborgenen Schichten von Autoencodern und RBMs als effektive Merkmalsdetektoren fungieren; aber es kommt selten vor, dass wir diese Funktionen direkt nutzen können. Tatsächlich ist der obige Datensatz eher eine Ausnahme als eine Regel. Stattdessen müssen wir einen Weg finden, diese erkannten Merkmale indirekt zu nutzen.
Glücklicherweise wurde entdeckt, dass diese Strukturen gestapelt werden können, um tiefe Netzwerke zu bilden. Diese Netzwerke können gierig Schicht für Schicht trainiert werden, um dabei zu helfen, die mit der klassischen Backpropagation verbundenen Probleme des verschwindenden Gradienten und der Überanpassung zu überwinden.
Die resultierenden Strukturen sind oft ziemlich mächtig und produzieren beeindruckende Ergebnisse. Nehmen Sie zum Beispiel Googles berühmtes „Katzen“-Papier, in dem sie eine spezielle Art von tiefen Autoencodern verwenden, um die Gesichtserkennung von Menschen und Katzen auf der Grundlage von unbeschrifteten Daten zu „lernen“.
Lasst uns genauer hinschauen.
Gestapelte Autoencoder
Wie der Name schon sagt, besteht dieses Netzwerk aus mehreren gestapelten Autoencodern.
Die verborgene Schicht von Autoencoder t dient als Eingabeschicht für Autoencoder t + 1 . Die Eingabeschicht des ersten Autoencoders ist die Eingabeschicht für das gesamte Netzwerk. Das gierige schichtweise Trainingsverfahren funktioniert wie folgt:
- Trainieren Sie den ersten Autoencoder ( t=1 , oder die roten Verbindungen in der Abbildung oben, aber mit einer zusätzlichen Ausgabeschicht) einzeln im Backpropagation-Verfahren mit allen verfügbaren Trainingsdaten.
- Trainieren Sie den zweiten Autoencoder t=2 (grüne Verbindungen). Da die Eingabeschicht für t=2 die verborgene Schicht von t=1 ist, interessiert uns die Ausgabeschicht von t=1 nicht mehr und wir entfernen sie aus dem Netzwerk. Das Training beginnt mit dem Festklemmen eines Eingangssamples an der Eingangsschicht von t=1 , das an die Ausgangsschicht von t=2 weitergeleitet wird. Als nächstes werden die Gewichtungen (Eingabe-versteckt und verborgen-Ausgabe) von t=2 unter Verwendung von Backpropagation aktualisiert. t=2 verwendet alle Trainingsgebiete, ähnlich wie t=1 .
- Wiederholen Sie das vorherige Verfahren für alle Schichten (dh entfernen Sie die Ausgabeschicht des vorherigen Autoencoders, ersetzen Sie sie durch noch einen anderen Autoencoder und trainieren Sie mit Rückwärtsausbreitung).
- Die Schritte 1-3 werden als Vortraining bezeichnet und belassen die Gewichte richtig initialisiert. Es gibt jedoch keine Zuordnung zwischen den Eingabedaten und den Ausgabelabels. Wenn das Netzwerk beispielsweise darauf trainiert ist, Bilder von handgeschriebenen Ziffern zu erkennen, ist es immer noch nicht möglich, die Einheiten vom letzten Merkmalsdetektor (dh der verborgenen Schicht des letzten Autocodierers) auf den Zifferntyp des Bildes abzubilden. In diesem Fall besteht die häufigste Lösung darin, eine oder mehrere vollständig verbundene Schichten zur letzten Schicht (blaue Verbindungen) hinzuzufügen. Das gesamte Netzwerk kann nun als mehrschichtiges Perzeptron betrachtet werden und wird mittels Backpropagation trainiert (dieser Schritt wird auch Feinabstimmung genannt).
Bei gestapelten Auto-Encodern geht es also darum, eine effektive Vortrainingsmethode zum Initialisieren der Gewichtungen eines Netzwerks bereitzustellen, sodass Sie ein komplexes, mehrschichtiges Perzeptron erhalten, das zum Trainieren (oder Feinabstimmen ) bereit ist.
Deep-Belief-Netzwerke
Wie bei Autoencodern können wir auch Boltzmann-Maschinen stapeln, um eine Klasse zu erstellen, die als Deep Belief Networks (DBNs) bekannt ist.
In diesem Fall fungiert die verborgene Schicht von RBM t als sichtbare Schicht für RBM t+1 . Die Eingabeschicht des ersten RBM ist die Eingabeschicht für das gesamte Netzwerk, und das gierige schichtweise Vortraining funktioniert folgendermaßen:
- Trainieren Sie das erste RBM t=1 mit kontrastiver Divergenz mit allen Trainingsbeispielen.
- Trainieren Sie das zweite RBM t=2 . Da die sichtbare Schicht für t=2 die verborgene Schicht von t=1 ist, beginnt das Training mit dem Klemmen des Eingabesamples auf die sichtbare Schicht von t=1 , die nach vorne zur verborgenen Schicht von t=1 propagiert wird. Diese Daten dienen dann dazu, ein kontrastives Divergenztraining für t=2 einzuleiten.
- Wiederholen Sie den vorherigen Vorgang für alle Ebenen.
- Ähnlich wie bei den gestapelten Autoencodern kann das Netzwerk nach dem Vortraining erweitert werden, indem eine oder mehrere vollständig verbundene Schichten mit der letzten verdeckten RBM-Schicht verbunden werden. Dies bildet ein mehrschichtiges Perzeptron, das dann unter Verwendung von Backpropagation fein abgestimmt werden kann.
Dieses Verfahren ähnelt dem von gestapelten Autoencodern, wobei jedoch die Autoencoder durch RBMs und Backpropagation durch den kontrastiven Divergenzalgorithmus ersetzt werden.
(Hinweis: Weitere Informationen zum Erstellen und Trainieren von gestapelten Autoencodern oder Deep-Belief-Netzwerken finden Sie hier im Beispielcode.)
Faltungsnetzwerke
Als letzte Deep-Learning-Architektur werfen wir einen Blick auf Convolutional Networks, eine besonders interessante und spezielle Klasse von Feedforward-Netzwerken, die sich sehr gut für die Bilderkennung eignen.
Bevor wir uns die tatsächliche Struktur von Faltungsnetzwerken ansehen, definieren wir zunächst einen Bildfilter oder einen quadratischen Bereich mit zugehörigen Gewichten. Ein Filter wird auf ein gesamtes Eingabebild angewendet, und Sie werden oft mehrere Filter anwenden. Beispielsweise könnten Sie vier 6x6-Filter auf ein bestimmtes Eingabebild anwenden. Dann ist das Ausgabepixel mit den Koordinaten 1,1 die gewichtete Summe eines 6x6-Quadrats von Eingabepixeln mit der oberen linken Ecke 1,1 und den Gewichtungen des Filters (das ebenfalls ein 6x6-Quadrat ist). Das Ausgabepixel 2,1 ist das Ergebnis des Eingabequadrats mit der oberen linken Ecke 2,1 und so weiter.
Damit sind diese Netzwerke durch die folgenden Eigenschaften definiert:
- Faltungsschichten wenden eine Reihe von Filtern auf die Eingabe an. Beispielsweise könnte die erste Faltungsschicht des Bildes vier 6x6-Filter haben. Das Ergebnis eines auf das Bild angewendeten Filters wird als Merkmalskarte (FM) bezeichnet, und die Anzahl der Merkmalskarten entspricht der Anzahl der Filter. Wenn die vorherige Schicht ebenfalls konvolutionell ist, werden die Filter auf alle ihre FMs mit unterschiedlichen Gewichten angewendet, sodass jede Eingangs-FM mit jeder Ausgangs-FM verbunden ist. Die Intuition hinter den gemeinsamen Gewichtungen im Bild ist, dass die Merkmale unabhängig von ihrer Position erkannt werden, während die Vielzahl von Filtern es jedem von ihnen ermöglicht, einen anderen Satz von Merkmalen zu erkennen.
- Subsampling-Layer reduzieren die Größe der Eingabe. Wenn die Eingabe beispielsweise aus einem 32x32-Bild besteht und die Ebene einen Subsampling-Bereich von 2x2 hat, wäre der Ausgabewert ein 16x16-Bild, was bedeutet, dass 4 Pixel (jeweils 2x2 Quadrate) des Eingabebilds zu einer einzigen Ausgabe kombiniert werden Pixel. Es gibt mehrere Möglichkeiten zum Unterabtasten, aber die beliebtesten sind Max-Pooling, Average-Pooling und Stochastisches Pooling.
- Die letzte Unterabtastungs- (oder Faltungs-) Schicht ist normalerweise mit einer oder mehreren vollständig verbundenen Schichten verbunden, von denen die letzte die Zieldaten darstellt.
- Das Training wird unter Verwendung einer modifizierten Backpropagation durchgeführt, die die Subsampling-Schichten berücksichtigt und die Faltungsfiltergewichte basierend auf allen Werten aktualisiert, auf die dieser Filter angewendet wird.
Sie können hier mehrere Beispiele für Faltungsnetzwerke sehen, die auf dem MNIST-Datensatz (Graustufenbilder von handgeschriebenen Buchstaben) trainiert wurden (mit Backpropagation), insbesondere in den testLeNet* -Methoden (ich würde testLeNetTiny2 empfehlen, da es eine niedrige Fehlerrate von etwa 2% erreicht). in relativ kurzer Zeit). Hier gibt es auch eine schöne JavaScript-Visualisierung eines ähnlichen Netzwerks.
Implementierung
Nachdem wir nun die häufigsten Varianten neuronaler Netze behandelt haben, dachte ich, ich schreibe ein wenig über die Herausforderungen, die sich bei der Implementierung dieser Deep-Learning-Strukturen stellen.
Im Großen und Ganzen war (und ist) mein Ziel bei der Erstellung einer Deep-Learning-Bibliothek, ein auf neuronalen Netzwerken basierendes Framework zu erstellen, das die folgenden Kriterien erfüllt:
- Eine gemeinsame Architektur, die verschiedene Modelle darstellen kann (z. B. alle Varianten von neuronalen Netzen, die wir oben gesehen haben).
- Die Fähigkeit, verschiedene Trainingsalgorithmen zu verwenden (Backpropagation, kontrastive Divergenz usw.).
- Anständige Leistung.
Um diese Anforderungen zu erfüllen, habe ich beim Design der Software einen abgestuften (oder modularen) Ansatz gewählt.
Struktur
Beginnen wir mit den Grundlagen:
- NeuralNetworkImpl ist die Basisklasse für alle neuronalen Netzwerkmodelle.
- Jedes Netzwerk enthält eine Reihe von Schichten.
- Jede Schicht hat eine Liste von Verbindungen, wobei eine Verbindung eine Verbindung zwischen zwei Schichten ist, sodass das Netzwerk ein gerichteter azyklischer Graph ist.
Diese Struktur ist agil genug, um für klassische Feedforward-Netzwerke sowie für RBMs und komplexere Architekturen wie ImageNet verwendet zu werden.
Es ermöglicht auch, dass eine Schicht Teil von mehr als einem Netzwerk ist. Beispielsweise sind die Schichten in einem Deep Belief Network auch Schichten in ihren entsprechenden RBMs.
Darüber hinaus ermöglicht diese Architektur, dass ein DBN während der Vortrainingsphase als Liste gestapelter RBMs und während der Feinabstimmungsphase als Feedforward-Netzwerk angezeigt wird, was sowohl intuitiv nett als auch programmatisch bequem ist.
Datenweitergabe
Das nächste Modul kümmert sich um die Verbreitung von Daten durch das Netzwerk, ein zweistufiger Prozess:
- Bestimmen Sie die Reihenfolge der Ebenen. Um beispielsweise die Ergebnisse von einem mehrschichtigen Perzeptron zu erhalten, werden die Daten an die Eingabeschicht „geklemmt“ (daher ist dies die erste zu berechnende Schicht) und bis zur Ausgabeschicht weitergegeben. Um die Gewichte während der Backpropagation zu aktualisieren, muss der Ausgabefehler beginnend mit der Ausgabeschicht durch jede Schicht in der Breite-zuerst-Reihenfolge propagiert werden. Dies wird durch verschiedene Implementierungen von LayerOrderStrategy erreicht, die sich die Graphstruktur des Netzwerks zunutze macht und verschiedene Graphtraversalmethoden verwendet. Einige Beispiele sind die Breitenstrategie und das Targeting einer bestimmten Ebene. Die Reihenfolge wird tatsächlich durch die Verbindungen zwischen den Schichten bestimmt, sodass die Strategien eine geordnete Liste von Verbindungen zurückgeben.
- Berechnen Sie den Aktivierungswert. Jede Schicht hat einen zugeordneten ConnectionCalculator , der seine Liste von Verbindungen (aus dem vorherigen Schritt) und Eingabewerte (von anderen Schichten) nimmt und die resultierende Aktivierung berechnet. Beispielsweise nimmt in einem einfachen sigmoidalen Feedforward-Netzwerk der ConnectionCalculator der verborgenen Schicht die Werte der Eingabe- und Bias-Schicht (die jeweils die Eingabedaten und ein Array von 1s sind) und die Gewichtungen zwischen den Einheiten (im Falle einer vollständig verbundenen Schichten, die Gewichte werden tatsächlich in einer FullyConnected- Verbindung als Matrix gespeichert), berechnet die gewichtete Summe und speist das Ergebnis in die Sigmoid-Funktion ein. Die Verbindungsrechner implementieren eine Vielzahl von Übertragungsfunktionen (z. B. gewichtete Summe, Faltung) und Aktivierungsfunktionen (z. B. logistisch und tanh für mehrschichtiges Perzeptron, binär für RBM). Die meisten von ihnen können mit Aparapi auf einer GPU ausgeführt und mit Mini-Batch-Training verwendet werden.
GPU-Berechnung mit Aparapi
Wie ich bereits erwähnt habe, ist einer der Gründe für das Wiederaufleben neuronaler Netze in den letzten Jahren, dass ihre Trainingsmethoden der Parallelität sehr förderlich sind, sodass Sie das Training mit der Verwendung einer GPGPU erheblich beschleunigen können. In diesem Fall habe ich mich entschieden, mit der Aparapi-Bibliothek zu arbeiten, um GPU-Unterstützung hinzuzufügen.
Aparapi erlegt den Verbindungsrechnern einige wichtige Einschränkungen auf:
- Es sind nur eindimensionale Arrays (und Variablen) primitiver Datentypen erlaubt.
- Nur Member-Methoden der Aparapi- Kernel -Klasse selbst dürfen vom ausführbaren GPU-Code aufgerufen werden.
Daher werden die meisten Daten (Gewichte, Eingabe- und Ausgabe-Arrays) in Matrix -Instanzen gespeichert, die intern eindimensionale Float-Arrays verwenden. Alle Aparapi-Verbindungsrechner verwenden entweder AparapiWeightedSum (für vollständig verbundene Schichten und gewichtete Summeneingabefunktionen), AparapiSubsampling2D (für Subsampling-Schichten) oder AparapiConv2D (für Faltungsschichten). Einige dieser Einschränkungen können mit der Einführung der heterogenen Systemarchitektur überwunden werden. Aparapi also allows to run the same code on both CPU and GPU.
Training
The training module implements various training algorithms. It relies on the previous two modules. For example, BackPropagationTrainer (all the trainers are using the Trainer base class) uses feedforward layer calculator for the feedforward phase and a special breadth-first layer calculator for propagating the error and updating the weights.
My latest work is on Java 8 support and some other improvements, will soon be merged into master.
Fazit
The aim of this Java deep learning tutorial was to give you a brief introduction to the field of deep learning algorithms, beginning with the most basic unit of composition (the perceptron) and progressing through various effective and popular architectures, like that of the restricted Boltzmann machine.
The ideas behind neural networks have been around for a long time; but today, you can't step foot in the machine learning community without hearing about deep networks or some other take on deep learning. Hype shouldn't be mistaken for justification, but with the advances of GPGPU computing and the impressive progress made by researchers like Geoffrey Hinton, Yoshua Bengio, Yann LeCun and Andrew Ng, the field certainly shows a lot of promise. There's no better time to get familiar and get involved like the present.
Appendix: Resources
If you're interested in learning more, I found the following resources quite helpful during my work:
- DeepLearning.net: a portal for all things deep learning. It has some nice tutorials, software library and a great reading list.
- An active Google+ community.
- Two very good courses: Machine Learning and Neural Networks for Machine Learning, both offered on Coursera.
- The Stanford neural networks tutorial.