Comprensione degli KST LSTM


311

Sto cercando di conciliare la mia comprensione degli LSTM e sottolineato qui in questo post di Christopher Olah implementato a Keras. Sto seguendo il blog scritto da Jason Brownlee per il tutorial di Keras. Ciò di cui sono principalmente confuso è

  1. Il rimodellamento della serie di dati in [samples, time steps, features] e,
  2. Gli LSTM con stato

Concentriamoci sulle due domande precedenti con riferimento al codice incollato di seguito:

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

Nota: create_dataset accetta una sequenza di lunghezza N e restituisce una N-look_backmatrice di cui ogni elemento è alook_back sequenza di lunghezza.

Cos'è Time Steps e Features?

Come si può vedere TrainX è un array 3-D con Time_steps e Feature che rappresentano rispettivamente le ultime due dimensioni (3 e 1 in questo particolare codice). Rispetto all'immagine seguente, significa che stiamo considerando il many to onecaso in cui il numero di caselle rosa è 3? Oppure significa letteralmente che la lunghezza della catena è 3 (cioè solo 3 scatole verdi considerate).inserisci qui la descrizione dell'immagine

L'argomento delle caratteristiche diventa rilevante quando consideriamo le serie multivariate? ad esempio modellare due titoli finanziari contemporaneamente?

LSTM con stato

Gli LSTM con stato significano che salviamo i valori di memoria della cella tra le esecuzioni di batch? Se questo è il caso,batch_size è, e la memoria viene ripristinata tra le sessioni di allenamento, quindi qual era il punto di dire che era stato. Immagino che questo sia legato al fatto che i dati di allenamento non vengono mescolati, ma non sono sicuro di come.

qualche idea? Riferimento immagine: http://karpathy.github.io/2015/05/21/rnn-effectiveness/

Modifica 1:

Un po 'confuso sul commento di @ van sul fatto che le scatole rosse e verdi siano uguali. Quindi, solo per confermare, le seguenti chiamate API corrispondono ai diagrammi non srotolati? Soprattutto notando il secondo diagramma (è batch_sizestato scelto arbitrariamente.): inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Modifica 2:

Per le persone che hanno seguito il corso di deep learning di Udacity e sono ancora confuse sull'argomento time_step, guarda la seguente discussione: https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169

Aggiornare:

Si è scoperto che model.add(TimeDistributed(Dense(vocab_len)))era quello che stavo cercando. Ecco un esempio: https://github.com/sachinruk/ShakespeareBot

Update2:

Ho riassunto la maggior parte della mia comprensione degli LSTM qui: https://www.youtube.com/watch?v=ywinX5wgdEU


7
La prima foto dovrebbe essere (batch_size, 5, 1); la seconda foto dovrebbe essere (batch_size, 4, 3) (se non ci sono sequenze seguenti). E perché l'output è ancora "X"? Dovrebbe essere "Y"?
Van

1
Qui presumo X_1, X_2 ... X_6 è un singolo numero. E tre numeri (X_1, X_2, X_3) formano un vettore di forma (3,). Un numero (X_1) crea un vettore di forma (1,).
Van

2
@ Van, il tuo presupposto è corretto. È interessante, quindi sostanzialmente il modello non impara schemi oltre il numero di time_steps. Quindi se ho una serie temporale della lunghezza di 1000 e posso vedere visivamente uno schema ogni 100 giorni, dovrei rendere il parametro time_steps almeno 100. È un'osservazione corretta?
sabato

3
Sì. E se riesci a raccogliere 3 funzioni pertinenti al giorno, puoi impostare la dimensione delle funzioni su 3 come nella seconda foto. In tale circostanza, la forma di input sarà (batch_size, 100, 3).
Van

1
e per rispondere alla tua prima domanda è stato perché stavo prendendo una sola serie storica. Ad esempio i prezzi delle azioni, quindi X e Y appartengono alla stessa serie.
sabato

Risposte:


173

Prima di tutto, scegli grandi tutorial ( 1 , 2 ) per iniziare.

