Ich versuche, mein Verständnis von LSTMs in Einklang zu bringen und wies hier in diesem Beitrag von Christopher Olah darauf hin, der in Keras implementiert ist. Ich verfolge den Blog von Jason Brownlee für das Keras-Tutorial. Was mich hauptsächlich verwirrt ist,
[samples, time steps, features]
UndKonzentrieren wir uns auf die beiden obigen Fragen unter Bezugnahme auf den unten eingefügten Code:
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], look_back, 1))
testX = numpy.reshape(testX, (testX.shape[0], look_back, 1))
########################
# The IMPORTANT BIT
##########################
# create and fit the LSTM network
batch_size = 1
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(100):
model.fit(trainX, trainY, nb_Epoch=1, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
Hinweis: create_dataset verwendet eine Sequenz der Länge N und gibt ein N-look_back
- Array zurück, von dem jedes Element eine Sequenz der Länge look_back
Ist.
Wie zu sehen ist, ist TrainX ein 3-D-Array, wobei Time_steps und Feature die letzten beiden Dimensionen sind (3 und 1 in diesem speziellen Code). Bedeutet dies in Bezug auf das folgende Bild, dass wir den Fall many to one
In Betracht ziehen, in dem die Anzahl der rosa Kästchen 3 beträgt? Oder bedeutet dies wörtlich, dass die Kettenlänge 3 beträgt (d. H. Nur 3 grüne Kästchen berücksichtigt).
Wird das Argument features relevant, wenn wir multivariate Reihen betrachten? z.B. Modellierung von zwei Finanzwerten gleichzeitig?
Bedeuten zustandsbehaftete LSTMs, dass wir die Zellenspeicherwerte zwischen Chargenläufen speichern? In diesem Fall ist batch_size
Eins und der Speicher wird zwischen den Trainingsläufen zurückgesetzt. Ich vermute, das hängt damit zusammen, dass die Trainingsdaten nicht gemischt werden, aber ich weiß nicht, wie.
Irgendwelche Gedanken? Bildreferenz: http://karpathy.github.io/2015/05/21/rnn-effectiveness/
Ein bisschen verwirrt über @ vans Kommentar, dass die roten und grünen Kästchen gleich sind. Entsprechen die folgenden API-Aufrufe den entrollten Diagrammen, um nur zu bestätigen? Insbesondere das zweite Diagramm (batch_size
Wurde willkürlich ausgewählt.):
Informationen zu Personen, die Udacitys Deep Learning-Kurs absolviert haben und sich noch immer nicht mit dem Argument time_step auskennen, finden Sie in der folgenden Diskussion: https://discussions.udacity.com/t/rnn-lstm-use- Implementierung/163169
Es stellte sich heraus, dass model.add(TimeDistributed(Dense(vocab_len)))
das war, wonach ich gesucht hatte. Hier ist ein Beispiel: https://github.com/sachinruk/ShakespeareBot
Ich habe das meiste meines Verständnisses von LSTMs hier zusammengefasst: https://www.youtube.com/watch?v=ywinX5wgdEU
Zunächst wählen Sie großartige Tutorials aus ( 1 , 2 ), um zu beginnen.
Was Zeitschritt bedeutet : Time-steps==3
In X.shape (Beschreibung der Datenform) bedeutet, dass es drei rosa Kästchen gibt. Da in Keras jeder Schritt eine Eingabe erfordert, sollte die Anzahl der grünen Kästchen in der Regel der Anzahl der roten Kästchen entsprechen. Es sei denn, Sie hacken die Struktur.
viele zu viele gegen viele zu eins : In Keras gibt es einen return_sequences
- Parameter, wenn Sie LSTM
oder GRU
oder SimpleRNN
. Wenn return_sequences
False
ist (Standardeinstellung), dann sind es viele zu eins , wie in der Abbildung gezeigt. Die Rückgabeform ist (batch_size, hidden_unit_length)
, Die den letzten Status darstellt. Wenn return_sequences
True
ist, dann sind es viele zu viele . Seine Rückgabeform ist (batch_size, time_step, hidden_unit_length)
Wird das Merkmalargument relevant : Merkmalargument bedeutet "Wie groß ist Ihre rote Box" oder was ist die Eingangsabmessung für jeden Schritt. Wenn Sie beispielsweise 8 Arten von Marktinformationen vorhersagen möchten, können Sie Ihre Daten mit feature==8
Generieren.
Stateful : Sie können den Quellcode nachschlagen. Wenn beim Initialisieren des Status stateful==True
Verwendet wird, wird der Status des letzten Trainings als Anfangsstatus verwendet, andernfalls wird ein neuer Status generiert. Ich habe stateful
noch nicht aktiviert. Ich stimme jedoch nicht zu, dass batch_size
Nur 1 sein kann, wenn stateful==True
.
Derzeit generieren Sie Ihre Daten mit gesammelten Daten. Bild Ihre Bestandsinformationen kommen als Stream, anstatt auf einen Tag zu warten, um alle sequentiellen zu sammeln, möchten Sie Eingabedaten online während des Trainings/der Vorhersage generieren mit Netzwerk. Wenn Sie 400 Aktien in einem Netzwerk haben, können Sie batch_size==400
Einstellen.
Als Ergänzung zur akzeptierten Antwort zeigt diese Antwort Keras-Verhalten und wie jedes Bild erzielt werden kann.
Die interne Standard-Keras-Verarbeitung ist immer sehr viele, wie in der folgenden Abbildung (wobei ich nur als Beispiel features=2
, Druck und Temperatur verwendet habe):
In diesem Bild habe ich die Anzahl der Schritte auf 5 erhöht, um Verwechslungen mit den anderen Dimensionen zu vermeiden.
Für dieses Beispiel:
Unser Eingabearray sollte dann die Form (N,5,2)
Haben:
[ Step1 Step2 Step3 Step4 Step5
Tank A: [[Pa1,Ta1], [Pa2,Ta2], [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B: [[Pb1,Tb1], [Pb2,Tb2], [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
....
Tank N: [[Pn1,Tn1], [Pn2,Tn2], [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
]
Oft sollen LSTM-Schichten die gesamten Sequenzen verarbeiten. Das Teilen von Fenstern ist möglicherweise nicht die beste Idee. Die Ebene hat interne Zustände darüber, wie sich eine Sequenz entwickelt, wenn sie vorwärts geht. Windows eliminiert die Möglichkeit, lange Sequenzen zu lernen, und beschränkt alle Sequenzen auf die Fenstergröße.
In Fenstern ist jedes Fenster Teil einer langen Originalsequenz, für Keras werden sie jedoch als eigenständige Sequenzen angesehen:
[ Step1 Step2 Step3 Step4 Step5
Window A: [[P1,T1], [P2,T2], [P3,T3], [P4,T4], [P5,T5]],
Window B: [[P2,T2], [P3,T3], [P4,T4], [P5,T5], [P6,T6]],
Window C: [[P3,T3], [P4,T4], [P5,T5], [P6,T6], [P7,T7]],
....
]
Beachten Sie, dass Sie in diesem Fall zunächst nur eine Sequenz haben, diese jedoch in mehrere Sequenzen aufteilen, um Fenster zu erstellen.
Der Begriff "was ist eine Sequenz" ist abstrakt. Die wichtigen Teile sind:
Mit einer einfachen LSTM-Ebene können Sie viele bis viele Ziele erreichen, indem Sie return_sequences=True
Verwenden:
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
Mit genau der gleichen Ebene führt Keras die gleiche interne Vorverarbeitung durch, aber wenn Sie return_sequences=False
Verwenden (oder dieses Argument einfach ignorieren), verwirft Keras automatisch die vorletzten Schritte:
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
Dies wird jetzt nicht nur von Keras LSTM-Layern unterstützt. Sie müssen Ihre eigene Strategie erstellen, um die Schritte zu multiplizieren. Es gibt zwei gute Ansätze:
stateful=True
, Um immer wieder die Ausgabe eines Schritts zu übernehmen und als Eingabe für den nächsten Schritt zu dienen (benötigt output_features == input_features
).Um sich an das Standardverhalten von Keras anzupassen, benötigen wir Eingaben in Schritten. Deshalb wiederholen wir die Eingaben einfach für die gewünschte Länge:
outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)
#output_shape -> (batch_size, steps, units)
Jetzt kommt eine der möglichen Verwendungen von stateful=True
(Abgesehen davon, dass das Laden von Daten vermieden wird, die nicht gleichzeitig in den Arbeitsspeicher Ihres Computers passen)
Stateful ermöglicht es uns, "Teile" der Sequenzen in Stufen einzugeben. Der Unterschied ist:
stateful=False
Enthält der zweite Stapel völlig neue Sequenzen, unabhängig vom ersten Stapelstateful=True
Setzt der zweite Stapel den ersten Stapel fort und erweitert die gleichen Sequenzen.Es ist, als würde man die Sequenzen auch in Fenstern aufteilen, mit diesen zwei Hauptunterschieden:
stateful=True
Werden diese Fenster als eine einzige lange Sequenz verbundenIn stateful=True
Wird jede neue Charge als Fortsetzung der vorherigen Charge interpretiert (bis Sie model.reset_states()
aufrufen).
Beispiel für Eingaben, Charge 1 enthält die Schritte 1 und 2, Charge 2 enthält die Schritte 3 bis 5:
BATCH 1 BATCH 2
[ Step1 Step2 | [ Step3 Step4 Step5
Tank A: [[Pa1,Ta1], [Pa2,Ta2], | [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B: [[Pb1,Tb1], [Pb2,Tb2], | [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
.... |
Tank N: [[Pn1,Tn1], [Pn2,Tn2], | [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
] ]
Beachten Sie die Ausrichtung der Tanks in Charge 1 und Charge 2! Deshalb brauchen wir shuffle=False
(Es sei denn, wir verwenden natürlich nur eine Sequenz).
Sie können beliebig viele Stapel auf unbestimmte Zeit erstellen. (Verwenden Sie für variable Längen in jedem Stapel input_shape=(None,features)
.
In unserem Fall verwenden wir nur einen Schritt pro Stapel, da wir einen Ausgabeschritt erhalten und ihn als Eingabe definieren möchten.
Bitte beachten Sie, dass das Verhalten auf dem Bild nicht durch stateful=True
Verursacht wird. Wir werden dieses Verhalten in einer manuellen Schleife weiter unten erzwingen. In diesem Beispiel "erlaubt" stateful=True
Uns, die Sequenz anzuhalten, das zu manipulieren, was wir wollen, und dort fortzufahren, wo wir angehalten haben.
Ehrlich gesagt ist der Wiederholungsansatz wahrscheinlich die bessere Wahl für diesen Fall. Aber da wir uns stateful=True
Ansehen, ist dies ein gutes Beispiel. Der beste Weg, dies zu nutzen, ist der nächste "viele zu viele" Fall.
Schicht:
outputs = LSTM(units=features,
stateful=True,
return_sequences=True, #just to keep a Nice output shape even with length 1
input_shape=(None,features))(inputs)
#units = features because we want to use the outputs as inputs
#None because we want variable length
#output_shape -> (batch_size, steps, units)
Jetzt brauchen wir eine manuelle Schleife für Vorhersagen:
input_data = someDataWithShape((batch, 1, features))
#important, we're starting new sequences, not continuing old ones:
model.reset_states()
output_sequence = []
last_step = input_data
for i in steps_to_predict:
new_step = model.predict(last_step)
output_sequence.append(new_step)
last_step = new_step
#end of the sequences
model.reset_states()
Hier erhalten wir eine sehr nette Anwendung: Versuchen Sie bei gegebener Eingabesequenz, die zukünftigen unbekannten Schritte vorherzusagen.
Wir verwenden die gleiche Methode wie oben unter "eins zu viele", mit dem Unterschied, dass:
Schicht (wie oben):
outputs = LSTM(units=features,
stateful=True,
return_sequences=True,
input_shape=(None,features))(inputs)
#units = features because we want to use the outputs as inputs
#None because we want variable length
#output_shape -> (batch_size, steps, units)
Training:
Wir werden unser Modell trainieren, um den nächsten Schritt der Sequenzen vorherzusagen:
totalSequences = someSequencesShaped((batch, steps, features))
#batch size is usually 1 in these cases (often you have only one Tank in the example)
X = totalSequences[:,:-1] #the entire known sequence, except the last step
Y = totalSequences[:,1:] #one step ahead of X
#loop for resetting states at the start/end of the sequences:
for Epoch in range(epochs):
model.reset_states()
model.train_on_batch(X,Y)
Vorhersage:
Die erste Phase unserer Voraussage beinhaltet das "Anpassen der Staaten". Deshalb werden wir die gesamte Sequenz erneut vorhersagen, auch wenn wir diesen Teil bereits kennen:
model.reset_states() #starting a new sequence
predicted = model.predict(totalSequences)
firstNewStep = predicted[:,-1:] #the last step of the predictions is the first future step
Jetzt gehen wir zur Schleife wie in dem einen bis vielen Fall. Aber hier nicht zurücksetzen! . Wir möchten, dass das Modell weiß, in welchem Schritt der Sequenz es sich befindet (und es weiß, dass es sich aufgrund der oben gemachten Vorhersage um den ersten neuen Schritt handelt).
output_sequence = [firstNewStep]
last_step = firstNewStep
for i in steps_to_predict:
new_step = model.predict(last_step)
output_sequence.append(new_step)
last_step = new_step
#end of the sequences
model.reset_states()
Dieser Ansatz wurde in den folgenden Antworten und Dateien verwendet:
In allen obigen Beispielen habe ich das Verhalten von "one layer" gezeigt.
Sie können natürlich viele Ebenen übereinander stapeln, nicht unbedingt alle nach demselben Muster, und Ihre eigenen Modelle erstellen.
Ein interessantes Beispiel ist der "Autoencoder" mit einem "Many to One Encoder", gefolgt von einem "One to Many" Decoder:
Encoder:
inputs = Input((steps,features))
#a few many to many layers:
outputs = LSTM(hidden1,return_sequences=True)(inputs)
outputs = LSTM(hidden2,return_sequences=True)(outputs)
#many to one layer:
outputs = LSTM(hidden3)(outputs)
encoder = Model(inputs,outputs)
Decoder:
Verwenden der "Wiederholungsmethode";
inputs = Input((hidden3,))
#repeat to make one to many:
outputs = RepeatVector(steps)(inputs)
#a few many to many layers:
outputs = LSTM(hidden4,return_sequences=True)(outputs)
#last layer
outputs = LSTM(features,return_sequences=True)(outputs)
decoder = Model(inputs,outputs)
Autoencoder:
inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)
autoencoder = Model(inputs,outputs)
Trainiere mit fit(X,X)
Wenn Sie Details zur Berechnung von Schritten in LSTMs oder Details zu den oben genannten stateful=True
- Fällen wünschen, lesen Sie die folgende Antwort: Zweifel bezüglich des Verständnisses von Keras-LSTMs
Wenn Sie return_sequences in Ihrer letzten RNN-Ebene haben, können Sie keine einfache Dichte-Ebene verwenden, sondern TimeDistributed.
Hier ist ein Beispielcode, der anderen helfen könnte.
words = keras.layers.Input (batch_shape = (None, self.maxSequenceLength), name = "input")
# Build a matrix of size vocabularySize x EmbeddingDimension
# where each row corresponds to a "Word embedding" vector.
# This layer will convert replace each Word-id with a Word-vector of size Embedding Dimension.
embeddings = keras.layers.embeddings.Embedding(self.vocabularySize, self.EmbeddingDimension,
name = "embeddings")(words)
# Pass the Word-vectors to the LSTM layer.
# We are setting the hidden-state size to 512.
# The output will be batchSize x maxSequenceLength x hiddenStateSize
hiddenStates = keras.layers.GRU(512, return_sequences = True,
input_shape=(self.maxSequenceLength,
self.EmbeddingDimension),
name = "rnn")(embeddings)
hiddenStates2 = keras.layers.GRU(128, return_sequences = True,
input_shape=(self.maxSequenceLength, self.EmbeddingDimension),
name = "rnn2")(hiddenStates)
denseOutput = TimeDistributed(keras.layers.Dense(self.vocabularySize),
name = "linear")(hiddenStates2)
predictions = TimeDistributed(keras.layers.Activation("softmax"),
name = "softmax")(denseOutput)
# Build the computational graph by specifying the input, and output of the network.
model = keras.models.Model(input = words, output = predictions)
# model.compile(loss='kullback_leibler_divergence', \
model.compile(loss='sparse_categorical_crossentropy', \
optimizer = keras.optimizers.Adam(lr=0.009, \
beta_1=0.9,\
beta_2=0.999, \
epsilon=None, \
decay=0.01, \
amsgrad=False))