长期概率预测

长远预测是具有挑战性的,因为预测的波动性计算复杂性。为了解决这个问题,我们创建了NHITS模型,并提供了NeuralForecast 库的代码。NHITS通过分层插值和多速率输入处理,专门针对时间序列的不同频率进行部分输出。我们使用斯图登特t分布来建模目标时间序列。NHITS将为每个时间戳输出分布参数。

在这个笔记本中,我们展示如何在ETTm2基准数据集上使用NHITS进行概率预测。该数据集包含两个电力变压器在两个站点的数据显示点,包括负载和油温。

我们将向您展示如何加载数据、训练以及进行自动超参数调优,以实现最佳性能,甚至超越最新的Transformer架构,同时计算成本仅为其的1/50(快50倍)。

您可以使用Google Colab通过GPU运行这些实验。

在Colab中打开

1. 库

%%capture
!pip install neuralforecast datasetsforecast

2. 加载ETTm2数据

LongHorizon类将自动下载完整的ETTm2数据集并进行处理。

它返回三个数据框:Y_df包含目标变量的值,X_df包含外生日历特征,S_df包含每个时间序列的静态特征(ETTm2没有)。在这个例子中,我们将仅使用Y_df

如果您想使用自己的数据,只需替换Y_df。请确保使用长格式,并具有与我们的数据集类似的结构。

import pandas as pd
from datasetsforecast.long_horizon import LongHorizon
# 将此更改为自己的数据以尝试模型
Y_df, _, _ = LongHorizon.load(directory='./', group='ETTm2')
Y_df['ds'] = pd.to_datetime(Y_df['ds'])

# 在这个练习中,我们将选取960个时间戳作为验证和测试数据。
n_time = len(Y_df.ds.unique())
val_size = 96*10
test_size = 96*10

Y_df.groupby('unique_id').head(2)
unique_id ds y
0 HUFL 2016-07-01 00:00:00 -0.041413
1 HUFL 2016-07-01 00:15:00 -0.185467
57600 HULL 2016-07-01 00:00:00 0.040104
57601 HULL 2016-07-01 00:15:00 -0.214450
115200 LUFL 2016-07-01 00:00:00 0.695804
115201 LUFL 2016-07-01 00:15:00 0.434685
172800 LULL 2016-07-01 00:00:00 0.434430
172801 LULL 2016-07-01 00:15:00 0.428168
230400 MUFL 2016-07-01 00:00:00 -0.599211
230401 MUFL 2016-07-01 00:15:00 -0.658068
288000 MULL 2016-07-01 00:00:00 -0.393536
288001 MULL 2016-07-01 00:15:00 -0.659338
345600 OT 2016-07-01 00:00:00 1.018032
345601 OT 2016-07-01 00:15:00 0.980124
Important

数据框必须包含所有 ['unique_id', 'ds', 'y'] 列。 确保 y 列没有缺失值或非数值。

接下来,绘制标记验证和训练拆分的 HUFL 变量。

import matplotlib.pyplot as plt
# 我们将绘制变压器的温度图。 
# 并标记验证集和训练集的划分
u_id = 'HUFL'
x_plot = pd.to_datetime(Y_df[Y_df.unique_id==u_id].ds)
y_plot = Y_df[Y_df.unique_id==u_id].y.values

x_val = x_plot[n_time - val_size - test_size]
x_test = x_plot[n_time - test_size]

fig = plt.figure(figsize=(10, 5))
fig.tight_layout()

plt.plot(x_plot, y_plot)
plt.xlabel('Date', fontsize=17)
plt.ylabel('OT [15 min temperature]', fontsize=17)

plt.axvline(x_val, color='black', linestyle='-.')
plt.axvline(x_test, color='black', linestyle='-.')
plt.text(x_val, 5, '  Validation', fontsize=12)
plt.text(x_test, 3, '  Test', fontsize=12)

plt.grid()
plt.show()
plt.close()

3. 超参数选择与预测

AutoNHITS 类将自动执行超参数调整,使用 Tune 库,探索用户定义或默认的搜索空间。模型是根据验证集上的误差进行选择的,最佳模型随后被存储并在推理过程中使用。

AutoNHITS.default_config 属性包含一个建议的超参数空间。在这里,我们按照论文的超参数指定了一个不同的搜索空间。请注意,1000 次随机梯度步骤 足以达到最先进的性能。欢迎随意调整这个空间。

from ray import tune

from neuralforecast.auto import AutoNHITS
from neuralforecast.core import NeuralForecast

