Applied Deep Learning – Teil 3: Autoencoder

Willkommen zu Teil 3 der Applied Deep Learning Serie. Teil 1 war eine praktische Einführung in künstliche Neuronale Netze, die sowohl die Theorie als auch die Anwendung mit vielen Codebeispielen und Visualisierungen abdeckte. In Teil 2 haben wir Deep Learning auf reale Datensätze angewendet und die 3 am häufigsten auftretenden Probleme als Fallstudien behandelt: binäre Klassifikation, Multiklassenklassifikation und Regression.

Jetzt tauchen wir in spezifische Deep-Learning-Architekturen ein, beginnend mit den einfachsten: Autoencodern.

  1. Einführung
  2. Architektur
  3. Implementierung
  4. Rauschunterdrückung von Autoencodern
  5. Sparse Autoencoder
  6. Anwendungsfälle
  7. Fazit

Der Code für diesen Artikel ist hier als Jupyter Notebook verfügbar, Sie können ihn gerne herunterladen und selbst ausprobieren.

Einführung

Autoencoder sind eine bestimmte Art von Feedforward-neuronalen Netzen, bei denen die Eingabe mit der Ausgabe identisch ist. Sie komprimieren die Eingabe in einen niederdimensionalen Code und rekonstruieren dann die Ausgabe aus dieser Darstellung. Der Code ist eine kompakte „Zusammenfassung“ oder „Komprimierung“ der Eingabe, auch Latentraumdarstellung genannt.

Ein Autoencoder besteht aus 3 Komponenten: Encoder, Code und Decoder. Der Encoder komprimiert die Eingabe und erzeugt den Code, der Decoder rekonstruiert dann die Eingabe nur unter Verwendung dieses Codes.

Um einen Autoencoder zu erstellen, benötigen wir 3 Dinge: eine Codierungsmethode, eine Decodierungsmethode und eine Verlustfunktion, um die Ausgabe mit dem Ziel zu vergleichen. Wir werden diese im nächsten Abschnitt untersuchen.

Autoencoder sind hauptsächlich ein Algorithmus zur Reduzierung der Dimensionalität (oder Komprimierung) mit einigen wichtigen Eigenschaften:

  • Datenspezifisch: Autoencoder sind nur in der Lage, Daten sinnvoll zu komprimieren, ähnlich wie sie trainiert wurden. Da sie Funktionen erlernen, die für die angegebenen Trainingsdaten spezifisch sind, unterscheiden sie sich von einem Standard-Datenkomprimierungsalgorithmus wie gzip. Wir können also nicht erwarten, dass ein Autoencoder, der auf handgeschriebenen Ziffern trainiert ist, Landschaftsfotos komprimiert.
  • Verlustbehaftet: Die Ausgabe des Autoencoders entspricht nicht genau der Eingabe, sondern ist eine enge, aber verschlechterte Darstellung. Wenn Sie verlustfreie Komprimierung wünschen, sind sie nicht der richtige Weg.
  • Unüberwacht: Um einen Autoencoder zu trainieren, müssen wir nichts Besonderes tun, sondern nur die rohen Eingabedaten darauf werfen. Autoencoder gelten als unbeaufsichtigte Lerntechnik, da sie zum Trainieren keine expliziten Beschriftungen benötigen. Genauer gesagt sind sie jedoch selbstüberwacht, da sie aus den Trainingsdaten ihre eigenen Labels generieren.

Architektur

Lassen Sie uns die Details des Encoders, Codes und Decoders untersuchen. Sowohl der Encoder als auch der Decoder sind vollständig verbundene Feedforward-neuronale Netze, im Wesentlichen die ANNs, die wir in Teil 1 behandelt haben. Code ist eine einzelne Schicht eines Objekts mit der Dimensionalität unserer Wahl. Die Anzahl der Knoten in der Codeschicht (Codegröße) ist ein Hyperparameter, den wir vor dem Training des Autoencoders festlegen.

Dies ist eine detailliertere Visualisierung eines Autoencoders. Zuerst durchläuft der Eingang den Encoder, bei dem es sich um ein vollständig verbundenes GERÄT handelt, um den Code zu erzeugen. Der Decoder, der die ähnliche ANN-Struktur aufweist, erzeugt dann die Ausgabe nur unter Verwendung des Codes. Ziel ist es, eine Ausgabe zu erhalten, die mit der Eingabe identisch ist. Beachten Sie, dass die Decoderarchitektur das Spiegelbild des Encoders ist. Dies ist keine Anforderung, aber normalerweise der Fall. Die einzige Anforderung ist, dass die Dimensionalität von Eingabe und Ausgabe gleich sein muss. Alles in der Mitte kann gespielt werden.

