LSTM: Comment gérer la non-stationnarité lors de la prédiction d'une série chronologique


8

Je veux faire des prévisions à un pas pour les séries chronologiques avec LSTM. Pour comprendre l'algorithme, je me suis construit un exemple de jouet: un simple processus autocorrélé.

def my_process(n, p, drift=0, displacement=0):
    x = np.zeros(n)

    for i in range(1, n):
        x[i] = drift * i + p * x[i-1] + (1-p) * np.random.randn()
    return x + displacement

J'ai ensuite construit un modèle LSTM dans Keras, en suivant cet exemple . J'ai simulé des processus avec une autocorrélation élevée p=0.99de la longueur n=10000, formé le réseau neuronal sur les 80 premiers% de celui-ci et l'ai laissé faire des prévisions à un pas pour les 20% restants.

Si je mets drift=0, displacement=0, tout fonctionne bien: entrez la description de l'image ici

Puis je me suis mis drift=0, displacement=10et les choses ont pris la forme d'une poire (remarquez l'échelle différente sur l'axe des y): entrez la description de l'image ici

Ce n'est pas très surprenant: les LSTM devraient être alimentés avec des données normalisées! J'ai donc normalisé les données en les redimensionnant à l'intervalle . Ouf, les choses vont bien à nouveau: [1,1]entrez la description de l'image ici

Ensuite, j'ai défini drift=0.00001, displacement=10, normalisé à nouveau les données et exécuté l'algorithme dessus. Cela ne semble pas bon: entrez la description de l'image ici

