web-dev-qa-db-fra.com

Comment former un RNN avec des cellules LSTM pour la prédiction de séries chronologiques

J'essaie actuellement de construire un modèle simple pour prédire les séries chronologiques. L'objectif serait de former le modèle avec une séquence afin que le modèle soit capable de prédire les valeurs futures.

J'utilise des cellules tensorflow et lstm pour ce faire. Le modèle est entraîné avec une rétropropagation tronquée dans le temps. Ma question est de savoir comment structurer les données pour la formation.

Par exemple, supposons que nous voulons apprendre la séquence donnée:

[1,2,3,4,5,6,7,8,9,10,11,...]

Et nous déroulons le réseau pour num_steps=4.

Option 1

input data               label     
1,2,3,4                  2,3,4,5
5,6,7,8                  6,7,8,9
9,10,11,12               10,11,12,13
...

Option 2

input data               label     
1,2,3,4                  2,3,4,5
2,3,4,5                  3,4,5,6
3,4,5,6                  4,5,6,7
...

Option

input data               label     
1,2,3,4                  5
2,3,4,5                  6
3,4,5,6                  7
...

Option 4

input data               label     
1,2,3,4                  5
5,6,7,8                  9
9,10,11,12               13
...

Toute aide serait appréciée.

21
Jakob

Après avoir lu plusieurs blogs d'introduction à LSTM, par ex. Jakob Aungiers ' , l'option 3 semble être la bonne pour les LSTM apatrides.

Si vos LSTM doivent se souvenir de données antérieures à votre num_steps, vous pouvez vous entraîner de manière dynamique - pour un exemple Keras voir Article de blog de Philippe Remy "Stateful LSTM in Keras" . Philippe ne montre cependant pas d'exemple de taille de lot supérieure à un. Je suppose que dans votre cas, une taille de lot de quatre avec LSTM avec état pourrait être utilisée avec les données suivantes (écrites comme input -> label):

batch #0:
1,2,3,4 -> 5
2,3,4,5 -> 6
3,4,5,6 -> 7
4,5,6,7 -> 8

batch #1:
5,6,7,8 -> 9
6,7,8,9 -> 10
7,8,9,10 -> 11
8,9,10,11 -> 12

batch #2:
9,10,11,12 -> 13
...

Par cela, l'état de par ex. le 2ème échantillon du lot # 0 est correctement réutilisé pour continuer la formation avec le 2ème échantillon du lot # 1.

Ceci est en quelque sorte similaire à votre option 4, mais vous n'utilisez pas toutes les étiquettes disponibles là-bas.

Mise à jour:

Dans le prolongement de ma suggestion où batch_size est égal à num_steps, Alexis Huet donne une réponse pour le cas de batch_size étant un diviseur de num_steps, qui peut être utilisé pour de plus grandes num_steps. Il le décrit bien sur son blog.

4
Robert Pollak

Je suis sur le point d'apprendre les LSTM dans TensorFlow et d'essayer d'implémenter un exemple qui (heureusement) essaie de prédire des séries chronologiques/séries de nombres générées par une simple math-fuction.

Mais j'utilise une manière différente de structurer les données pour la formation, motivée par Apprentissage non supervisé de représentations vidéo à l'aide de LSTM :

Modèle LSTM Future Predictor

Option 5:

input data               label     
1,2,3,4                  5,6,7,8
2,3,4,5                  6,7,8,9
3,4,5,6                  7,8,9,10
...

À côté de cet article, j'ai (essayé) de m'inspirer des exemples TensorFlow RNN donnés. Ma solution complète actuelle ressemble à ceci:

import math
import random
import numpy as np
import tensorflow as tf

LSTM_SIZE = 64
LSTM_LAYERS = 2
BATCH_SIZE = 16
NUM_T_STEPS = 4
MAX_STEPS = 1000
LAMBDA_REG = 5e-4


def ground_truth_func(i, j, t):
    return i * math.pow(t, 2) + j


def get_batch(batch_size):
    seq = np.zeros([batch_size, NUM_T_STEPS, 1], dtype=np.float32)
    tgt = np.zeros([batch_size, NUM_T_STEPS], dtype=np.float32)

    for b in xrange(batch_size):
        i = float(random.randint(-25, 25))
        j = float(random.randint(-100, 100))
        for t in xrange(NUM_T_STEPS):
            value = ground_truth_func(i, j, t)
            seq[b, t, 0] = value

        for t in xrange(NUM_T_STEPS):
            tgt[b, t] = ground_truth_func(i, j, t + NUM_T_STEPS)
    return seq, tgt


# Placeholder for the inputs in a given iteration
sequence = tf.placeholder(tf.float32, [BATCH_SIZE, NUM_T_STEPS, 1])
target = tf.placeholder(tf.float32, [BATCH_SIZE, NUM_T_STEPS])

fc1_weight = tf.get_variable('w1', [LSTM_SIZE, 1], initializer=tf.random_normal_initializer(mean=0.0, stddev=1.0))
fc1_bias = tf.get_variable('b1', [1], initializer=tf.constant_initializer(0.1))

