概率预测是量化目标变量未来不确定性的自然解答。此任务需要对以下条件预测分布进行建模:

P(yt+1:t+H    y:t)\mathbb{P}(\mathbf{y}_{t+1:t+H} \;|\; \mathbf{y}_{:t})

我们将展示如何使用 NeuralForecast,通过结合经典的 长短期记忆网络 (LSTM) 和 神经层次插值网络 (NHITS) 以及 多分位数损失函数 (MQLoss) 来解决此任务。

MQLoss(yτ,[y^τ(q1),y^τ(q2),,y^τ(Q)])=1HqQL(yτ,y^τ(q))\mathrm{MQLoss}(y_{\tau}, [\hat{y}^{(q1)}_{\tau},\hat{y}^{(q2)}_{\tau},\dots,\hat{y}^{(Q)}_{\tau}]) = \frac{1}{H} \sum_{q} \mathrm{QL}(y_{\tau}, \hat{y}^{(q)}_{\tau})

在本笔记中,我们将:
1. 安装 NeuralForecast 库
2. 探索 M4 小时数据。
3. 训练 LSTM 和 NHITS 模型
4. 可视化 LSTM/NHITS 预测区间。

您可以使用 Google Colab 的 GPU 来运行这些实验。

1. 安装 NeuralForecast

!pip install neuralforecast

实用函数

下面定义的辅助函数 plot_grid 将有助于绘制不同的时间序列和不同模型的预测。

import logging
import warnings

import torch
from utilsforecast.plotting import plot_series
warnings.filterwarnings("ignore")

2. 加载 M4 数据

为了测试目的,我们将使用来自 M4 竞赛的小时数据集。

import pandas as pd
Y_train_df = pd.read_csv('https://auto-arima-results.s3.amazonaws.com/M4-Hourly.csv')
Y_test_df = pd.read_csv(
    'https://auto-arima-results.s3.amazonaws.com/M4-Hourly-test.csv'
).rename(columns={'y': 'y_test'})

在此示例中,我们将使用一部分数据以避免等待时间过长。您可以根据需要修改序列数量。

n_series = 8
uids = Y_train_df['unique_id'].unique()[:n_series]
Y_train_df = Y_train_df.query('unique_id in @uids')
Y_test_df = Y_test_df.query('unique_id in @uids')
plot_series(Y_train_df, Y_test_df)

3. 模型训练

core.NeuralForecast 提供了我们 PyTorch 模型集合的高级接口。NeuralForecast 通过 models=[LSTM(...), NHITS(...)] 列表实例化,并针对预测任务进行配置。

  • horizon 参数控制预测提前的步数,在此示例中为提前 48 小时(2 天)。
  • 使用 levels=[80,90]MQLoss 将网络的输出专门用于 80% 和 90% 的预测区间。
  • max_steps=2000 控制网络训练的持续时间。

有关更多网络实例化详情,请查阅其文档

from neuralforecast import NeuralForecast
from neuralforecast.losses.pytorch import MQLoss
from neuralforecast.models import LSTM, NHITS
logging.getLogger('pytorch_lightning').setLevel(logging.ERROR)
torch.set_float32_matmul_precision('high')
horizon = 48
levels = [80, 90]
models = [LSTM(input_size=3*horizon, h=horizon,
               loss=MQLoss(level=levels), max_steps=1000),
          NHITS(input_size=7*horizon, h=horizon,
                n_freq_downsample=[24, 12, 1],
                loss=MQLoss(level=levels), max_steps=2000),]
nf = NeuralForecast(models=models, freq=1)
Seed set to 1
Seed set to 1

库中的所有模型都是全局的,这意味着 Y_train_df 中的所有时间序列都会在共享优化过程中用于训练一个共享参数的单一模型。这是深度学习模型在预测文献中最常见的实践,被称为“交叉学习”。

nf.fit(df=Y_train_df)
Y_hat_df = nf.predict()
Y_hat_df.head()
Predicting: |          | 0/? [00:00<?, ?it/s]
Predicting: |          | 0/? [00:00<?, ?it/s]
unique_iddsLSTM-medianLSTM-lo-90LSTM-lo-80LSTM-hi-80LSTM-hi-90NHITS-medianNHITS-lo-90NHITS-lo-80NHITS-hi-80NHITS-hi-90
0H1701650.919861526.705933551.696289748.392456777.889526615.786743582.732117584.717468640.011841647.147034
1H1702547.724487439.353394463.725464638.429626663.398987569.632324524.486023522.324402578.411560594.515076
2H1703514.851074421.289917443.166443589.451782608.560425518.858887503.183411501.016968536.081543549.701050
3H1704485.141418403.336914421.090546547.966492567.057800495.627869476.579742468.514069498.171600527.931091
4H1705462.695831383.011108399.126282522.579224543.981750481.584534468.134857472.723450496.198975513.859985
Y_test_df = Y_test_df.merge(Y_hat_df, how='left', on=['unique_id', 'ds']).rename(columns=lambda x: x.replace('-median', ''))

4. 绘制预测结果

在此,我们通过绘制预测区间来结束分析,并验证 LSTMNHITS 都取得了出色的结果。

考虑输出 [NHITS-lo-90.0, NHITS-hi-90.0],它代表了 NHITS 网络的 80% 预测区间;其下限表示第 5 百分位数(或 0.05 分位数),而其上限表示第 95 百分位数(或 0.95 分位数)。对于训练良好的模型,我们期望目标值有 90% 的时间落在此区间内。

LSTM

plot_series(Y_train_df, Y_test_df, level=levels, models=['LSTM'])

NHITS

plot_series(Y_train_df, Y_test_df, level=levels, models=['NHITS'])

参考文献