Essentials of Deep Learning : Introduction to Long Short Term Memory

Introduction

Sequence prediction problems have been around for a long time. Sie gelten als eines der am schwierigsten zu lösenden Probleme in der Data-Science-Branche. Sie umfassen ein breites Spektrum von Problemen: von der Vorhersage von Verkaufszahlen bis zum Auffinden von Mustern in Börsendaten, vom Verstehen von Filmplots bis zur Erkennung Ihrer Sprechweise, von Sprachübersetzungen bis zur Vorhersage Ihres nächsten Wortes auf der Tastatur Ihres iPhones.

Mit den jüngsten Durchbrüchen in der Datenwissenschaft hat sich gezeigt, dass für fast alle diese Sequenzvorhersage-Probleme Long Short Term Memory-Netzwerke, auch bekannt als LSTMs, die effektivste Lösung darstellen.

LSTMs haben in vielerlei Hinsicht einen Vorteil gegenüber konventionellen neuronalen Feed-Forward-Netzwerken und RNN. Dies liegt an ihrer Eigenschaft, sich selektiv Muster über lange Zeiträume hinweg zu merken. Der Zweck dieses Artikels ist es, LSTM zu erklären und Sie in die Lage zu versetzen, sie in realen Problemen zu verwenden. Schauen wir es uns an!

Hinweis: Um diesen Artikel zu lesen, müssen Sie Grundkenntnisse über neuronale Netze haben und wissen, wie Keras (eine Deep-Learning-Bibliothek) funktioniert. Sie können die genannten Artikel lesen, um diese Konzepte zu verstehen:

  • Neuronale Netzwerke von Grund auf verstehen
  • Grundlagen des Deep Learning – Einführung in rekurrente neuronale Netzwerke
  • Tutorial: Optimierung neuronaler Netze mit Keras (mit Fallstudie zur Bilderkennung)

Inhaltsverzeichnis

  1. Flashback: Ein Blick in rekurrente neuronale Netze (RNN)
  2. Grenzen von RNNs
  3. Verbesserungen gegenüber RNN : Long Short Term Memory (LSTM)
  4. Architektur von LSTM
    1. Vergissene Gatter
    2. Eingangsgatter
    3. Ausgangsgatter
  5. Textgenerierung mit LSTMs.

Flashback: Ein Blick in rekurrente neuronale Netze (RNN)

Nehmen wir ein Beispiel für sequentielle Daten, z. B. die Daten des Aktienmarktes für eine bestimmte Aktie. Ein einfaches maschinelles Lernmodell oder ein künstliches neuronales Netz kann lernen, die Aktienkurse auf der Grundlage einer Reihe von Merkmalen vorherzusagen: dem Volumen der Aktie, dem Eröffnungswert usw. Während der Aktienkurs von diesen Merkmalen abhängt, ist er auch in hohem Maße von den Aktienwerten der vorangegangenen Tage abhängig. Für einen Händler sind diese Werte der vorangegangenen Tage (oder der Trend) ein entscheidender Faktor für Vorhersagen.

In herkömmlichen neuronalen Feed-Forward-Netzen werden alle Testfälle als unabhängig betrachtet. Das heißt, bei der Anpassung des Modells für einen bestimmten Tag werden die Aktienkurse der vorangegangenen Tage nicht berücksichtigt.

Diese Zeitabhängigkeit wird durch rekurrente neuronale Netze erreicht. Ein typisches RNN sieht wie folgt aus:

Das mag auf den ersten Blick einschüchternd wirken, aber einmal aufgefaltet, sieht es viel einfacher aus:

Jetzt ist es für uns einfacher zu visualisieren, wie diese Netze den Trend der Aktienkurse berücksichtigen, bevor sie die Aktienkurse für heute vorhersagen. Hier ist jede Vorhersage zum Zeitpunkt t (h_t) abhängig von allen vorherigen Vorhersagen und den daraus gelernten Informationen.

RNNs können unseren Zweck der Sequenzverarbeitung zu einem großen Teil, aber nicht vollständig erfüllen. Wir wollen, dass unsere Computer gut genug sind, um Shakespeare-Sonette zu schreiben. RNNs sind großartig, wenn es um kurze Zusammenhänge geht, aber um eine Geschichte aufzubauen und sich daran zu erinnern, müssen unsere Modelle in der Lage sein, den Kontext hinter den Sequenzen zu verstehen und sich daran zu erinnern, genau wie ein menschliches Gehirn. Das ist mit einem einfachen RNN nicht möglich.

