Tôi muốn thực hiện các dự đoán trước một bước cho chuỗi thời gian với LSTM. Để hiểu thuật toán, tôi đã xây dựng cho mình một ví dụ về đồ chơi: Một quy trình tự tương quan đơn giản.
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
Sau đó, tôi đã xây dựng một mô hình LSTM trong Keras, theo ví dụ này . Tôi mô phỏng các quá trình với tự tương quan cao p=0.99
độ dài n=10000
, được đào tạo mạng lưới thần kinh trên 80% đầu tiên của nó và để cho nó làm một bước trước những dự đoán cho remaning 20%.
Nếu tôi đặt drift=0, displacement=0
, mọi thứ đều hoạt động tốt:
Sau đó, tôi đặt drift=0, displacement=10
và mọi thứ trở thành hình quả lê (chú ý tỷ lệ khác nhau trên trục y):
Điều này không đáng ngạc nhiên lắm: LSTM nên được cung cấp dữ liệu chuẩn hóa! Vì vậy, tôi đã chuẩn hóa dữ liệu bằng cách thay đổi kích thước dữ liệu thành khoảng . Phew, mọi thứ lại tốt đẹp:
Sau đó, tôi thiết lập drift=0.00001, displacement=10
, chuẩn hóa dữ liệu một lần nữa và chạy thuật toán trên nó. Điều này trông có vẻ không ổn:
Rõ ràng LSTM không thể đối phó với sự trôi dạt. Phải làm sao (Vâng, trong ví dụ về đồ chơi này, tôi chỉ có thể trừ đi sự trôi dạt; nhưng đối với chuỗi thời gian thực, điều này khó hơn nhiều). Có lẽ tôi có thể chạy LSTM của mình với sự khác biệt thay vì chuỗi thời gian ban đầu
Câu hỏi của tôi: Tại sao thuật toán của tôi bị hỏng khi tôi sử dụng nó trên chuỗi thời gian khác nhau? Một cách tốt để đối phó với trôi dạt trong chuỗi thời gian là gì?
Đây là mã đầy đủ cho mô hình của tôi:
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)
displacement