Es gibt 4 Hyperparameter, die wir vor dem Training eines Autoencoders festlegen müssen:

  • Codegröße: Anzahl der Knoten in der mittleren Ebene. Kleinere Größe führt zu mehr Kompression.
  • Anzahl der Ebenen: Der Autoencoder kann so tief sein, wie wir möchten. In der obigen Abbildung haben wir 2 Schichten sowohl im Encoder als auch im Decoder, ohne den Eingang und den Ausgang zu berücksichtigen.
  • Anzahl der Knoten pro Layer: Die Autoencoder-Architektur, an der wir arbeiten, wird als gestapelter Autoencoder bezeichnet, da die Layer nacheinander gestapelt werden. Normalerweise sehen gestapelte Autoencoder wie ein „Sandwitch“ aus. Die Anzahl der Knoten pro Schicht nimmt mit jeder nachfolgenden Schicht des Encoders ab und nimmt im Decoder wieder zu. Auch der Decoder ist bezüglich des Schichtaufbaus symmetrisch zum Encoder. Wie oben erwähnt, ist dies nicht erforderlich und wir haben die vollständige Kontrolle über diese Parameter.
  • Verlustfunktion: Wir verwenden entweder den mittleren quadratischen Fehler (mse) oder die binäre Crossentropie. Wenn die Eingabewerte im Bereich liegen, verwenden wir normalerweise Crossentropy , andernfalls verwenden wir den mittleren quadratischen Fehler. Für weitere Details schauen Sie sich dieses Video an.

Autoencoder werden auf die gleiche Weise wie ANNs über Backpropagation trainiert. Lesen Sie die Einführung von Teil 1 für weitere Details darüber, wie neuronale Netze trainiert werden, es gilt direkt für die Autoencoder.

Implementierung

Jetzt implementieren wir einen Autoencoder für die folgende Architektur, 1 versteckte Schicht im Encoder und Decoder.

Wir werden den äußerst beliebten MNIST-Datensatz als Eingabe verwenden. Es enthält Schwarz-Weiß-Bilder von handgeschriebenen Ziffern.

Sie haben die Größe 28×28 und wir verwenden sie als Vektor von 784 Zahlen dazwischen. Überprüfen Sie das Jupyter Notebook für die Details.

Wir werden nun den Autoencoder mit Keras implementieren. Die Hyperparameter sind: 128 Knoten in der verborgenen Ebene, die Codegröße beträgt 32 und die binäre Crossentropie ist die Verlustfunktion.

Dies ist den ANNs, an denen wir gearbeitet haben, sehr ähnlich, aber jetzt verwenden wir die Keras Functional API. Weitere Informationen finden Sie in diesem Handbuch, aber hier ist ein kurzer Vergleich. Bevor wir Ebenen mithilfe der sequentiellen API wie folgt hinzugefügt haben:

model.add(Dense(16, activation='relu'))
model.add(Dense(8, activation='relu'))

Mit der funktionalen API machen wir das:

layer_1 = Dense(16, activation='relu')(input)
layer_2 = Dense(8, activation='relu')(layer_1)

Es ist ausführlicher, aber eine flexiblere Möglichkeit, komplexe Modelle zu definieren. Wir können leicht Teile unseres Modells greifen, zum Beispiel nur den Decoder, und damit arbeiten. Mit der funktionalen API stellen wir ihr die Eingabe zur Verfügung und speichern die Ausgabe. Die Ausgabe einer Ebene wird zur Eingabe der nächsten Ebene. Mit der sequentiellen API hat die add Methode dies implizit für uns gehandhabt.

Beachten Sie, dass alle Ebenen die Relu-Aktivierungsfunktion verwenden, wie dies bei tiefen neuronalen Netzen der Standard ist. Die letzte Schicht verwendet die Sigmoidaktivierung, da die Ausgänge dazwischen liegen müssen . Der Eingang befindet sich ebenfalls im gleichen Bereich.

Beachten Sie auch den Aufruf der Funktion fit, bevor wir mit ANNs:

model.fit(x_train, y_train)

Aber jetzt machen wir:

model.fit(x_train, x_train)

Denken Sie daran, dass die Ziele des Autoencoders mit der Eingabe identisch sind. Deshalb liefern wir die Trainingsdaten als Ziel.

Visualisierung

Lassen Sie uns nun visualisieren, wie gut unser Autoencoder seine Eingabe rekonstruiert.