Warum? Schauen wir uns das mal an.

Grenzen von RNNs

Rekurrente neuronale Netze funktionieren gut, wenn wir mit kurzfristigen Abhängigkeiten zu tun haben. Das heißt, wenn sie auf Probleme angewendet werden wie:

RNNs erweisen sich als recht effektiv. Das liegt daran, dass dieses Problem nichts mit dem Kontext der Aussage zu tun hat. Das RNN muss sich nicht daran erinnern, was vor dieser Aussage gesagt wurde oder welche Bedeutung sie hatte, es muss nur wissen, dass der Himmel in den meisten Fällen blau ist. Die Vorhersage würde also lauten:

Vanille RNNs verstehen jedoch den Kontext hinter einer Eingabe nicht. An etwas, das vor langer Zeit gesagt wurde, kann man sich nicht erinnern, wenn man in der Gegenwart Vorhersagen macht. Verstehen wir dies als Beispiel:

Da der Autor 20 Jahre lang in Spanien gearbeitet hat, ist es sehr wahrscheinlich, dass er die spanische Sprache gut beherrscht. Um jedoch eine korrekte Vorhersage zu treffen, muss sich das RNN diesen Kontext merken. Die relevanten Informationen können von dem Punkt, an dem sie benötigt werden, durch eine große Menge irrelevanter Daten getrennt sein. An dieser Stelle versagt ein rekurrentes neuronales Netz!

Der Grund hierfür ist das Problem des verschwindenden Gradienten. Um das zu verstehen, muss man wissen, wie ein neuronales Feed-Forward-Netz lernt. Wir wissen, dass bei einem herkömmlichen neuronalen Feed-Forward-Netz die Gewichtsaktualisierung, die auf eine bestimmte Schicht angewendet wird, ein Vielfaches der Lernrate, des Fehlerterms der vorherigen Schicht und der Eingabe in diese Schicht ist. Der Fehlerterm für eine bestimmte Schicht ist also irgendwo ein Produkt aus den Fehlern aller vorherigen Schichten. Bei Aktivierungsfunktionen wie der Sigmoidfunktion werden die kleinen Werte ihrer Ableitungen (die in der Fehlerfunktion vorkommen) auf dem Weg zu den Anfangsschichten mehrfach multipliziert. Dies hat zur Folge, dass der Gradient in Richtung der Anfangsschichten fast verschwindet und es schwierig wird, diese Schichten zu trainieren.

Ein ähnlicher Fall wird bei rekurrenten neuronalen Netzen beobachtet. RNN merken sich Dinge nur für eine kurze Zeitspanne, d.h. wenn wir die Information nach einer kurzen Zeit benötigen, kann sie reproduzierbar sein, aber sobald eine große Anzahl von Wörtern eingegeben wird, geht diese Information irgendwo verloren. Dieses Problem kann durch die Anwendung einer leicht abgewandelten Version von RNNs gelöst werden – den Long Short-Term Memory Networks.

Verbesserung gegenüber RNN: LSTM (Long Short-Term Memory) Networks

Wenn wir unseren Kalender für den Tag zusammenstellen, ordnen wir unsere Termine nach Prioritäten, richtig? Wenn wir für etwas Wichtiges Platz schaffen müssen, wissen wir, welche Besprechung abgesagt werden könnte, um einer möglichen Besprechung Platz zu machen.

Es stellt sich heraus, dass ein RNN dies nicht tut. Um eine neue Information hinzuzufügen, transformiert es die bestehende Information vollständig, indem es eine Funktion anwendet. Dadurch wird die gesamte Information im Ganzen verändert, d. h. es gibt keine Rücksicht auf „wichtige“ und „weniger wichtige“ Informationen.

LSTMs hingegen nehmen kleine Veränderungen an der Information durch Multiplikationen und Additionen vor. Bei LSTMs fließen die Informationen durch einen Mechanismus, der als Zellzustände bekannt ist. Auf diese Weise können sich LSTMs selektiv an Dinge erinnern oder sie vergessen. Die Informationen in einem bestimmten Zellzustand haben drei verschiedene Abhängigkeiten.

