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):
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:
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:
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=True
per 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:
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.
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).
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=True
casi sopra, puoi leggere di più in questa risposta: Dubbi su `Comprensione degli LSTM di Keras`