LSTM: Umgang mit Nichtstationarität bei der Vorhersage einer Zeitreihe

8

Ich möchte mit LSTM Vorhersagen für Zeitreihen mit einem Schritt voraus machen. Um den Algorithmus zu verstehen, habe ich mir ein Spielzeugbeispiel erstellt: Ein einfacher autokorrelierter Prozess.

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

Dann habe ich in Keras ein LSTM-Modell erstellt, das diesem Beispiel folgt . Ich simulierte Prozesse mit hoher Autokorrelation p=0.99der Länge n=10000, trainierte das neuronale Netzwerk auf den ersten 80% und ließ es einen Schritt voraus Vorhersagen für die verbleibenden 20% machen.

Wenn ich setze drift=0, displacement=0, funktioniert alles gut: Geben Sie hier die Bildbeschreibung ein

Dann setzte ich drift=0, displacement=10und die Dinge wurden birnenförmig (beachten Sie die unterschiedliche Skala auf der y-Achse): Geben Sie hier die Bildbeschreibung ein

[1,1]Geben Sie hier die Bildbeschreibung ein

Dann habe ich gesetzt drift=0.00001, displacement=10, die Daten wieder normalisiert und den Algorithmus darauf ausgeführt. Das sieht nicht gut aus: Geben Sie hier die Bildbeschreibung ein

XtXt1XtGeben Sie hier die Bildbeschreibung ein

Meine Frage: Warum fällt mein Algorithmus aus, wenn ich ihn für differenzierte Zeitreihen verwende? Was ist ein guter Weg, um mit Abweichungen in Zeitreihen umzugehen?

Hier ist der vollständige Code für mein Modell:

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)
Elias Strehle
quelle

Antworten:

1

Betrachten Sie noch einmal Ihren autokorrelierten Prozess:

    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

Es sieht so aus, als würden die Dinge zusammenbrechen, wenn der Wert von displacementhoch ist. Dies ist, wie Sie sagen, sinnvoll, da LSTMs normalisierte Daten benötigen.

Der driftParameter ist etwas anders. Wenn eine kleine Driftmenge enthalten ist, da diese pgroß ist, ist die Driftmenge ähnlich der Menge an zufälligem Rauschen, über das hinzugefügt wird np.random.randn().

In den Darstellungen für drift=0.00001, displacement=10sieht es so aus, als wären die Vorhersagen bis auf die y-Verschiebung in Ordnung. Aus diesem Grund denke ich, dass die Wurzel des Problems immer noch im displacementParameter liegt, nicht im driftParameter. Das Differenzieren hilft nicht mit dem displacementParameter. Stattdessen wird die Drift korrigiert.

Ich kann es nicht an Ihrem Code erkennen, aber es sieht so aus, als ob der Code displacementin nicht berücksichtigt wurde model.predict. Das ist meine beste Vermutung.

StatsSorceress
quelle
displacement(Xt+1+c)(Xt+c)=Xt+1Xt
1
Hallo nochmal, okay, guter Punkt! Hmm. Ich denke, was Sie "Drift" nennen, würde ich einen gleitenden Durchschnitt nennen (ich hoffe). Sie können versuchen, eine Art Kovariate in Ihr Modell aufzunehmen, um den gleitenden Durchschnitt zu berücksichtigen. Idealerweise würde ein LSTM das natürlich von selbst entdecken, aber hier scheint es festzustecken.
StatsSorceress
Ich bin etwas besorgt darüber, weil ich LSTMs auf Aktienkurse anwenden möchte. Sie haben einen Drift / gleitenden Durchschnitt, und der Standardansatz (zumindest in der Statistik) besteht darin, Differenzierung anzuwenden, dh Renditen anstelle von Preisen zu verwenden. Daher möchte ich verstehen, warum dies mit LSTMs (selbst für ein so einfaches Modell) nicht zu funktionieren scheint.
Elias Strehle
1
Haben Sie die Vorwärts- und Rückwärtsdurchläufe mit Ihren ursprünglichen Werten im Vergleich zu differenzierten Werten verfolgt? Ich frage mich, ob mit den differenzierten Werten ein verschwindendes Gradientenproblem auftritt. LSTMs sind natürlich robuster, aber sie können auf solche Probleme stoßen, daher ist es möglicherweise einen Blick wert.
StatsSorceress
1

Wenn Sie x_minund wählen x_max, wählen Sie es 1:thresholdallein aus. Da Ihre Serie monoton ansteigt (fast ..), sind die Testwerte alle Werte> 1. Dies hat das LSTM-Modell während des Trainings überhaupt nicht gesehen.

Sehen Sie deshalb, was Sie sehen?

Können Sie dasselbe mit x_minund x_maxaus dem gesamten Datensatz versuchen ?

Jigidi Sarnath
quelle
Dies könnte in meinem Spielzeugbeispiel funktionieren. Aber wenn ich LSTMs verwende, um tatsächlich etwas vorherzusagen, müsste ich in die Zukunft schauen.
Elias Strehle