Wir wollen dies anhand eines Beispiels veranschaulichen. Nehmen wir das Beispiel der Vorhersage von Aktienkursen für eine bestimmte Aktie. Der Aktienkurs von heute wird abhängen von:

  1. Dem Trend, dem die Aktie in den vergangenen Tagen gefolgt ist, vielleicht einem Abwärtstrend oder einem Aufwärtstrend.
  2. Dem Preis der Aktie am Vortag, denn viele Händler vergleichen den Vortagespreis der Aktie, bevor sie sie kaufen.
  3. Den Faktoren, die den Preis der Aktie für heute beeinflussen können. Das kann eine neue Unternehmenspolitik sein, die weithin kritisiert wird, oder ein Rückgang des Gewinns des Unternehmens, oder vielleicht ein unerwarteter Wechsel in der Führungsspitze des Unternehmens.

Diese Abhängigkeiten können auf jedes beliebige Problem verallgemeinert werden als:

  1. Der vorherige Zellzustand (d.h. die Information, die nach dem vorherigen Zeitschritt im Speicher vorhanden war)
  2. Der vorherige verborgene Zustand (d.h. das ist derselbe wie die Ausgabe der vorherigen Zelle)
  3. Die Eingabe im aktuellen Zeitschritt (d.h. die neue Information, die in diesem Moment eingespeist wird)

Ein weiteres wichtiges Merkmal des LSTM ist seine Analogie mit Förderbändern!

Das ist richtig!

Industrien benutzen sie, um Produkte für verschiedene Prozesse zu bewegen. LSTMs nutzen diesen Mechanismus, um Informationen zu transportieren.

Während die Informationen durch die verschiedenen Schichten fließen, können sie hinzugefügt, verändert oder entfernt werden, so wie ein Produkt geformt, lackiert oder verpackt werden kann, während es sich auf einem Förderband befindet.

Das folgende Diagramm erklärt die enge Verwandtschaft von LSTMs und Fließbändern.

Quelle

Obwohl dieses Diagramm nicht einmal annähernd der tatsächlichen Architektur eines LSTMs entspricht, erfüllt es vorerst unseren Zweck.

Gerade wegen dieser Eigenschaft von LSTMs, bei der sie nicht die gesamte Information manipulieren, sondern sie nur geringfügig verändern, sind sie in der Lage, Dinge selektiv zu vergessen und zu erinnern. Wie sie das tun, werden wir im nächsten Abschnitt erfahren…

Architektur von LSTMs

Die Funktionsweise von LSTMs lässt sich anhand der Arbeitsweise des Teams eines Nachrichtensenders veranschaulichen, das über einen Mordfall berichtet. Eine Nachrichtenstory besteht aus Fakten, Beweisen und Aussagen vieler Menschen. Jedes Mal, wenn ein neues Ereignis eintritt, wird einer der drei Schritte unternommen.

Angenommen, wir gingen davon aus, dass der Mord durch „Vergiftung“ des Opfers begangen wurde, aber der gerade eingetroffene Autopsiebericht besagt, dass die Todesursache „ein Schlag auf den Kopf“ war. Was macht man als Mitglied dieses Nachrichtenteams? Man vergisst sofort die vorherige Todesursache und alle Geschichten, die sich um diese Tatsache ranken.

Was, wenn ein völlig neuer Verdächtiger ins Spiel kommt. Eine Person, die einen Groll gegen das Opfer hegte und der Mörder sein könnte? Sie geben diese Information in Ihren News-Feed ein, richtig?

Nun können all diese bruchstückhaften Informationen nicht in den Mainstream-Medien verbreitet werden. Also müssen Sie diese Informationen nach einem bestimmten Zeitintervall zusammenfassen und die relevanten Dinge an Ihr Publikum ausgeben. Vielleicht in der Form „XYZ entpuppt sich als der Hauptverdächtige“.

Lassen Sie uns nun in die Details der Architektur des LSTM-Netzes einsteigen:

Quelle

