En complément de la réponse acceptée, cette réponse montre les comportements des kéros et comment réaliser chaque image.
Comportement général de Keras
Le traitement interne des keras standard est toujours un à plusieurs comme dans l'image suivante (où j'ai utilisé features=2
, pression et température, juste à titre d'exemple):
Dans cette image, j'ai augmenté le nombre d'étapes à 5, pour éviter toute confusion avec les autres dimensions.
Pour cet exemple:
- Nous avons N réservoirs d'huile
- Nous avons passé 5 heures à prendre des mesures toutes les heures (pas de temps)
- Nous avons mesuré deux caractéristiques:
Notre tableau d'entrée devrait alors avoir la forme suivante (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]],
]
Entrées pour fenêtres coulissantes
Souvent, les couches LSTM sont censées traiter l'intégralité des séquences. La division des fenêtres n'est peut-être pas la meilleure idée. La couche a des états internes sur l'évolution d'une séquence à mesure qu'elle avance. Windows élimine la possibilité d'apprendre de longues séquences, limitant toutes les séquences à la taille de la fenêtre.
Dans les fenêtres, chaque fenêtre fait partie d'une longue séquence d'origine, mais selon Keras, elles seront vues chacune comme une séquence indépendante:
[ 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]],
....
]
Notez que dans ce cas, vous n'avez initialement qu'une seule séquence, mais vous la divisez en plusieurs séquences pour créer des fenêtres.
Le concept de "qu'est-ce qu'une séquence" est abstrait. Les parties importantes sont:
- vous pouvez avoir des lots avec de nombreuses séquences individuelles
- ce qui fait que les séquences sont des séquences, c'est qu'elles évoluent par étapes (généralement des pas de temps)
Atteindre chaque cas avec des "couches simples"
Atteindre la norme plusieurs à plusieurs:
Vous pouvez réaliser plusieurs à plusieurs avec une simple couche LSTM, en utilisant return_sequences=True
:
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
Atteindre plusieurs à un:
En utilisant exactement la même couche, les keras effectueront exactement le même prétraitement interne, mais lorsque vous utilisez return_sequences=False
(ou ignorez simplement cet argument), les keras ignoreront automatiquement les étapes précédentes à la dernière:
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
Atteindre un à plusieurs
Maintenant, cela n'est pas pris en charge par les couches keras LSTM seules. Vous devrez créer votre propre stratégie pour multiplier les étapes. Il existe deux bonnes approches:
- Créer une entrée à plusieurs étapes constante en répétant un tenseur
- Utilisez a
stateful=True
pour prendre de façon récurrente la sortie d'une étape et la servir d'entrée de l'étape suivante (besoins output_features == input_features
)
Un à plusieurs avec vecteur de répétition
Afin de s'adapter au comportement standard des keras, nous avons besoin d'entrées par étapes, donc, nous répétons simplement les entrées pour la longueur que nous voulons:
outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)
#output_shape -> (batch_size, steps, units)
Comprendre stateful = True
Vient maintenant l'un des usages possibles de stateful=True
(en plus d'éviter de charger des données qui ne peuvent pas contenir la mémoire de votre ordinateur à la fois)
Stateful nous permet de saisir des "parties" des séquences par étapes. La différence est:
- Dans
stateful=False
, le deuxième lot contient de nouvelles séquences entières, indépendantes du premier lot
- Dans
stateful=True
, le deuxième lot continue le premier lot, étendant les mêmes séquences.
C'est comme diviser les séquences dans les fenêtres aussi, avec ces deux différences principales:
- ces fenêtres ne se superposent pas !!
stateful=True
verra ces fenêtres connectées comme une seule longue séquence
Dans stateful=True
, chaque nouveau lot sera interprété comme poursuivant le lot précédent (jusqu'à ce que vous appeliez model.reset_states()
).
- La séquence 1 du lot 2 continuera la séquence 1 du lot 1.
- La séquence 2 du lot 2 continuera la séquence 2 du lot 1.
- La séquence n du lot 2 continuera la séquence n du lot 1.
Exemple d'entrées, le lot 1 contient les étapes 1 et 2, le lot 2 contient les étapes 3 à 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]],
] ]
Remarquez l'alignement des réservoirs des lots 1 et 2! C'est pourquoi nous avons besoin shuffle=False
(à moins que nous n'utilisions qu'une seule séquence, bien sûr).
Vous pouvez avoir un nombre illimité de lots, indéfiniment. (Pour avoir des longueurs variables dans chaque lot, utilisez input_shape=(None,features)
.
Un à plusieurs avec stateful = True
Pour notre cas ici, nous n'utiliserons qu'une seule étape par lot, car nous voulons obtenir une étape de sortie et en faire une entrée.
Veuillez noter que le comportement dans l'image n'est pas "provoqué par" stateful=True
. Nous forcerons ce comportement dans une boucle manuelle ci-dessous. Dans cet exemple, stateful=True
c'est ce qui nous «permet» d'arrêter la séquence, de manipuler ce que nous voulons et de continuer d'où nous nous sommes arrêtés.
Honnêtement, l'approche répétée est probablement un meilleur choix pour ce cas. Mais puisque nous examinons stateful=True
, c'est un bon exemple. La meilleure façon de l'utiliser est le prochain cas "plusieurs à plusieurs".
Couche:
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)
Maintenant, nous allons avoir besoin d'une boucle manuelle pour les prédictions:
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()
Plusieurs à plusieurs avec stateful = True
Maintenant, ici, nous obtenons une très belle application: étant donné une séquence d'entrée, essayez de prédire ses futures étapes inconnues.
Nous utilisons la même méthode que dans le "un à plusieurs" ci-dessus, à la différence que:
- nous utiliserons la séquence elle-même pour être les données cibles, une longueur d'avance
- nous connaissons une partie de la séquence (nous rejetons donc cette partie des résultats).
Couche (comme ci-dessus):
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)
Formation:
Nous allons former notre modèle pour prédire la prochaine étape des séquences:
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)
Prédire:
La première étape de notre prévision consiste à "ajuster les états". C'est pourquoi nous allons à nouveau prédire la séquence entière, même si nous en connaissons déjà cette partie:
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
Nous passons maintenant à la boucle comme dans le cas un à plusieurs. Mais ne réinitialisez pas les états ici! . Nous voulons que le modèle sache à quelle étape de la séquence il se trouve (et il sait qu'il est à la première nouvelle étape en raison de la prédiction que nous venons de faire ci-dessus)
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()
Cette approche a été utilisée dans ces réponses et dossier:
Réalisation de configurations complexes
Dans tous les exemples ci-dessus, j'ai montré le comportement de "une couche".
Vous pouvez, bien sûr, empiler plusieurs couches les unes sur les autres, sans nécessairement toutes suivre le même modèle, et créer vos propres modèles.
Un exemple intéressant qui est apparu est le "autoencoder" qui a un "plusieurs à un codeur" suivi d'un décodeur "un à plusieurs":
Encodeur:
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)
Décodeur:
Utilisation de la méthode "répétition";
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)
Encodeur automatique:
inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)
autoencoder = Model(inputs,outputs)
Entraînez-vous avec fit(X,X)
Explications supplémentaires
Si vous voulez des détails sur la façon dont les étapes sont calculées dans les LSTM, ou des détails sur les stateful=True
cas ci-dessus, vous pouvez en savoir plus dans cette réponse: Doutes concernant `Comprendre les LSTM Keras`