LSTM: Cara menangani nonstasioneritas saat memprediksi deret waktu

8

Saya ingin melakukan prediksi selangkah lebih maju untuk seri waktu dengan LSTM. Untuk memahami algoritme, saya membuat contoh mainan untuk diri saya sendiri: Proses autokorelasi sederhana.

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

Lalu saya membangun model LSTM di Keras, mengikuti contoh ini . Saya mensimulasikan proses dengan autokorelasi tinggi p=0.99panjang n=10000, melatih jaringan saraf pada 80% pertama dan membiarkannya melakukan prediksi selangkah lebih maju untuk remaning 20%.

Jika saya atur drift=0, displacement=0, semuanya bekerja dengan baik: masukkan deskripsi gambar di sini

Lalu saya atur drift=0, displacement=10dan segalanya menjadi berbentuk pir (perhatikan skala yang berbeda pada sumbu y): masukkan deskripsi gambar di sini

Ini tidak terlalu mengejutkan: LSTM harus diumpankan dengan data yang dinormalisasi! Jadi saya menormalkan data dengan mengubah ukurannya ke interval[1,1]. Fiuh, semuanya baik-baik saja lagi: masukkan deskripsi gambar di sini

Lalu saya atur drift=0.00001, displacement=10, menormalkan kembali data dan menjalankan algoritma di atasnya. Ini tidak terlihat bagus: masukkan deskripsi gambar di sini

Rupanya LSTM tidak bisa berurusan dengan penyimpangan. Apa yang harus dilakukan? (Ya, dalam contoh mainan ini saya hanya bisa mengurangi penyimpangan; tetapi untuk seri waktu dunia nyata, ini jauh lebih sulit). Mungkin saya bisa menjalankan LSTM saya pada perbedaanXtXt1 bukannya seri waktu asli Xt. Ini akan menghapus penyimpangan konstan dari deret waktu. Tetapi menjalankan LSTM pada deret waktu yang berbeda tidak bekerja sama sekali: masukkan deskripsi gambar di sini

Pertanyaan saya: Mengapa algoritma saya rusak ketika saya menggunakannya pada deret waktu yang berbeda? Apa cara yang baik untuk menangani drift dalam deret waktu?

Ini kode lengkap untuk model saya:

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
sumber

Jawaban:

1

Melihat lagi proses autokorelasi Anda:

    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

Sepertinya hal-hal mogok ketika nilai displacementtinggi. Ini masuk akal, seperti yang Anda katakan, karena LSTM membutuhkan data yang dinormalisasi.

The driftparameter yang berbeda sedikit. Ketika sejumlah kecil drift dimasukkan, karena pbesar, jumlah drift mirip dengan jumlah noise acak yang ditambahkan melalui np.random.randn().

Di plot untuk drift=0.00001, displacement=10, sepertinya prediksi akan baik-baik saja kecuali untuk pergeseran-y. Karena itu, saya pikir akar masalahnya masih dalam displacementparameter, bukan driftparameter. Membedakan, seperti yang telah dilakukan, tidak akan membantu dengan displacementparameter; sebagai gantinya, itu mengoreksi drift.

Saya tidak tahu dari kode Anda, tetapi sepertinya kode displacementitu tidak diperhitungkan model.predict. Itu tebakan terbaik saya.

StorScerceress
sumber
Terima kasih telah melihatnya! Perbedaan akan membantu dengan displacementparameter:(Xt+1+c)(Xt+c)=Xt+1Xt. Juga, contoh terakhir menggunakan normalisasi ( setelah dibedakan), sehingga seharusnya tidak menjadi masalah ...
Elias Strehle
1
Hai lagi, oke, poin bagus! Hmm. Saya pikir apa yang Anda sebut 'melayang', saya akan menyebutnya rata-rata bergerak (saya harap). Anda dapat mencoba memasukkan beberapa jenis kovariat ke dalam model Anda untuk memperhitungkan rata-rata bergerak. Idealnya LSTM akan menemukan itu sendiri, tentu saja, tetapi di sini tampaknya macet.
StatsSorceress
Saya sedikit khawatir tentang hal ini karena saya ingin menerapkan LSTM pada harga saham. Mereka memang memiliki drift / moving average, dan pendekatan standar (dalam statistik, setidaknya) adalah menerapkan perbedaan, yaitu menggunakan pengembalian bukan harga. Jadi saya ingin memahami mengapa ini tampaknya tidak berhasil (bahkan untuk model yang sederhana) dengan LSTMs.
Elias Strehle
1
Sudahkah Anda melacak melewati maju dan mundur dengan nilai asli Anda vs nilai yang berbeda? Saya bertanya-tanya apakah ada semacam masalah gradien menghilang terjadi dengan nilai-nilai dibedakan. LSTM tentu saja lebih kuat untuk ini, tetapi mereka dapat mengalami masalah seperti ini, jadi mungkin patut dilihat.
StatsSorceress
1

Ketika Anda memilih x_mindan x_max, Anda memilihnya 1:thresholdsendiri. Karena seri Anda meningkat secara monoton (hampir ..), nilai pengujian adalah semua nilai> 1. Ini, model LSTM tidak pernah terlihat selama pelatihan sama sekali.

Apakah itu sebabnya Anda melihat apa yang Anda lihat?

Bisakah Anda mencoba hal yang sama dengan x_mindan x_maxberasal dari seluruh dataset sebagai gantinya?

Jigidi Sarnath
sumber
Ini mungkin bekerja dalam contoh mainan saya; tetapi jika saya menggunakan LSTM untuk benar-benar memprediksi sesuatu, ini akan membutuhkan melihat ke masa depan.
Elias Strehle