Nun, das ist nicht annähernd die vereinfachte Version, die wir zuvor gesehen haben, aber lassen Sie mich es Ihnen erklären. Ein typisches LSTM-Netzwerk besteht aus verschiedenen Speicherblöcken, die Zellen
genannt werden (die Rechtecke, die wir im Bild sehen). Es gibt zwei Zustände, die an die nächste Zelle weitergegeben werden: den Zellzustand und den verborgenen Zustand. Die Speicherblöcke sind dafür verantwortlich, sich an Dinge zu erinnern, und die Manipulationen an diesem Speicher erfolgen durch drei Hauptmechanismen, die Gates genannt werden. Jeder von ihnen wird im Folgenden besprochen.

4.1 Forget Gate

Nehmen wir ein Beispiel für ein Textvorhersageproblem. Nehmen wir an, ein LSTM wird mit dem folgenden Satz gefüttert:

Sobald der erste Punkt nach „Person“ auftaucht, erkennt das Forget Gate, dass es im nächsten Satz einen Kontextwechsel geben könnte. Das hat zur Folge, dass das Subjekt des Satzes vergessen wird und der Platz für das Subjekt frei wird. Und wenn wir anfangen, über „Dan“ zu sprechen, wird diese Position des Subjekts an „Dan“ vergeben. Dieser Prozess des Vergessens des Subjekts wird durch das Vergessens-Tor bewirkt.

Ein Vergessens-Tor ist dafür zuständig, Informationen aus dem Zellzustand zu entfernen. Die Informationen, die für das Verständnis des LSTM nicht mehr benötigt werden oder die Informationen, die weniger wichtig sind, werden durch Multiplikation eines Filters entfernt. Dies ist notwendig, um die Leistung des LSTM-Netzes zu optimieren.

Dieses Gatter nimmt zwei Eingaben auf: h_t-1 und x_t.

h_t-1 ist der verborgene Zustand der vorherigen Zelle oder die Ausgabe der vorherigen Zelle und x_t ist die Eingabe in diesem bestimmten Zeitschritt. Die gegebenen Eingaben werden mit den Gewichtsmatrizen multipliziert und ein Bias wird hinzugefügt. Anschließend wird die Sigmoidfunktion auf diesen Wert angewandt. Die Sigmoidfunktion gibt einen Vektor aus, dessen Werte von 0 bis 1 reichen und der jeder Zahl im Zellzustand entspricht. Im Grunde genommen ist die Sigmoidfunktion dafür verantwortlich, zu entscheiden, welche Werte beibehalten und welche verworfen werden sollen. Wird für einen bestimmten Wert im Zellzustand eine „0“ ausgegeben, so bedeutet dies, dass der Zellzustand diese Information vollständig vergessen soll. Eine „1“ bedeutet, dass das „forget gate“ die gesamte Information speichern will. Dieser Ausgangsvektor der Sigmoidfunktion wird mit dem Zellstatus multipliziert.

4.2 Input Gate

Okay, nehmen wir ein weiteres Beispiel, bei dem das LSTM einen Satz analysiert:

Nun ist die wichtige Information hier, dass „Bob“ schwimmen kann und dass er vier Jahre bei der Marine gedient hat. Die Tatsache, dass er dies alles am Telefon erzählt hat, ist jedoch weniger wichtig und kann ignoriert werden. Dieser Prozess des Hinzufügens neuer Informationen kann über das Eingangsgatter erfolgen.

Hier ist seine Struktur:

Das Eingangsgatter ist für das Hinzufügen von Informationen zum Zellzustand verantwortlich. Diese Hinzufügung von Informationen erfolgt im Wesentlichen in drei Schritten, wie aus dem obigen Diagramm ersichtlich ist.

  1. Die Regelung, welche Werte zum Zellzustand hinzugefügt werden müssen, erfolgt durch eine Sigmoidfunktion. Dies ist im Grunde genommen dem Vergessenstor sehr ähnlich und fungiert als Filter für alle Informationen von h_t-1 und x_t.
  2. Erstellen eines Vektors, der alle möglichen Werte enthält, die dem Zellzustand hinzugefügt werden können (wie von h_t-1 und x_t wahrgenommen). Dies geschieht mit Hilfe der tanh-Funktion, die Werte von -1 bis +1 ausgibt.
  3. Multiplizieren des Wertes des Regulierungsfilters (des Sigmoid-Gatters) mit dem erzeugten Vektor (der tanh-Funktion) und anschließendes Hinzufügen dieser nützlichen Information zum Zellzustand durch eine Additionsoperation.