Wir führen den Autoencoder auf dem Testset einfach mit der Vorhersagefunktion von Keras aus. Für jedes Bild im Testset erhalten wir die Ausgabe des Autoencoders. Wir erwarten, dass die Ausgabe der Eingabe sehr ähnlich ist.

Sie sind in der Tat ziemlich ähnlich, aber nicht genau gleich. Wir können es deutlicher in der letzten Ziffer „4“ bemerken. Da dies eine einfache Aufgabe war, hat unser Autoencoder ziemlich gut funktioniert.

Beratung

Wir haben die vollständige Kontrolle über die Architektur des Autoencoders. Wir können es sehr leistungsfähig machen, indem wir die Anzahl der Layer, Knoten pro Layer und vor allem die Codegröße erhöhen. Durch Erhöhen dieser Hyperparameter kann der Autoencoder komplexere Codierungen lernen. Aber wir sollten darauf achten, es nicht zu mächtig zu machen. Andernfalls lernt der Autoencoder einfach, seine Eingaben in die Ausgabe zu kopieren, ohne eine sinnvolle Darstellung zu lernen. Es wird nur die Identitätsfunktion nachahmen. Der Autoencoder rekonstruiert die Trainingsdaten perfekt, passt sie jedoch an, ohne sie auf neue Instanzen verallgemeinern zu können, was wir nicht wollen.

Aus diesem Grund bevorzugen wir eine „Sandwitch“ -Architektur und halten die Codegröße bewusst klein. Da die Codierungsschicht eine geringere Dimensionalität als die Eingabedaten aufweist, wird der Autoencoder als unterkomplett bezeichnet. Es wird nicht in der Lage sein, seine Eingaben direkt in die Ausgabe zu kopieren, und wird gezwungen sein, intelligente Funktionen zu lernen. Wenn die Eingabedaten ein Muster haben, zum Beispiel enthält die Ziffer „1“ normalerweise eine etwas gerade Linie und die Ziffer „0“ ist kreisförmig, es wird diese Tatsache lernen und sie in einer kompakteren Form codieren. Wenn die Eingabedaten ohne interne Korrelation oder Abhängigkeit völlig zufällig waren, kann ein unterkompletter Autoencoder sie nicht perfekt wiederherstellen. Aber zum Glück gibt es in der realen Welt viel Abhängigkeit.

Rauschunterdrückung von Autoencodern

Wenn die Codeschicht klein gehalten wurde, musste unser Autoencoder eine intelligente Darstellung der Daten erlernen. Es gibt eine andere Möglichkeit, den Autoencoder zum Erlernen nützlicher Funktionen zu zwingen, indem er seinen Eingaben zufälliges Rauschen hinzufügt und die ursprünglichen rauschfreien Daten wiederherstellt. Auf diese Weise kann der Autoencoder die Eingabe nicht einfach in seine Ausgabe kopieren, da die Eingabe auch zufälliges Rauschen enthält. Wir bitten sie, das Rauschen zu subtrahieren und die zugrunde liegenden aussagekräftigen Daten zu erzeugen. Dies wird als rauschunterdrückender Autoencoder bezeichnet.

Die obere Zeile enthält die Originalbilder. Wir fügen ihnen zufälliges Gaußsches Rauschen hinzu und die verrauschten Daten werden zur Eingabe in den Autoencoder. Der Autoencoder sieht das Originalbild überhaupt nicht. Aber dann erwarten wir, dass der Autoencoder das rauschfreie Originalbild regeneriert.

Es gibt nur einen kleinen Unterschied zwischen der Implementierung von Denoising Autoencoder und dem regulären. Die Architektur ändert sich überhaupt nicht, nur die Fit-Funktion. Wir haben den regulären Autoencoder wie folgt trainiert:

autoencoder.fit(x_train, x_train)

Denoising Autoencoder wird trainiert als:

autoencoder.fit(x_train_noisy, x_train)

So einfach ist das, alles andere ist genau das gleiche. Die Eingabe in den Autoencoder ist das verrauschte Bild, und das erwartete Ziel ist das ursprüngliche rauschfreie Bild.

Visualisierung

Lassen Sie uns nun visualisieren, ob wir die rauschfreien Bilder wiederherstellen können.

Sieht ziemlich gut aus. Die untere Zeile ist die Autoencoder-Ausgabe. Wir können es besser machen, indem wir komplexere Autoencoder-Architekturen wie Convolutional Autoencoder verwenden. Wir werden Windungen im kommenden Artikel behandeln.

Sparse Autoencoder