Apparemment, le LSTM ne peut pas faire face à une dérive. Que faire? (Oui, dans cet exemple de jouet, je pourrais simplement soustraire la dérive; mais pour les séries temporelles réelles, c'est beaucoup plus difficile). Je pourrais peut-être exécuter mon LSTM sur la différence au lieu de la série temporelle d'origine . Cela supprimera toute dérive constante de la série chronologique. Mais l'exécution du LSTM sur les séries chronologiques différenciées ne fonctionne pas du tout: XtXt1Xtentrez la description de l'image ici

Ma question: pourquoi mon algorithme tombe-t-il en panne lorsque je l'utilise sur les séries chronologiques différenciées? Quelle est la bonne façon de gérer les dérives dans les séries chronologiques?

Voici le code complet de mon modèle:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

from keras.layers.core import Dense, Activation, Dropout
from keras.layers.recurrent import LSTM
from keras.models import Sequential


# The LSTM model
my_model = Sequential()

my_model.add(LSTM(input_shape=(1, 1), units=50, return_sequences=True))
my_model.add(Dropout(0.2))

my_model.add(LSTM(units=100, return_sequences=False))
my_model.add(Dropout(0.2))

my_model.add(Dense(units=1))
my_model.add(Activation('linear'))

my_model.compile(loss='mse', optimizer='rmsprop')


def my_prediction(x, model, normalize=False, difference=False):
    # Plot the process x
    plt.figure(figsize=(15, 7))
    plt.subplot(121)
    plt.plot(x)
    plt.title('Original data')

    n = len(x)
    thrs = int(0.8 * n)    # Train-test split
    # Save starting values for test set to reverse differencing
    x_test_0 = x[thrs + 1]
    # Save minimum and maximum on test set to reverse normalization
    x_min = min(x[:thrs])  
    x_max = max(x[:thrs])

    if difference:
        x = np.diff(x)   # Take difference to remove drift
    if normalize:
        x = (2*x - x_min - x_max) / (x_max - x_min)   # Normalize to [-1, 1]

    # Split into train and test set. The model will be trained on one-step-ahead predictions.
    x_train, y_train, x_test, y_test = x[0:(thrs-1)], x[1:thrs], x[thrs:(n-1)], x[(thrs+1):n]

    x_train, x_test = x_train.reshape(-1, 1, 1), x_test.reshape(-1, 1, 1)
    y_train, y_test = y_train.reshape(-1, 1), y_test.reshape(-1, 1)

    # Fit the model
    model.fit(x_train, y_train, batch_size=200, epochs=10, validation_split=0.05, verbose=0)

    # Predict the test set
    y_pred = model.predict(x_test)

    # Reverse differencing and normalization
    if normalize:
        y_pred = ((x_max - x_min) * y_pred + x_max + x_min) / 2
        y_test = ((x_max - x_min) * y_test + x_max + x_min) / 2  
    if difference:
        y_pred = x_test_0 + np.cumsum(y_pred)
        y_test = x_test_0 + np.cumsum(y_test)

    # Plot estimation
    plt.subplot(122)
    plt.plot(y_pred[-100:], label='One-step-ahead-predictions')
    plt.plot(y_test[-100:], label='Actual data')
    plt.title('Prediction on test set')
    plt.legend()
    plt.show()

# Make plots
x = my_process(10000, 0.99, drift=0, displacement=0)
my_prediction(x, my_model, normalize=False, difference=False)

x = my_process(10000, 0.99, drift=0, displacement=10)
my_prediction(x, my_model, normalize=False, difference=False)

x = my_process(10000, 0.99, drift=0, displacement=10)
my_prediction(x, my_model, normalize=True, difference=False)

x = my_process(10000, 0.99, drift=0.00001, displacement=10)
my_prediction(x, my_model, normalize=True, difference=False)

x = my_process(10000, 0.99, drift=0.00001, displacement=10)
my_prediction(x, my_model, normalize=True, difference=True)

Réponses:


1

En regardant à nouveau votre processus autocorrélé:

    def my_process(n, p, drift=0, displacement=0):
        x = np.zeros(n)

        for i in range(1, n):
            x[i] = drift * i + p * x[i-1] + (1-p) * np.random.randn()
    return x + displacement

Il semble que les choses se dégradent lorsque la valeur de displacementest élevée. Cela a du sens, comme vous le dites, car les LSTM ont besoin de données normalisées.

Le driftparamètre est un peu différent. Lorsqu'une petite quantité de dérive est incluse, étant donné qu'elle pest grande, la quantité de dérive est similaire à la quantité de bruit aléatoire ajoutée via np.random.randn().

Dans les graphiques pour drift=0.00001, displacement=10, il semble que les prédictions seraient bonnes, sauf pour le décalage y. Pour cette raison, je pense que la racine du problème est toujours dans le displacementparamètre, pas le driftparamètre. La différence, comme cela a été fait, n'aidera pas avec le displacementparamètre; au lieu de cela, il corrige la dérive.

Je ne peux pas le dire à partir de votre code, mais il semble que ce displacementn'était peut- être pas le cas dans model.predict. C'est ma meilleure supposition.


Merci de l'avoir regardé! La différenciation aidera cependant avec le displacementparamètre: . De plus, le dernier exemple utilise la normalisation ( après la différenciation), donc cela ne devrait pas être un problème ...(Xt+1+c)-(Xt+c)=Xt+1-Xt
Elias Strehle

1
Salut encore, d'accord, bon point! Hmm. Je pense que ce que vous appelez «dérive», j'appellerais une moyenne mobile (j'espère). Vous pouvez essayer d'inclure une sorte de covariable dans votre modèle pour tenir compte de la moyenne mobile. Idéalement, un LSTM découvrirait cela tout seul, bien sûr, mais ici, il semble être bloqué.
StatsSorceress

Cela m'inquiète un peu, car je veux appliquer les LSTM aux cours des actions. Ils ont une dérive / moyenne mobile, et l'approche standard (au moins en statistique) consiste à appliquer la différenciation, c'est-à-dire à utiliser les rendements plutôt que les prix. Je voudrais donc comprendre pourquoi cela ne semble pas fonctionner (même pour un modèle aussi simple) avec les LSTM.
Elias Strehle

1
Avez-vous tracé les passes avant et arrière avec vos valeurs d'origine par rapport aux valeurs différenciées? Je me demande s'il y a une sorte de problème de gradient qui disparaît avec les valeurs différenciées. Les LSTM sont bien sûr plus robustes à cela, mais ils peuvent rencontrer ce type de problèmes, donc cela pourrait valoir le coup d'œil.
StatsSorceress

1

Lorsque vous choisissez x_minet x_max, vous le choisissez 1:thresholdseul. Étant donné que votre série augmente de façon monotone (enfin presque ..), les valeurs de test sont toutes des valeurs> 1. Ceci, le modèle LSTM n'a jamais vu pendant l'entraînement.

Est-ce pour cela que vous voyez ce que vous voyez?

Pouvez-vous essayer la même chose avec x_minet x_maxprovenant de l'ensemble de données à la place?


Cela pourrait fonctionner dans mon exemple de jouet; mais si j'utilise des LSTM pour réellement prédire quelque chose, cela nécessitera de regarder vers l'avenir.
Elias Strehle
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.