Nach diesem dreistufigen Prozess wird sichergestellt, dass nur die Information zum Zellzustand hinzugefügt wird, die wichtig und nicht redundant ist.

4.3 Output Gate

Nicht alle Informationen, die den Zellzustand durchlaufen, sind geeignet, zu einem bestimmten Zeitpunkt ausgegeben zu werden. Wir wollen das an einem Beispiel verdeutlichen:

In diesem Satz könnte es eine Reihe von Möglichkeiten für den leeren Platz geben. Wir wissen jedoch, dass das derzeitige Wort „tapfer“ ein Adjektiv ist, das zur Beschreibung eines Substantivs verwendet wird. Das nachfolgende Wort hat also eine starke Tendenz, ein Substantiv zu sein. Und somit könnte Bob eine geeignete Ausgabe sein.

Diese Aufgabe, nützliche Informationen aus dem aktuellen Zellzustand auszuwählen und sie als Ausgabe auszugeben, wird durch das Ausgangsgatter erledigt. Hier ist seine Struktur:

Die Funktionsweise eines Ausgangsgatters lässt sich wiederum auf drei Schritte herunterbrechen:

  1. Erstellung eines Vektors nach Anwendung der tanh-Funktion auf den Zellzustand, wodurch die Werte auf den Bereich -1 bis +1 skaliert werden.
  2. Erstellung eines Filters unter Verwendung der Werte von h_t-1 und x_t, so dass er die Werte regulieren kann, die aus dem oben erstellten Vektor ausgegeben werden müssen. Dieser Filter verwendet wiederum eine Sigmoidfunktion.
  3. Multiplizieren Sie den Wert dieses Regulierungsfilters mit dem in Schritt 1 erstellten Vektor und senden Sie ihn als Ausgabe und auch an den verborgenen Zustand der nächsten Zelle.

Der Filter im obigen Beispiel wird sicherstellen, dass er alle anderen Werte außer „Bob“ verringert. Der Filter muss also auf den Eingabe- und versteckten Zustandswerten aufgebaut und auf den Zellzustandsvektor angewendet werden.

Texterzeugung mit LSTMs

Wir haben genug von den theoretischen Konzepten und der Funktionsweise von LSTMs. Jetzt würden wir versuchen, ein Modell zu erstellen, das eine Anzahl von n Zeichen nach dem Originaltext von Macbeth vorhersagen kann. Die meisten der klassischen Texte sind nicht mehr urheberrechtlich geschützt und können hier gefunden werden. Eine aktualisierte Version der .txt-Datei finden Sie hier.

Wir werden die Bibliothek Keras verwenden, die eine High-Level-API für neuronale Netze ist und auf TensorFlow oder Theano aufbaut. Stellen Sie also sicher, dass Sie Keras installiert und funktionsfähig haben, bevor Sie in diesen Code eintauchen.

Okay, also lasst uns etwas Text generieren!

  • Importieren von Abhängigkeiten

# Importing dependencies numpy and kerasimport numpyfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropoutfrom keras.layers import LSTMfrom keras.utils import np_utils

Wir importieren alle benötigten Abhängigkeiten und das ist so ziemlich selbsterklärend.

  • Laden der Textdatei und Erstellen der Zuordnungen von Zeichen zu ganzen Zahlen

# load textfilename = "/macbeth.txt"text = (open(filename).read()).lower()# mapping characters with integersunique_chars = sorted(list(set(text)))char_to_int = {}int_to_char = {}for i, c in enumerate (unique_chars): char_to_int.update({c: i}) int_to_char.update({i: c})

Die Textdatei ist geöffnet, und alle Zeichen sind in Kleinbuchstaben umgewandelt. Um die folgenden Schritte zu erleichtern, werden wir jedes Zeichen einer entsprechenden Zahl zuordnen. Dies geschieht, um den Berechnungsteil des LSTM zu vereinfachen.

  • Vorbereitung des Datensatzes

# preparing input and output datasetX = Y = for i in range(0, len(text) - 50, 1): sequence = text label =text X.append( for char in sequence]) Y.append(char_to_int)