Cosa significa Time-step :Time-steps==3 in X.shape (Descrivere la forma dei dati) significa che ci sono tre caselle rosa. Poiché in Keras ogni passaggio richiede un input, pertanto il numero delle caselle verdi dovrebbe in genere essere uguale al numero delle caselle rosse. A meno che non modifichi la struttura.

molti a molti vs. molti a uno : in keras, c'è un return_sequencesparametro durante l'inizializzazione LSTMo GRUo SimpleRNN. Quando return_sequencesè False(per impostazione predefinita), allora è molti a uno come mostrato in figura. La sua forma di ritorno è (batch_size, hidden_unit_length), che rappresenta l'ultimo stato. Quando lo return_sequencesè True, allora è molti a molti . La sua forma di ritorno è(batch_size, time_step, hidden_unit_length)

L'argomento delle funzionalità diventa rilevante : argomento della funzione significa "Quanto è grande il riquadro rosso" o qual è la dimensione di input per ogni passaggio. Se si desidera prevedere, ad esempio, 8 tipi di informazioni di mercato, è possibile generare i dati con feature==8.

Stateful : puoi cercare il codice sorgente . Quando si inizializza lo stato, se stateful==True, lo stato dell'ultimo addestramento verrà utilizzato come stato iniziale, altrimenti genererà un nuovo stato. Non ho ancora acceso stateful. Tuttavia, non sono d'accordo con il fatto che batch_sizepuò essere solo 1 quandostateful==True .

Attualmente, generi i tuoi dati con i dati raccolti. Immagina che le tue informazioni di borsa stiano arrivando come stream, piuttosto che aspettare un giorno per raccogliere tutte le sequenziali, ti piacerebbe generare dati di input online mentre ti alleni / predici con la rete. Se hai 400 azioni che condividono la stessa rete, puoi impostare batch_size==400.


Leggermente confuso sul perché le scatole rosse e verdi debbano essere le stesse. Potresti guardare la modifica che ho fatto (principalmente le nuove foto) e commentare?
sabato

1
Infatti. Controlla il documento:stateful: Boolean (default False). If True, the last state for each sample at index i in a batch will be used as initial state for the sample of index i in the following batch.
Van

1
@Van Se ho una serie temporale multivariata, dovrei ancora utilizzare lookback = 1?
inn

1
Perché la dimensionalità LSTM dello spazio di uscita (32) differisce dal numero di neuroni (cellule LSTM)?
Appiccicoso

1
Aggiunta a stateful=True: la dimensione del lotto può essere qualsiasi cosa ti piaccia, ma devi attenersi ad essa. Se si genera il modello con una dimensione del lotto di 5, allora tutti fit(), predict()e dei relativi metodi necessario un lotto di 5. Nota, tuttavia, che questo stato non viene salvato con model.save(), che potrebbe sembrare indesiderabile. Tuttavia, è possibile aggiungere manualmente lo stato al file hdf5, se necessario. Ma in realtà ciò consente di modificare le dimensioni del batch semplicemente salvando e ricaricando un modello.
jlh

192

A complemento della risposta accettata, questa risposta mostra i comportamenti di Keras e come ottenere ogni immagine.

Comportamento generale di Keras

L'elaborazione interna delle telecamere standard è sempre da molte a molte come nella seguente immagine (dove ho usato features=2, pressione e temperatura, solo come esempio):

ManyToMany

In questa immagine, ho aumentato il numero di passaggi a 5, per evitare confusione con le altre dimensioni.

Per questo esempio:

  • Abbiamo N serbatoi di petrolio
  • Abbiamo trascorso 5 ore a prendere misure orarie (fasi temporali)
  • Abbiamo misurato due caratteristiche:
    • Pressione P
    • Temperatura T

Il nostro array di input dovrebbe quindi avere una forma simile a (N,5,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]],
        ]

Ingressi per finestre scorrevoli

Spesso, i layer LSTM dovrebbero elaborare l'intera sequenza. Dividere le finestre potrebbe non essere la migliore idea. Il livello ha stati interni su come si sta evolvendo una sequenza mentre avanza. Le finestre eliminano la possibilità di apprendere lunghe sequenze, limitando tutte le sequenze alle dimensioni della finestra.

