wake-up-neo.com

Grundlegendes zu Keras-LSTMs

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,

  1. Die Umformung der Datenreihe in [samples, time steps, features] Und
  2. Die zustandsbehafteten LSTMs

Konzentrieren 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.

Was sind Zeitschritte und Funktionen?

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). enter image description here

Wird das Argument features relevant, wenn wir multivariate Reihen betrachten? z.B. Modellierung von zwei Finanzwerten gleichzeitig?

Stateful LSTMs

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/

Bearbeiten 1:

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.): enter image description hereenter image description here

Bearbeiten 2:

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

Aktualisieren:

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

Update2:

Ich habe das meiste meines Verständnisses von LSTMs hier zusammengefasst: https://www.youtube.com/watch?v=ywinX5wgdEU

239
sachinruk

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_sequencesFalse 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_sequencesTrue 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.

132
Van

Als Ergänzung zur akzeptierten Antwort zeigt diese Antwort Keras-Verhalten und wie jedes Bild erzielt werden kann.

Allgemeines Keras-Verhalten

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):

ManyToMany

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:

  • Wir haben N Öltanks
  • Wir haben 5 Stunden damit verbracht, stündlich Maßnahmen zu ergreifen (Zeitschritte)
  • Wir haben zwei Merkmale gemessen:
    • Druck P
    • Temperatur T

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]],
        ]

Eingänge für Schiebefenster

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:

  • sie können Chargen mit vielen einzelnen Sequenzen erstellen
  • was die Sequenzen zu Sequenzen macht, ist, dass sie sich in Schritten entwickeln (normalerweise in Zeitschritten)

Jeweils mit "Einzelschichten" erreichen

Viele zu vielen Standards erreichen:

StandardManyToMany

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)

Viele zu Eins erreichen:

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:

ManyToOne

outputs = LSTM(units)(inputs)

#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned

Eins zu viel erreichen

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:

  • Erstellen Sie eine konstante mehrstufige Eingabe, indem Sie einen Tensor wiederholen
  • Verwenden Sie einen 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).

Eins zu viele mit Wiederholungsvektor

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:

OneToManyRepeat

outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)

#output_shape -> (batch_size, steps, units)

Stateful verstehen = True

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:

  • In stateful=False Enthält der zweite Stapel völlig neue Sequenzen, unabhängig vom ersten Stapel
  • In stateful=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:

  • diese Fenster überlagern sich nicht !!
  • Mit stateful=True Werden diese Fenster als eine einzige lange Sequenz verbunden

In stateful=True Wird jede neue Charge als Fortsetzung der vorherigen Charge interpretiert (bis Sie model.reset_states() aufrufen).

  • Sequenz 1 in Charge 2 setzt Sequenz 1 in Charge 1 fort.
  • Sequenz 2 in Charge 2 setzt Sequenz 2 in Charge 1 fort.
  • Sequenz n in Charge 2 setzt Sequenz n in Charge 1 fort.

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).

Eins zu viele mit stateful = True

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.

OneToManyStateful

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()

Viele zu viele mit stateful = True

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:

  • wir werden die Sequenz selbst als Zieldaten verwenden, um einen Schritt voraus zu sein
  • wir kennen einen Teil der Sequenz (also verwerfen wir diesen Teil der Ergebnisse).

ManyToManyStateful

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:

Komplexe Konfigurationen realisieren

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)

Zusätzliche Erklärungen

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

117
Daniel Möller

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))
0
Sanjay Krishna