Die Daten werden in einem solchen Format vorbereitet, dass wir, wenn wir wollen, dass das LSTM das „O“ in „HELLO“ vorhersagt, die Eingabe und die erwartete Ausgabe eingeben. In ähnlicher Weise legen wir hier die Länge der gewünschten Sequenz fest (im Beispiel auf 50 gesetzt) und speichern dann die Kodierungen der ersten 49 Zeichen in X und die erwartete Ausgabe, d. h. das 50.

  • Umformung von X

# reshaping, normalizing and one hot encodingX_modified = numpy.reshape(X, (len(X), 50, 1))X_modified = X_modified / float(len(unique_chars))Y_modified = np_utils.to_categorical(Y)

Ein LSTM-Netzwerk erwartet die Eingabe in der Form, in der samples die Anzahl der Datenpunkte ist, die wir haben, time steps ist die Anzahl der zeitabhängigen Schritte, die in einem einzelnen Datenpunkt enthalten sind, features bezieht sich auf die Anzahl der Variablen, die wir für den entsprechenden wahren Wert in Y haben. Wir skalieren dann die Werte in X_modifiziert zwischen 0 und 1 und kodieren unsere wahren Werte in Y_modifiziert.

  • Definieren des LSTM-Modells

# defining the LSTM modelmodel = Sequential()model.add(LSTM(300, input_shape=(X_modified.shape, X_modified.shape), return_sequences=True))model.add(Dropout(0.2))model.add(LSTM(300))model.add(Dropout(0.2))model.add(Dense(Y_modified.shape, activation='softmax'))model.compile(loss='categorical_crossentropy', optimizer='adam')

Ein sequentielles Modell, das ein linearer Stapel von Schichten ist, wird verwendet. Die erste Schicht ist eine LSTM-Schicht mit 300 Speichereinheiten und gibt Sequenzen zurück. Damit soll sichergestellt werden, dass die nächste LSTM-Schicht Sequenzen und nicht nur zufällig verstreute Daten erhält. Nach jeder LSTM-Schicht wird eine Dropout-Schicht eingefügt, um eine Überanpassung des Modells zu vermeiden. Die letzte Schicht schließlich ist eine voll verknüpfte Schicht mit einer „Softmax“-Aktivierung und Neuronen, die der Anzahl der eindeutigen Zeichen entsprechen, da wir ein heiß kodiertes Ergebnis ausgeben müssen.

  • Anpassen des Modells und Erzeugen von Zeichen

# fitting the modelmodel.fit(X_modified, Y_modified, epochs=1, batch_size=30)# picking a random seedstart_index = numpy.random.randint(0, len(X)-1)new_string = X# generating charactersfor i in range(50): x = numpy.reshape(new_string, (1, len(new_string), 1)) x = x / float(len(unique_chars)) #predicting pred_index = numpy.argmax(model.predict(x, verbose=0)) char_out = int_to_char seq_in = for value in new_string] print(char_out) new_string.append(pred_index) new_string = new_string

Das Modell wird über 100 Epochen angepasst, mit einer Stapelgröße von 30. Dann legen wir einen zufälligen Seed fest (zur einfachen Reproduzierbarkeit) und beginnen mit der Erzeugung von Zeichen. Die Vorhersage des Modells gibt die Zeichenkodierung des vorhergesagten Zeichens aus, die dann in den Zeichenwert zurückdekodiert und an das Muster angehängt wird.

So würde die Ausgabe des Netzes aussehen

Nach genügend Trainingsepochen wird es mit der Zeit immer bessere Ergebnisse liefern. So würde man LSTM verwenden, um eine Sequenzvorhersageaufgabe zu lösen.

Anmerkungen zum Schluss

LSTMs sind eine sehr vielversprechende Lösung für Probleme im Zusammenhang mit Sequenzen und Zeitreihen. Der einzige Nachteil, den ich bei ihnen sehe, ist die Schwierigkeit, sie zu trainieren. Das Training selbst eines einfachen Modells erfordert viel Zeit und Systemressourcen. Aber das ist nur eine Hardware-Beschränkung! Ich hoffe, dass es mir gelungen ist, Ihnen ein grundlegendes Verständnis dieser Netze zu vermitteln. Bei Problemen oder Fragen im Zusammenhang mit diesem Blog können Sie gerne unten einen Kommentar abgeben.

Lernen, engagieren, hacken und angestellt werden!

Schreibe einen Kommentar

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