from neuralforecast.losses.pytorch import DistributionLoss

import logging
logging.getLogger("pytorch_lightning").setLevel(logging.WARNING)
horizon = 96 # 24小时 = 4 × 15分钟。

# 使用你自己的配置或AutoNHITS.default_config
nhits_config = {
       "learning_rate": tune.choice([1e-3]),                                     # 初始学习率
       "max_steps": tune.choice([1000]),                                         # SGD步骤数
       "input_size": tune.choice([5 * horizon]),                                 # 输入大小 = 乘数 * 时间范围
       "batch_size": tune.choice([7]),                                           # Windows中的系列数量
       "windows_batch_size": tune.choice([256]),                                 # 批处理中的窗口数量
       "n_pool_kernel_size": tune.choice([[2, 2, 2], [16, 8, 1]]),               # MaxPool's Kernelsize
       "n_freq_downsample": tune.choice([[168, 24, 1], [24, 12, 1], [1, 1, 1]]), # Interpolation expressivity ratios
       "activation": tune.choice(['ReLU']),                                      # Type of non-linear activation
       "n_blocks":  tune.choice([[1, 1, 1]]),                                    # Blocks per each 3 stacks
       "mlp_units":  tune.choice([[[512, 512], [512, 512], [512, 512]]]),        # 2 512-Layers per block for each stack
       "interpolation_mode": tune.choice(['linear']),                            # Type of multi-step interpolation
       "random_seed": tune.randint(1, 10),
       "scaler_type": tune.choice(['robust']),
       "val_check_steps": tune.choice([100])
    }
Tip

有关不同空间选项的更多信息,例如列表和连续区间,请参见 https://docs.ray.io/en/latest/tune/index.html。

要实例化 AutoNHITS,您需要定义:

  • h: 预测范围
  • loss: 训练损失。使用 DistributionLoss 生成概率预测。
  • config: 超参数搜索空间。如果为 None,则 AutoNHITS 类将使用预定义的建议超参数空间。
  • num_samples: 探索的配置数量。
models = [AutoNHITS(h=horizon,
                    loss=DistributionLoss(distribution='StudentT', level=[80, 90]), 
                    config=nhits_config,
                    num_samples=5)]

通过实例化一个NeuralForecast对象来拟合模型,使用以下必需参数:

  • models:一个模型列表。

  • freq:一个字符串,指示数据的频率。 (参见pandas的可用频率。)

# 拟合与预测
nf = NeuralForecast(
    models=models,
    freq='15min')

cross_validation 方法允许您模拟多个历史预测,极大地简化了通过用 fitpredict 方法替换 for 循环的流程。

对于时间序列数据,交叉验证是通过在历史数据上定义一个滑动窗口并预测随后的时间段来完成的。这种形式的交叉验证使我们能够更好地估计模型的预测能力,覆盖更广泛的时间实例,同时保持训练集中的数据连续,这是我们模型所要求的。

cross_validation 方法将使用验证集进行超参数选择,然后为测试集生成预测。

%%capture
Y_hat_df = nf.cross_validation(df=Y_df, val_size=val_size,
                               test_size=test_size, n_windows=None)

4. 可视化

最后,我们将预测结果与 Y_df 数据集合并,并绘制预测图。

Y_hat_df = Y_hat_df.reset_index(drop=True)
Y_hat_df = Y_hat_df[(Y_hat_df['unique_id']=='OT') & (Y_hat_df['cutoff']=='2018-02-11 12:00:00')]
Y_hat_df = Y_hat_df.drop(columns=['y','cutoff'])
plot_df = Y_df.merge(Y_hat_df, on=['unique_id','ds'], how='outer').tail(96*10+50+96*4).head(96*2+96*4)

plt.plot(plot_df['ds'], plot_df['y'], c='black', label='True')
plt.plot(plot_df['ds'], plot_df['AutoNHITS-median'], c='blue', label='median')
plt.fill_between(x=plot_df['ds'], 
                    y1=plot_df['AutoNHITS-lo-90'], y2=plot_df['AutoNHITS-hi-90'],
                    alpha=0.4, label='level 90')
plt.legend()
plt.grid()
plt.plot()

参考文献

Cristian Challu, Kin G. Olivares, Boris N. Oreshkin, Federico Garza, Max Mergenthaler-Canseco, Artur Dubrawski (2021). NHITS: 用于时间序列预测的神经层次插值。接受于AAAI 2023。

Give us a ⭐ on Github