Comprendre Le Kéras LSTMs

j'essaie de concilier ma compréhension de LSTMs et a souligné ici dans ce post de Christopher Olah mis en œuvre dans Keras. Je suis le blog écrit par Jason Brownlee pour le tutoriel Keras. Ce qui me trouble le plus, c'est

  1. la refonte de La série [samples, time steps, features] et,
  2. La dynamique LSTMs

vous Permet de se concentrer sur les deux questions ci-dessus en se référant au code collé ci-dessous:

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

Note: create_dataset prend une séquence de longueur N et renvoie un tableau N-look_back dont chaque élément est une séquence de longueur look_back .

Qu'est-ce que les étapes et les caractéristiques du temps?

comme on peut le voir TrainX est un tableau 3-D avec Time_steps et Feature étant les deux dernières dimensions respectivement (3 et 1 dans ce particulier code.) En ce qui concerne l'image ci-dessous, cela signifie-t-il que nous considérons le cas many to one , où le nombre de boîtes roses est de 3? Ou cela signifie-t-il littéralement la longueur de la chaîne est de 3 (c.-à-d. seulement 3 boîtes vertes considérées). enter image description here

l'argument fonctionnalités devenir pertinent lorsque l'on considère multivariée de la série? par exemple, modéliser simultanément deux stocks financiers?

Stateful LSTMs

est-ce que les LSTM statiques signifient que nous sauvegardons les valeurs de la mémoire cellulaire entre les séries de lots? Si c'est le cas, batch_size en est un, et la mémoire est réinitialisée entre les parcours d'entraînement donc à quoi bon dire qu'elle était stateful. Je suppose que c'est lié au fait que les données d'entraînement ne sont pas mélangées, mais je ne sais pas comment.

une idée? Référence de l'Image: http://karpathy.github.io/2015/05/21/rnn-effectiveness/

Edit 1:

un peu confus sur le commentaire de @van sur l'égalité des boîtes rouge et verte. Donc juste pour confirmer, les appels API suivants correspondent-ils aux diagrammes déroulés? En particulier en notant le deuxième diagramme ( batch_size a été arbitrairement choisi.): enter image description here enter image description here

Edit 2:

pour les personnes qui ont suivi le cours de formation approfondie D'Udacity et qui sont encore confuses au sujet de l'argument time_step, regardez la discussion suivante: https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169

mise à jour:

Il s'avère que model.add(TimeDistributed(Dense(vocab_len))) était ce que je cherchais. Voici un exemple: https://github.com/sachinruk/ShakespeareBot

Update2:

j'ai résumé la plupart de ma compréhension de LSTMs ici: https://www.youtube.com/watch?v=ywinX5wgdEU

162
demandé sur Sachin_ruk 2016-08-02 11:04:13

3 réponses

tout d'abord, vous choisissez de grands tutoriels( 1 , 2 ) pour commencer.

Ce Que Time-step signifie : Time-steps==3 en forme de X. (décrivant la forme des données) signifie qu'il y a trois boîtes roses. Depuis dans Keras chaque étape nécessite une entrée, donc le nombre de cases vertes devraient généralement égal au nombre de cases rouges. À moins que vous ne Hacker la structure.

beaucoup à beaucoup vs. beaucoup à un : dans keras, il y a un paramètre return_sequences lorsque vous initialisez LSTM ou GRU ou SimpleRNN . Lorsque return_sequences est False (par défaut), alors il est beaucoup à un comme indiqué sur l'image. Sa forme de retour est (batch_size, hidden_unit_length) , qui représentent le dernier état. Quand return_sequences est True , alors il est beaucoup à beaucoup . Sa forme de retour est (batch_size, time_step, hidden_unit_length)

est-ce que l'argument des caractéristiques devient pertinent : l'argument des caractéristiques signifie "Quelle est la taille de votre boîte rouge" ou Quelle est la dimension d'entrée à chaque étape. Si vous voulez prédire à partir de, disons, 8 types d'informations de marché, alors vous pouvez générer vos données avec feature==8 .

Stateful : vous pouvez rechercher le code source . Lors de l'initialisation de l'état, si stateful==True , alors que l'état de la dernière formation sera utilisé comme état initial, sinon il va générer un nouvel état. Je n'ai pas encore allumé stateful . Cependant, je ne suis pas d'accord avec le fait que le batch_size ne peut être que 1 lorsque stateful==True .

Actuellement, vous produisez vos données avec des données collectées. L'Image de votre information de stock arrive en flux, plutôt que d'attendre un jour pour recueillir tous les séquentiels, vous souhaitez générer des données d'entrée en ligne pendant la formation / la prévision avec le réseau. Si vous avez 400 stocks partageant un même réseau, alors vous pouvez définir batch_size==400 .

89
répondu Van 2016-08-03 08:09:59

en complément de la réponse acceptée, cette réponse montre les comportements de keras et comment réaliser chaque image.

Général Keras comportement

le traitement interne standard de keras est toujours de beaucoup à beaucoup comme dans l'image suivante (où j'ai utilisé features=2 , pression et température, juste comme un exemple):

ManyToMany

dans ce image, j'ai augmenté le nombre d'étapes à 5, pour éviter la confusion avec les autres dimensions.

pour cet exemple:

  • Nous avons N réservoirs d'huile
  • Nous avons passé 5 heures à prendre des mesures à l'heure (pas de temps)
  • Nous avons mesuré deux caractéristiques:
    • Pression P
    • Température T

