Quiero hacer predicciones de un paso adelante para series temporales con LSTM. Para comprender el algoritmo, construí un ejemplo de juguete: un proceso simple autocorrelacionado.
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
Luego construí un modelo LSTM en Keras, siguiendo este ejemplo . Simulé procesos con alta autocorrelación p=0.99
de longitud n=10000
, entrené la red neuronal en el primer 80% y le permití hacer predicciones de un paso adelante para el 20% restante.
Si configuro drift=0, displacement=0
, todo funciona bien:
Luego configuré drift=0, displacement=10
y las cosas se pusieron en forma de pera (observe la diferente escala en el eje y):
Esto no es terriblemente sorprendente: ¡los LSTM deben alimentarse con datos normalizados! Así que normalicé los datos volviéndolos a escalar al intervalo . Uf, las cosas están bien otra vez:
Luego configuré drift=0.00001, displacement=10
, normalicé los datos nuevamente y ejecuté el algoritmo sobre ellos. Esto no se ve bien:
Aparentemente, el LSTM no puede lidiar con una deriva. ¿Qué hacer? (Sí, en este ejemplo de juguete simplemente podría restar la deriva; pero para las series de tiempo real, esto es mucho más difícil). Tal vez podría ejecutar mi LSTM en la diferencia lugar de la serie de tiempo original . Esto eliminará cualquier deriva constante de la serie de tiempo. Pero ejecutar el LSTM en las series de tiempo diferenciadas no funciona en absoluto:
Mi pregunta: ¿Por qué mi algoritmo se descompone cuando lo uso en las series de tiempo diferenciadas? ¿Cuál es una buena manera de lidiar con las desviaciones en series de tiempo?
Aquí está el código completo para mi modelo:
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
embargo, la diferenciación ayudará con el parámetro: . Además, el último ejemplo utiliza la normalización ( después de la diferenciación), por lo que no debería ser un problema ...