Wir haben zwei Möglichkeiten eingeführt, um den Autoencoder zu zwingen, nützliche Funktionen zu lernen: die Codegröße klein zu halten und Autoencoder zu entrauschen. Die dritte Methode ist die Regularisierung. Wir können den Autoencoder regularisieren, indem wir eine Sparsity-Einschränkung verwenden, sodass nur ein Bruchteil der Knoten Werte ungleich Null hat, die als aktive Knoten bezeichnet werden.

Insbesondere fügen wir der Verlustfunktion einen Strafterm hinzu, sodass nur ein Bruchteil der Knoten aktiv wird. Dies zwingt den Autoencoder, jede Eingabe als eine Kombination aus einer kleinen Anzahl von Knoten darzustellen, und fordert ihn auf, eine interessante Struktur in den Daten zu entdecken. Diese Methode funktioniert auch dann, wenn die Codegröße groß ist, da zu jedem Zeitpunkt nur eine kleine Teilmenge der Knoten aktiv ist.

Es ist ziemlich einfach, dies in Keras mit nur einem Parameter zu tun. Zur Erinnerung: Zuvor haben wir die Codeebene wie folgt erstellt:

code = Dense(code_size, activation='relu')(input_img)

Wir fügen nun einen weiteren Parameter namens activity_regularizer hinzu, indem wir die Regularisierungsstärke angeben. Dies ist normalerweise ein Wert im Bereich . Hier haben wir 10e-6 gewählt.

code = Dense(code_size, activation='relu', activity_regularizer=l1(10e-6))(input_img)

Der endgültige Verlust des Sparse-Modells ist aufgrund des hinzugefügten Regularisierungsterms um 0,01 höher als der des Standardmodells.

Zeigen wir, dass die vom regularisierten Modell generierten Codierungen tatsächlich spärlich sind. Wenn wir uns das Histogramm der Codewerte für die Bilder im Testsatz ansehen, lautet die Verteilung wie folgt:

Der Mittelwert für das Standardmodell ist 6.6, aber für das regularisierte Modell ist es 0.8, eine ziemlich große Reduktion. Wir können sehen, dass ein großer Teil der Codewerte im regularisierten Modell tatsächlich 0 ist, was wir wollten. Die Varianz des regularisierten Modells ist ebenfalls relativ gering.

Anwendungsfälle

Nun könnten wir die folgenden Fragen stellen. Wie gut können Autoencoder die Eingabe komprimieren? Und sind sie eine häufig verwendete Deep-Learning-Technik?

Leider sind Autoencoder in realen Anwendungen nicht weit verbreitet. Als Komprimierungsmethode sind sie nicht besser als ihre Alternativen, zum Beispiel macht jpeg die Fotokomprimierung besser als ein Autoencoder. Und die Tatsache, dass Autoencoder datenspezifisch sind, macht sie als allgemeine Technik unpraktisch. Sie haben jedoch 3 häufige Anwendungsfälle:

  • Datenrauschen: Wir haben ein Beispiel dafür auf Bildern gesehen.
  • Reduzierung der Dimensionalität: Die Visualisierung hochdimensionaler Daten ist eine Herausforderung. t-SNE ist die am häufigsten verwendete Methode, hat jedoch mit einer großen Anzahl von Dimensionen zu kämpfen (typischerweise über 32). Daher werden Autoencoder als Vorverarbeitungsschritt verwendet, um die Dimensionalität zu reduzieren, und diese komprimierte Darstellung wird von t-SNE verwendet, um die Daten im 2D-Raum zu visualisieren. Für großartige Artikel über t-SNE siehe hier und hier .
  • Variational Autoencoder (VAE): Dies ist ein moderner und komplexer Anwendungsfall von Autoencodern, den wir in einem anderen Artikel behandeln werden. Als kurze Zusammenfassung lernt VAE jedoch die Parameter der Wahrscheinlichkeitsverteilung aus den Eingabedaten, anstatt im Fall von Vanille-Autoencodern eine beliebige Funktion zu lernen. Durch Abtastpunkte aus dieser Verteilung können wir die VAE auch als generatives Modell verwenden. Hier ist eine gute Referenz.

Fazit

Autoencoder sind eine sehr nützliche Dimensionalitätsreduktionstechnik. Sie sind sehr beliebt als Lehrmaterial in einführenden Deep-Learning-Kursen, höchstwahrscheinlich aufgrund ihrer Einfachheit. In diesem Artikel haben wir sie ausführlich behandelt und ich hoffe, es hat Ihnen gefallen.

Der gesamte Code für diesen Artikel ist hier verfügbar, wenn Sie selbst hacken möchten. Wenn Sie Feedback haben, können Sie mich gerne auf Twitter erreichen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

Previous post New England Diary
Next post Ein globaler Leitfaden zum Trinkgeld beim Essen