Example below is self-complete.
I have recently switched from using darts (where forecast horizons etc) are all handled for me to keras because I wanted to integrate with other libraries such as shap etc.
As part of attempting to setup a multi-step forecast, I have created a very simple linear function to ensure I understand all the basic components associated with the keras model pipeline. Model performance on single step predictions of the validation set is R^2: 0.943, MAPE: 0.102%. I obviously expect performance to degrade with multi-step forecasts, especially since the input size is smaller than the number of steps I hope to perform. However, this paper (scroll to highlighted text created by link), uses 7 days input periods to forecast one day ahead, and autoregressively generates 28 forecasts using LSTM cells on data far noiser than a linear trend.
Can anyone spot the basic mistake I have made somewhere?
import numpy as np
import matplotlib.pyplot as plt
import keras
from sklearn.metrics import r2_score, mean_absolute_percentage_error
horizon = 1
forecast_steps = 28
input_size = 7
batch_size = 32
training_size = 5000
linear = np.arange(training_size) / training_size
val = (np.arange(100) + training_size) / training_size
dataset_train = keras.preprocessing.timeseries_dataset_from_array(linear[:-horizon], linear[input_size:], sequence_length=input_size, batch_size=batch_size)
dataset_val = keras.preprocessing.timeseries_dataset_from_array(val[:-horizon], val[input_size:], sequence_length=input_size, batch_size=batch_size)
model = keras.models.Sequential()
model.add(keras.layers.Input(shape=(input_size, 1), dtype='float64', batch_size=batch_size))
model.add(keras.layers.LSTM(units=1, dtype='float64'))
model.add(keras.layers.Dense(1, dtype='float64'))
model.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-3), loss="mse")
history = model.fit(
dataset_train,
epochs=500,
validation_data=dataset_val,
callbacks=[keras.callbacks.EarlyStopping(monitor="val_loss", min_delta=0, patience=7, mode='min')],
)
Multi-Step Forecast:
last_steps = np.zeros((1, input_size))
last_steps[0, :] = linear[:input_size]
pred = np.zeros(28)
for x, y in dataset_train.take(1):
for ii in range(28):
pred[ii] = model.predict(last_steps, verbose=False, batch_size=1)[0][0]
new_last_steps = np.zeros((1, input_size))
new_last_steps[0, :-1] = last_steps[0, 1:]
new_last_steps[0, -1] = pred[ii]
last_steps[:] = new_last_steps[:]
plt.plot(pred, 'go')
plt.plot(linear[input_size: input_size + forecast_steps])
Single Step Model Performance Evaluation:
train_pred = model.predict(dataset_train)
plt.plot(linear)
plt.plot(np.arange(len(train_pred)) + input_size, train_pred)
plt.show()
print(r2_score(linear[input_size:], train_pred))
print(mean_absolute_percentage_error(linear[input_size:], train_pred) * 100)
val_pred = model.predict(dataset_val)
plt.plot(val)
plt.plot(np.arange(len(val_pred)) + input_size, val_pred)
plt.show()
print(r2_score(val[input_size:], val_pred))
print(mean_absolute_percentage_error(val[input_size:], val_pred) * 100)
Questions Reviewed:
Single to Multi-Step – Accepted Answer suggested out of the box solutions I have used before and am trying to replace
Broken Multi-Step – I attempted fixes provided and they had no impact on performance