Nelle finestre, ogni finestra fa parte di una lunga sequenza originale, ma per Keras saranno viste ciascuna come una sequenza indipendente:

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

Nota che in questo caso hai inizialmente solo una sequenza, ma la stai dividendo in molte sequenze per creare finestre.

Il concetto di "cos'è una sequenza" è astratto. Le parti importanti sono:

  • puoi avere batch con molte sequenze individuali
  • ciò che rende le sequenze sequenze è che si evolvono in fasi (di solito fasi temporali)

Raggiungere ogni caso con "singoli strati"

Raggiungere standard da molti a molti:

StandardManyToMany

Puoi ottenere molti a molti con un semplice livello LSTM, usando return_sequences=True:

outputs = LSTM(units, return_sequences=True)(inputs)

#output_shape -> (batch_size, steps, units)

Raggiungere molti a uno:

Utilizzando lo stesso layer esatto, keras eseguirà esattamente la stessa preelaborazione interna, ma quando si utilizza return_sequences=False(o semplicemente si ignora questo argomento), keras eliminerà automaticamente i passaggi precedenti all'ultimo:

ManyToOne

outputs = LSTM(units)(inputs)

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

Raggiungere uno a molti

Ora, questo non è supportato dai soli livelli KST LSTM. Dovrai creare la tua strategia per moltiplicare i passaggi. Esistono due buoni approcci:

  • Crea un input multi-step costante ripetendo un tensore
  • Utilizzare a stateful=Trueper eseguire periodicamente l'output di un passaggio e servirlo come input del passaggio successivo (esigenze output_features == input_features)

Uno a molti con ripetizione vettoriale

Per adattarci al comportamento standard di keras, abbiamo bisogno di input in step, quindi semplicemente ripetiamo gli input per la lunghezza che vogliamo:

OneToManyRepeat

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

#output_shape -> (batch_size, steps, units)

Comprensione stateful = True

Ora arriva uno dei possibili usi di stateful=True (oltre a evitare il caricamento di dati che non possono adattarsi alla memoria del tuo computer in una volta)

Stateful ci consente di inserire "parti" delle sequenze in più fasi. La differenza è:

  • In stateful=False, il secondo batch contiene sequenze completamente nuove, indipendenti dal primo batch
  • In stateful=True, il secondo batch continua il primo batch, estendendo le stesse sequenze.

È come dividere le sequenze anche in Windows, con queste due differenze principali:

  • queste finestre non si sovrappongono !!
  • stateful=True vedrà queste finestre connesse come un'unica lunga sequenza

In stateful=True, ogni nuovo batch verrà interpretato come continuazione del batch precedente (fino a quando non si chiama model.reset_states()).

  • La sequenza 1 nel lotto 2 continuerà la sequenza 1 nel lotto 1.
  • La sequenza 2 nel lotto 2 continuerà la sequenza 2 nel lotto 1.
  • La sequenza n nel lotto 2 continuerà la sequenza n nel lotto 1.

Esempio di input, il batch 1 contiene i passaggi 1 e 2, il batch 2 contiene i passaggi da 3 a 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]],
        ]                                  ]

Notare l'allineamento dei serbatoi nel lotto 1 e lotto 2! Ecco perché ne abbiamo bisogno shuffle=False(a meno che non stiamo usando solo una sequenza, ovviamente).

Puoi avere un numero illimitato di lotti, a tempo indeterminato. (Per avere lunghezze variabili in ciascun lotto, utilizzare input_shape=(None,features).

Uno a molti con stateful = True

Nel nostro caso, utilizzeremo solo 1 passaggio per batch, poiché vogliamo ottenere un passaggio di output e renderlo un input.

Si noti che il comportamento nell'immagine non è "causato da" stateful=True. Forzeremo quel comportamento in un ciclo manuale di seguito. In questo esempio, stateful=Trueè ciò che "ci permette" di interrompere la sequenza, manipolare ciò che vogliamo e continuare da dove ci siamo fermati.

OneToManyStateful

Onestamente, l'approccio ripetuto è probabilmente una scelta migliore per questo caso. Ma dal momento che stiamo esaminando stateful=True, questo è un buon esempio. Il modo migliore per utilizzare questo è il prossimo caso "molti a molti".

Strato:

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) 

