循环神经网络模型¶
在这个笔记本中,我们展示了一个如何将RNNs与darts结合使用的示例。如果你是darts的新手,我们建议你首先跟随 快速开始 笔记本。
[1]:
# fix python path if working locally
from utils import fix_pythonpath_if_working_locally
fix_pythonpath_if_working_locally()
[2]:
%load_ext autoreload
%autoreload 2
%matplotlib inline
[3]:
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import pandas as pd
import shutil
from sklearn.preprocessing import MinMaxScaler
from tqdm import tqdm_notebook as tqdm
import matplotlib.pyplot as plt
from darts import TimeSeries
from darts.dataprocessing.transformers import Scaler
from darts.models import RNNModel, ExponentialSmoothing, BlockRNNModel
from darts.metrics import mape
from darts.utils.statistics import check_seasonality, plot_acf
from darts.datasets import AirPassengersDataset, SunspotsDataset
from darts.utils.timeseries_generation import datetime_attribute_timeseries
import warnings
warnings.filterwarnings("ignore")
import logging
logging.disable(logging.CRITICAL)
循环模型¶
Darts 包含两个循环预测模型类:RNNModel
和 BlockRNNModel
。
RNNModel
是完全循环的,因为在预测时,输出是使用这些输入计算的:
前一个目标值,它将被设置为第一个预测的最后一个已知目标值,而对于所有其他预测,它将被设置为前一个预测值
前一个隐藏状态
当前的协变量(如果模型是使用协变量训练的)
因此,具有预测范围 n
的预测是通过 RNNModel
预测的 n
次迭代创建的,并且需要知道 n
个未来的协变量。该模型适用于目标序列高度依赖于提前已知的协变量的预测问题。
BlockRNNModel
有一个递归编码器阶段,该阶段对其输入进行编码,还有一个全连接神经网络解码器阶段,该阶段根据编码器阶段的最后一个隐藏状态生成一个长度为 output_chunk_length
的预测。因此,该模型生成‘块’形式的预测,并且仅限于查看与输入目标序列具有相同时间索引的协变量。
航空乘客示例¶
这是一个高度依赖协变量的数据集。知道月份可以告诉我们很多关于季节性成分的信息,而年份则决定了趋势成分的影响。这两个协变量在将来都是已知的,因此 RNNModel
类是这个问题的首选。
[4]:
# Read data:
series = AirPassengersDataset().load()
# Create training and validation sets:
train, val = series.split_after(pd.Timestamp("19590101"))
# Normalize the time series (note: we avoid fitting the transformer on the validation set)
transformer = Scaler()
train_transformed = transformer.fit_transform(train)
val_transformed = transformer.transform(val)
series_transformed = transformer.transform(series)
# create month and year covariate series
year_series = datetime_attribute_timeseries(
pd.date_range(start=series.start_time(), freq=series.freq_str, periods=1000),
attribute="year",
one_hot=False,
)
year_series = Scaler().fit_transform(year_series)
month_series = datetime_attribute_timeseries(
year_series, attribute="month", one_hot=True
)
covariates = year_series.stack(month_series)
cov_train, cov_val = covariates.split_after(pd.Timestamp("19590101"))
让我们训练一个LSTM神经网络。如果要使用普通的RNN或GRU,请分别将``’LSTM’替换为
’RNN’或
’GRU’``。
[5]:
my_model = RNNModel(
model="LSTM",
hidden_dim=20,
dropout=0,
batch_size=16,
n_epochs=300,
optimizer_kwargs={"lr": 1e-3},
model_name="Air_RNN",
log_tensorboard=True,
random_state=42,
training_length=20,
input_chunk_length=14,
force_reset=True,
save_checkpoints=True,
)
在接下来的内容中,我们可以直接将整个 covariates
系列作为 future_covariates
参数提供给模型;模型将切片这些协变量,并仅使用它需要的内容来训练以预测目标 train_transformed
:
[6]:
my_model.fit(
train_transformed,
future_covariates=covariates,
val_series=val_transformed,
val_future_covariates=covariates,
verbose=True,
)
[6]:
<darts.models.forecasting.rnn_model.RNNModel at 0x7f800691edc0>
查看验证集上的预测¶
使用“当前”模型 - 即,训练过程结束时的模型:
[7]:
def eval_model(model):
pred_series = model.predict(n=26, future_covariates=covariates)
plt.figure(figsize=(8, 5))
series_transformed.plot(label="actual")
pred_series.plot(label="forecast")
plt.title("MAPE: {:.2f}%".format(mape(pred_series, val_transformed)))
plt.legend()
eval_model(my_model)
使用根据验证损失在训练过程中获得的最佳模型:
[8]:
best_model = RNNModel.load_from_checkpoint(model_name="Air_RNN", best=True)
eval_model(best_model)
回测¶
让我们回测我们的 RNN
模型,看看它在6个月的预测范围内表现如何:
[9]:
backtest_series = my_model.historical_forecasts(
series_transformed,
future_covariates=covariates,
start=pd.Timestamp("19590101"),
forecast_horizon=6,
retrain=False,
verbose=True,
)
[10]:
plt.figure(figsize=(8, 5))
series_transformed.plot(label="actual")
backtest_series.plot(label="backtest")
plt.legend()
plt.title("Backtest, starting Jan 1959, 6-months horizon")
print(
"MAPE: {:.2f}%".format(
mape(
transformer.inverse_transform(series_transformed),
transformer.inverse_transform(backtest_series),
)
)
)
MAPE: 2.71%
月度太阳黑子¶
现在让我们尝试一个更具挑战性的时间序列;自1749年以来每月太阳黑子的数量。首先,我们从数据中构建时间序列,并检查其周期性。
[11]:
series_sunspot = SunspotsDataset().load()
series_sunspot.plot()
check_seasonality(series_sunspot, max_lag=240)
[11]:
(True, 125)
[12]:
plot_acf(series_sunspot, 125, max_lag=240) # ~11 years seasonality
[13]:
train_sp, val_sp = series_sunspot.split_after(pd.Timestamp("19401001"))
transformer_sunspot = Scaler()
train_sp_transformed = transformer_sunspot.fit_transform(train_sp)
val_sp_transformed = transformer_sunspot.transform(val_sp)
series_sp_transformed = transformer_sunspot.transform(series_sunspot)
[14]:
my_model_sun = BlockRNNModel(
model="GRU",
input_chunk_length=125,
output_chunk_length=36,
hidden_dim=10,
n_rnn_layers=1,
batch_size=32,
n_epochs=100,
dropout=0.1,
model_name="sun_GRU",
nr_epochs_val_period=1,
optimizer_kwargs={"lr": 1e-3},
log_tensorboard=True,
random_state=42,
force_reset=True,
)
my_model_sun.fit(train_sp_transformed, val_series=val_sp_transformed, verbose=True)
[14]:
<darts.models.forecasting.block_rnn_model.BlockRNNModel at 0x7f80095963d0>
为了评估我们的模型,我们将使用3年的预测视野在验证集上模拟历史预测。为了加快速度,我们将只查看每第10个预测。为了便于比较,我们还将拟合一个指数平滑模型。
[15]:
# Compute the backtest predictions with the two models
pred_series = my_model_sun.historical_forecasts(
series_sp_transformed,
start=pd.Timestamp("19401001"),
forecast_horizon=36,
stride=10,
retrain=False,
last_points_only=True,
verbose=True,
)
pred_series_ets = ExponentialSmoothing(seasonal_periods=120).historical_forecasts(
series_sp_transformed,
start=pd.Timestamp("19401001"),
forecast_horizon=36,
stride=10,
retrain=True,
last_points_only=True,
verbose=True,
)
[16]:
val_sp_transformed.plot(label="actual")
pred_series.plot(label="our RNN")
pred_series_ets.plot(label="ETS")
plt.legend()
print("RNN MAPE:", mape(pred_series, val_sp_transformed))
print("ETS MAPE:", mape(pred_series_ets, val_sp_transformed))
RNN MAPE: 73.19010018398568
ETS MAPE: 116.63584309419007