Ich möchte besser verstehen, warum sich LSTM über einen längeren Zeitraum an Informationen erinnern kann als Vanille / einfaches wiederkehrendes neuronales Netzwerk (SRNN), indem ich ein Experiment aus dem Artikel Lernen von Langzeitabhängigkeiten mit Gradientenabstieg von Bengio et al. 1994 .
Siehe Abb. 1. und 2 auf diesem Papier. Die Aufgabe ist in einer bestimmten Reihenfolge einfach. Wenn sie mit einem hohen Wert (z. B. 1) beginnt, lautet die Ausgabebezeichnung 1; Wenn es mit einem niedrigen Wert beginnt (z. B. -1), ist das Ausgabeetikett 0. Die Mitte ist Rauschen. Diese Aufgabe wird als Informations-Latching bezeichnet, da sich das Modell beim Durchlaufen des mittleren Rauschens den Startwert merken muss, um ein korrektes Etikett auszugeben. Es wurde ein einzelnes Neuron-RNN verwendet, um ein Modell zu erstellen, das ein solches Verhalten aufweist. Die Abbildung 2 (b) zeigt die Ergebnisse, und die Erfolgshäufigkeit des Trainings eines solchen Modells nimmt mit zunehmender Sequenzlänge dramatisch ab. Es gab kein Ergebnis für LSTM, da es 1994 noch nicht erfunden wurde.
Ich werde also neugierig und würde gerne sehen, ob LSTM für eine solche Aufgabe tatsächlich eine bessere Leistung erbringt. In ähnlicher Weise konstruierte ich ein einzelnes Neuron-RNN sowohl für Vanille- als auch für LSTM-Zellen, um das Informations-Latching zu modellieren. Überraschenderweise stellte ich fest, dass LSTM schlechter abschneidet, und ich weiß nicht warum. Könnte mir jemand helfen zu erklären oder wenn etwas mit meinem Code nicht stimmt, bitte?
Hier ist mein Ergebnis:
Hier ist mein Code:
import matplotlib.pyplot as plt
import numpy as np
from keras.models import Model
from keras.layers import Input, LSTM, Dense, SimpleRNN
N = 10000
num_repeats = 30
num_epochs = 5
# sequence length options
lens = [2, 5, 8, 10, 15, 20, 25, 30] + np.arange(30, 210, 10).tolist()
res = {}
for (RNN_CELL, key) in zip([SimpleRNN, LSTM], ['srnn', 'lstm']):
res[key] = {}
print(key, end=': ')
for seq_len in lens:
print(seq_len, end=',')
xs = np.zeros((N, seq_len))
ys = np.zeros(N)
# construct input data
positive_indexes = np.arange(N // 2)
negative_indexes = np.arange(N // 2, N)
xs[positive_indexes, 0] = 1
ys[positive_indexes] = 1
xs[negative_indexes, 0] = -1
ys[negative_indexes] = 0
noise = np.random.normal(loc=0, scale=0.1, size=(N, seq_len))
train_xs = (xs + noise).reshape(N, seq_len, 1)
train_ys = ys
# repeat each experiments multiple times
hists = []
for i in range(num_repeats):
inputs = Input(shape=(None, 1), name='input')
rnn = RNN_CELL(1, input_shape=(None, 1), name='rnn')(inputs)
out = Dense(2, activation='softmax', name='output')(rnn)
model = Model(inputs, out)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
hist = model.fit(train_xs, train_ys, epochs=num_epochs, shuffle=True, validation_split=0.2, batch_size=16, verbose=0)
hists.append(hist.history['val_acc'][-1])
res[key][seq_len] = hists
print()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(pd.DataFrame.from_dict(res['lstm']).mean(), label='lstm')
ax.plot(pd.DataFrame.from_dict(res['srnn']).mean(), label='srnn')
ax.legend()
Ich habe auch das Ergebnis im Notizbuch angezeigt , was praktisch wäre, wenn Sie die Ergebnisse replizieren möchten. Es dauerte über einen Tag, bis das Experiment auf meinem Computer nur mit CPU ausgeführt wurde. Auf einem GPU-fähigen Computer könnte es schneller sein.
Update 2018-04-18 :
Ich habe versucht, eine Figur in der Landschaft von RNN zu reproduzieren, die von Abbildung 6 in Über die Schwierigkeit des Trainings wiederkehrender neuronaler Netze inspiriert ist . Ich finde es interessant, die Bildung von Klippen in der Verlustlandschaft zu sehen, wenn die Anzahl der Wiederholungen / Zeitschritte / Sequenzlängen zunimmt, was mit der Erklärung der hier beobachteten Schwierigkeit des Trainings langer Sequenzen zusammenhängen könnte. Weitere Details finden Sie hier .
Update 2018-04-19
@ Shimaos Experiment erweitern. Es scheint, dass LSTM und GRU einfach nicht so gut darin sind, Informationen zu erfassen. Wenn Sie jedoch zu einer anderen Aufgabe wechseln, die ich als Bit-Relay bezeichne (@ shimaos Aufgabe 2), ist die GRU besser, während SRNN und LSTM gleich schlecht sind.
Jetzt neige ich dazu zu glauben, dass die Leistung eines Zelltyps aufgabenspezifisch sein könnte.
Aufgabe 1: Informationsspeicherung (1 Einheit; 10 Wiederholungen; 10 Epochen)
Aufgabe 2: Bitrelais (8 Einheiten; 10 Wiederholungen; 10 Epochen)
Fehlerbalken sind Standardabweichungen.
Eine interessante Frage ist dann, warum LSTM beim Latching von Informationen nicht funktioniert. Angesichts der Einfachheit der Aufgabe sollte sie funktionieren können, nicht wahr? Könnte in Bezug auf die Steigungen mit der Landschaft (z. B. Klippen) zusammenhängen.
quelle
Antworten:
Es gibt einen Fehler in Ihrem Code, da die erste Hälfte Ihrer konstruierten Beispiele positiv und der Rest negativ ist, aber Keras nicht mischen, bevor die Daten in Zug und Wert aufgeteilt werden, was bedeutet, dass der gesamte Wertesatz negativ ist und der Das Zugset ist auf positiv ausgerichtet, weshalb Sie seltsame Ergebnisse wie die Genauigkeit 0 (schlechter als der Zufall) erhalten haben.
Außerdem habe ich einige Parameter (wie Lernrate, Anzahl der Epochen und Stapelgröße) angepasst, um sicherzustellen, dass das Training immer konvergiert.
Schließlich habe ich nur 5 und 100 Zeitschritte ausgeführt, um Rechenaufwand zu sparen.
Seltsamerweise trainiert das LSTM nicht richtig, obwohl das GRU fast so gut funktioniert wie das RNN.
Ich habe eine etwas schwierigere Aufgabe ausprobiert: In positiven Sequenzen ist das Vorzeichen des ersten Elements und eines Elements in der Mitte der Sequenz das gleiche (beide +1 oder beide -1), in negativen Sequenzen sind die Vorzeichen unterschiedlich. Ich hatte gehofft, dass die zusätzliche Speicherzelle im LSTM hier davon profitieren würde
Es hat besser funktioniert als RNN, aber nur am Rande, und die GRU gewinnt aus irgendeinem Grund.
Ich habe keine vollständige Antwort darauf, warum das RNN bei der einfachen Aufgabe besser abschneidet als das LSTM. Ich denke, es muss sein, dass wir nicht die richtigen Hyperparameter gefunden haben, um das LSTM richtig zu trainieren, zusätzlich zu der Tatsache, dass das Problem für ein einfaches RNN einfach ist. Möglicherweise neigt ein Modell mit so wenigen Parametern auch eher dazu, im lokalen Minimum hängen zu bleiben.
Der geänderte Code
quelle
Ich weiß nicht, ob es einen Unterschied machen wird, aber ich würde verwenden:
out = Dense (1, activation='sigmoid', ...
und
model.compile(loss='binary_crossentropy', ...
für ein binäres Klassifizierungsproblem.
quelle