Notre tableau d'entrées devrait alors être quelque chose en forme de (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 les séquences entières. Diviser les fenêtres n'est peut-être pas la meilleure idée. La couche a des états internes sur la façon dont une séquence évolue au fur et à mesure qu'elle progresse. Windows élimine la possibilité d'apprendre de longues séquences, limitant toutes les séquences à la taille de la fenêtre.

In fenêtres, chaque fenêtre est partie d'une longue séquence d'origine, mais par Keras ils seront vus comme une séquence autonome:

        [     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 windows.

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 par étapes)

réalisation de chaque cas avec "couches simples"

la Réalisation de standard plusieurs à plusieurs:

StandardManyToMany

vous pouvez réaliser beaucoup à beaucoup avec une couche LSTM simple, en utilisant return_sequences=True :

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

#output_shape -> (batch_size, steps, units)

la Réalisation de nombreux à un:

en utilisant exactement la même couche, keras fera exactement le même prétraitement interne, mais lorsque vous utilisez return_sequences=False (ou ignorez simplement cet argument), keras rejettera automatiquement les étapes antérieures à la dernière:

ManyToOne

outputs = LSTM(units)(inputs)

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

la Réalisation de l'un à plusieurs

maintenant, ce n'est pas supporté par keras LSTM couches seul. Vous devrez créer votre propre stratégie pour multiplier les étapes. Il y a deux bonnes approches:

  • créer une entrée constante en plusieurs étapes en répétant un tenseur
  • utilisez un stateful=True pour prendre de façon récurrente la sortie d'un pas et le servir comme entrée de la prochaine étape (besoins output_features == input_features )

Un-à-plusieurs avec la répétition vecteur

pour s'adapter à keras comportement standard, Nous avons besoin d'entrées par étapes, donc, nous répétons simplement les entrées pour la longueur que nous voulons:

OneToManyRepeat

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

#output_shape -> (batch_size, steps, units)

Comprendre la dynamique = True

vient maintenant l'une des utilisations possibles de stateful=True (en plus d'éviter de charger des données qui ne peuvent pas s'adapter à la mémoire de votre ordinateur à la fois)

permet de saisir des "parties" de les séquences dans les stades. La différence est:

  • dans stateful=False , le second lot contient des séquences entièrement nouvelles, indépendantes du premier lot
  • Dans stateful=True , le deuxième lot, poursuit le premier lot, prolongeant les mêmes séquences.

C'est comme diviser les séquences dans windows aussi, avec ces deux différences principales:

  • ces fenêtres font ne pas superposer!!
  • stateful=True verra ces fenêtres connectés comme une seule longue séquence

dans stateful=True , chaque nouveau lot sera interprété comme continuant le lot précédent (jusqu'à ce que vous appeliez model.reset_states() ).

  • la séquence 1 du deuxième lot continuera la séquence 1 du premier lot.
  • la séquence 2 du deuxième lot continuera la séquence 2 du premier lot.
  • La séquence
  • n du deuxième lot continuera la séquence n du premier lot.

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

noter l'alignement des citernes du lot 1 et du lot 2! C'est pourquoi nous avons besoin de shuffle=False (à moins que nous n'utilisions une seule séquence, bien sûr).

Vous pouvez avoir n'importe quel nombre de lots, indéfiniment. (Pour avoir des longueurs variables dans chaque lot, utilisez input_shape=(None,features) .

Un à plusieurs avec état=True

Pour notre cas ici, nous allons utiliser seulement 1 étape par lot, parce que nous voulons obtenir une sortie de l'étape et de faire une entrée.

s'il vous Plaît notez que le comportement de l'image n'est pas "causé par" stateful=True . Nous allons forcer ce comportement dans une boucle manuelle ci-dessous. Dans cet exemple, stateful=True est ce qui "permet" d'arrêter la séquence, de manipuler ce que nous vouloir, et continuer d'où nous nous sommes arrêtés.

OneToManyStateful

honnêtement, l'approche de répétition est probablement un meilleur choix pour ce cas. Mais puisque nous examinons stateful=True , c'est un bon exemple. La meilleure façon d'utiliser ceci est le prochain cas "beaucoup à beaucoup".

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 état=True

maintenant, ici, nous obtenons une application très agréable: étant donné une séquence d'entrée, essayer de prédire ses étapes futures inconnues.

nous utilisons la même méthode que dans le" one to many "ci-dessus, avec la différence que:

  • nous allons utiliser la séquence elle-même pour être les données cibles, un pas d'avance
  • nous connaissons une partie de la séquence (si nous écartons cette partie des résultats).

ManyToManyStateful

couche (même que 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évision:

la première étape de notre prédiction consiste à"ajuster les États". C'est pourquoi nous allons de nouveau prédire la séquence entière, même si nous connaissons déjà cette partie de celle-ci:

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

Maintenant, nous allons à la boucle, comme dans celui de nombreux cas. 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 que c'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 fichier:

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, pas nécessairement toutes suivant le même modèle, et créer vos propres modèles.

Un exemple intéressant qui apparaît est le "autoencoder" qui a "beaucoup à un encodeur", suivi par "un à plusieurs" décodeur:

"15191980920 de l'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)

"15191980920 Décodeur":

utilisant la méthode de" 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)

Autoencoder:

inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)

autoencoder = Model(inputs,outputs)

Train avec fit(X,X)

42
répondu Daniel Möller 2018-07-24 13:32:41

lorsque vous avez return_sequences dans votre dernière couche de RNN, vous ne pouvez pas utiliser une simple couche Dense au lieu d'utiliser TimeDistributed.

voici un exemple de code qui pourrait aider les autres.

mots = keras.couche.D'entrée(batch_shape=(None, auto.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
répondu Sanjay Krishna 2018-04-25 09:05:36