Ora avremo bisogno di un ciclo manuale per le previsioni:

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

Molti a molti con stateful = True

Ora, qui, otteniamo un'applicazione molto bella: data una sequenza di input, prova a prevedere i suoi futuri passi sconosciuti.

Stiamo usando lo stesso metodo di "uno a molti" sopra, con la differenza che:

  • useremo la sequenza stessa per essere i dati target, un passo avanti
  • conosciamo parte della sequenza (quindi scartiamo questa parte dei risultati).

ManyToManyStateful

Strato (come sopra):

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) 

Formazione:

Formeremo il nostro modello per prevedere il prossimo passo delle sequenze:

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)

predire:

Il primo stadio della nostra previsione prevede "regolare gli stati". Ecco perché prediremo di nuovo l'intera sequenza, anche se ne conosciamo già questa parte:

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

Ora andiamo al loop come nel caso di uno a molti. Ma non ripristinare gli stati qui! . Vogliamo che il modello sappia in quale fase della sequenza è (e sa che è al primo nuovo passaggio a causa della previsione che abbiamo appena fatto sopra)

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

Questo approccio è stato utilizzato in queste risposte e file:

Ottenere configurazioni complesse

In tutti gli esempi sopra, ho mostrato il comportamento di "uno strato".

Puoi, ovviamente, impilare molti strati uno sopra l'altro, non necessariamente seguendo tutti lo stesso modello e creare i tuoi modelli.

Un esempio interessante che è apparso è il "codificatore automatico" che ha un "codificatore molti a uno" seguito da un decodificatore "uno a molti":

Codificatore:

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:

Utilizzando il metodo "ripeti";

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)

Allenati con fit(X,X)

Spiegazioni aggiuntive

Se desideri dettagli su come vengono calcolati i passaggi in LSTM o dettagli sui stateful=Truecasi sopra, puoi leggere di più in questa risposta: Dubbi su `Comprensione degli LSTM di Keras`


1
Uso molto interessante di stateful con l'utilizzo di output come input. Proprio come una nota aggiuntiva, un altro modo per farlo sarebbe usare l'API Keras funzionale (come hai fatto qui, anche se credo che avresti potuto usare quella sequenziale) e semplicemente riutilizzare la stessa cella LSTM per ogni passaggio , passando allo stesso tempo lo stato risultante e l'output dalla cella a se stesso. Vale a dire my_cell = LSTM(num_output_features_per_timestep, return_state=True), seguito da un ciclo dia, _, c = my_cell(output_of_previous_time_step, initial_states=[a, c])
Jacob R

1
Le celle e la lunghezza sono valori completamente indipendenti. Nessuna delle immagini rappresenta il numero di "celle". Sono tutti per "lunghezza".
Daniel Möller,

1
@ DanielMöller So che è un po 'in ritardo, ma la tua risposta attira davvero la mia attenzione. Un tuo punto ha sconvolto tutto ciò che riguarda la mia comprensione di ciò che è batch per LSTM. Fornisci esempi con N carri armati, cinque passaggi e due funzioni. Ho creduto che, se batch è ad esempio due, ciò significa che due campioni (serbatoi con 5 passaggi 2 caratteristiche) verranno immessi nella rete e successivamente verranno adattati i pesi. Ma se ho capito bene, affermi che il lotto 2 significa che i timestep dei campioni saranno divisi in 2 e che la prima metà di tutti i campioni verrà inviata a LSTM-> aggiornamento del peso e del secondo.
viceriel,

1
Sì. Su stateful = True, batch 1 = gruppo di campioni, aggiorna. Quindi batch 2 = più passaggi per lo stesso gruppo di campioni, aggiornare.
Daniel Möller,

2
Vorrei poter votare questo 100 volte. Risposta super utile.
adamconkey,

4

Quando hai return_sequences nell'ultimo livello di RNN non puoi utilizzare un semplice livello Dense invece utilizzare TimeDistributed.

Ecco un esempio di codice che potrebbe aiutare gli altri.

parole = 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))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.