# ENCODER
with tf.variable_scope('ENC_LSTM'):
    lstm = tf.nn.rnn_cell.LSTMCell(LSTM_SIZE)
    multi_lstm = tf.nn.rnn_cell.MultiRNNCell([lstm] * LSTM_LAYERS)
    initial_state = multi_lstm.zero_state(BATCH_SIZE, tf.float32)
    state = initial_state
    for t_step in xrange(NUM_T_STEPS):
        if t_step > 0:
            tf.get_variable_scope().reuse_variables()

        # state value is updated after processing each batch of sequences
        output, state = multi_lstm(sequence[:, t_step, :], state)

learned_representation = state

# DECODER
with tf.variable_scope('DEC_LSTM'):
    lstm = tf.nn.rnn_cell.LSTMCell(LSTM_SIZE)
    multi_lstm = tf.nn.rnn_cell.MultiRNNCell([lstm] * LSTM_LAYERS)
    state = learned_representation
    logits_stacked = None
    loss = 0.0
    for t_step in xrange(NUM_T_STEPS):
        if t_step > 0:
            tf.get_variable_scope().reuse_variables()

        # state value is updated after processing each batch of sequences
        output, state = multi_lstm(sequence[:, t_step, :], state)
        # output can be used to make next number prediction
        logits = tf.matmul(output, fc1_weight) + fc1_bias

        if logits_stacked is None:
            logits_stacked = logits
        else:
            logits_stacked = tf.concat(1, [logits_stacked, logits])

        loss += tf.reduce_sum(tf.square(logits - target[:, t_step])) / BATCH_SIZE

reg_loss = loss + LAMBDA_REG * (tf.nn.l2_loss(fc1_weight) + tf.nn.l2_loss(fc1_bias))

train = tf.train.AdamOptimizer().minimize(reg_loss)

with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())

    total_loss = 0.0
    for step in xrange(MAX_STEPS):
        seq_batch, target_batch = get_batch(BATCH_SIZE)

        feed = {sequence: seq_batch, target: target_batch}
        _, current_loss = sess.run([train, reg_loss], feed)
        if step % 10 == 0:
            print("@{}: {}".format(step, current_loss))
        total_loss += current_loss

    print('Total loss:', total_loss)

    print('### SIMPLE EVAL: ###')
    seq_batch, target_batch = get_batch(BATCH_SIZE)
    feed = {sequence: seq_batch, target: target_batch}
    prediction = sess.run([logits_stacked], feed)
    for b in xrange(BATCH_SIZE):
        print("{} -> {})".format(str(seq_batch[b, :, 0]), target_batch[b, :]))
        print(" `-> Prediction: {}".format(prediction[0][b]))

Un exemple de sortie de ceci ressemble à ceci:

### SIMPLE EVAL: ###
# [input seq] -> [target prediction]
#  `-> Prediction: [model prediction]  
[  33.   53.  113.  213.] -> [  353.   533.   753.  1013.])
 `-> Prediction: [ 19.74548721  28.3149128   33.11489105  35.06603241]
[ -17.  -32.  -77. -152.] -> [-257. -392. -557. -752.])
 `-> Prediction: [-16.38951683 -24.3657589  -29.49801064 -31.58583832]
[ -7.  -4.   5.  20.] -> [  41.   68.  101.  140.])
 `-> Prediction: [ 14.14126873  22.74848557  31.29668617  36.73633194]
...

Le modèle est un LSTM-autoencoder ayant 2 couches chacune.

Malheureusement, comme vous pouvez le voir dans les résultats, ce modèle n'apprend pas correctement la séquence. Je pourrais être le cas que je fais juste une mauvaise erreur quelque part, ou que les étapes de formation 1000-10000 sont juste beaucoup trop peu pour un LSTM. Comme je l'ai dit, je commence également à comprendre/utiliser correctement les LSTM. Mais j'espère que cela peut vous donner une inspiration concernant la mise en œuvre.

5
bsautermeister

Je pense que l'option 1 est la plus proche de l'implémentation de référence dans /tensorflow/models/rnn/ptb/reader.py

def ptb_iterator(raw_data, batch_size, num_steps):
  """Iterate on the raw PTB data.

  This generates batch_size pointers into the raw PTB data, and allows
  minibatch iteration along these pointers.

  Args:
    raw_data: one of the raw data outputs from ptb_raw_data.
    batch_size: int, the batch size.
    num_steps: int, the number of unrolls.

  Yields:
    Pairs of the batched data, each a matrix of shape [batch_size, num_steps].
    The second element of the Tuple is the same data time-shifted to the
    right by one.

  Raises:
    ValueError: if batch_size or num_steps are too high.
  """
  raw_data = np.array(raw_data, dtype=np.int32)

  data_len = len(raw_data)
  batch_len = data_len // batch_size
  data = np.zeros([batch_size, batch_len], dtype=np.int32)
  for i in range(batch_size):
    data[i] = raw_data[batch_len * i:batch_len * (i + 1)]

  Epoch_size = (batch_len - 1) // num_steps

  if Epoch_size == 0:
    raise ValueError("Epoch_size == 0, decrease batch_size or num_steps")

  for i in range(Epoch_size):
    x = data[:, i*num_steps:(i+1)*num_steps]
    y = data[:, i*num_steps+1:(i+1)*num_steps+1]
    yield (x, y)

Cependant, une autre option consiste à sélectionner un pointeur dans votre tableau de données au hasard pour chaque séquence d'apprentissage.

1
j314erre