用 Python 轻松实现时间序列预测:Darts 时间序列深度编码器 TimeSeries Deep Encoder

文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。

Darts

Darts 是一个 Python 库,用于对时间序列进行用户友好型预测和异常检测。它包含多种模型,从 ARIMA 等经典模型到深度神经网络。所有预测模型都能以类似 scikit-learn 的方式使用 fit()predict() 函数。该库还可以轻松地对模型进行回溯测试,将多个模型的预测结果结合起来,并将外部数据考虑在内。Darts 支持单变量和多变量时间序列和模型。基于 ML 的模型可以在包含多个时间序列的潜在大型数据集上进行训练,其中一些模型还为概率预测提供了丰富的支持。

时间序列深度编码器

本笔记本演示了如何使用 Darts 的 TiDEModel 并将其与 NHiTSModel 进行基准测试。

TiDE(Time-series Dense Encoder)是一种纯深度学习编码器-解码器架构。它的特殊之处在于,时间解码器可以帮助减轻异常样本对预测的影响(参见论文中的图 4)。

查看原始论文和模型描述:http://arxiv.org/abs/2304.08424

# 如果在本地运行,修复 python 路径
from utils import fix_pythonpath_if_working_locally

fix_pythonpath_if_working_locally()
%matplotlib inline
%load_ext autoreload
%autoreload 2
%matplotlib inline
import warnings

import matplotlib.pyplot as plt
import pandas as pd
import torch
from pytorch_lightning.callbacks.early_stopping import EarlyStopping

from darts.dataprocessing.transformers.scaler import Scaler
from darts.datasets import AusBeerDataset
from darts.metrics import mae, mse
from darts.models import NHiTSModel, TiDEModel

warnings.filterwarnings("ignore")
import logging

logging.disable(logging.CRITICAL)

模型参数设置

样板代码并不有趣,尤其是在训练多个模型以比较性能的背景下。为了避免这种情况,我们使用一个通用配置,该配置可用于任何 Darts 的 TorchForecastingModel

关于这些参数的一些有趣之处:

  • 梯度裁剪: 通过在批次的梯度上设置上限来缓解反向传播期间的梯度爆炸问题。

  • 学习率: 模型的大部分学习发生在早期 epoch。随着训练的进行,降低学习率通常有助于微调模型。话虽如此,它也可能导致严重的过拟合。

  • 早停: 为了避免过拟合,我们可以使用早停。它在验证集上监控某个指标,并在该指标基于自定义条件不再改善时停止训练。

optimizer_kwargs = {
    "lr": 1e-3,
}

# PyTorch Lightning Trainer 参数
pl_trainer_kwargs = {
    "gradient_clip_val": 1,
    "max_epochs": 200,
    "accelerator": "auto",
    "callbacks": [],
}

# 学习率调度器
lr_scheduler_cls = torch.optim.lr_scheduler.ExponentialLR
lr_scheduler_kwargs = {
    "gamma": 0.999,
}

# 早停(稍后需要为每个模型重置)
# 此设置在验证损失连续 10 个 epoch 未下降超过 1e-3 时停止训练
early_stopping_args = {
    "monitor": "val_loss",
    "patience": 10,
    "min_delta": 1e-3,
    "mode": "min",
}

#
common_model_args = {
    "input_chunk_length": 12,  # 回溯窗口
    "output_chunk_length": 12,  # 预测/前瞻窗口
    "optimizer_kwargs": optimizer_kwargs,
    "pl_trainer_kwargs": pl_trainer_kwargs,
    "lr_scheduler_cls": lr_scheduler_cls,
    "lr_scheduler_kwargs": lr_scheduler_kwargs,
    "likelihood": None,  # 使用似然进行概率预测
    "save_checkpoints": True,  # 检查点用于恢复最佳模型状态
    "force_reset": True,
    "batch_size": 256,
    "random_state": 42,
}

数据加载与准备

我们考虑澳大利亚以兆升为单位的季度啤酒销量。

在训练之前,我们将数据分成训练集、验证集和测试集。模型将从训练集中学习,使用验证集来确定何时停止训练,最后在测试集上进行评估。

为了避免从验证集和测试集中泄露信息,我们基于训练集的特性对数据进行缩放。

series = AusBeerDataset().load()

train, temp = series.split_after(0.6)
val, test = temp.split_after(0.5)
train.plot(label="train")
val.plot(label="val")
test.plot(label="test")
scaler = Scaler()  # 默认使用 sklearn 的 MinMaxScaler
train = scaler.fit_transform(train)
val = scaler.transform(val)
test = scaler.transform(test)

img

模型配置

使用已建立的共享参数,我们可以看到 NHiTS 和 TiDE 使用了默认参数。唯一的例外是,TiDE 在有和没有可逆实例归一化的情况下都进行了测试。

然后我们遍历模型字典并训练所有模型。使用早停时,保存检查点很重要。这允许我们越过最佳模型配置继续训练,然后在训练完成后恢复最优权重。

# 创建模型
model_nhits = NHiTSModel(**common_model_args, model_name="hi")

model_tide = TiDEModel(
    **common_model_args, use_reversible_instance_norm=False, model_name="tide0"
)

model_tide_rin = TiDEModel(
    **common_model_args, use_reversible_instance_norm=True, model_name="tide1"
)

models = {
    "NHiTS": model_nhits,
    "TiDE": model_tide,
    "TiDE+RIN": model_tide_rin,
}
# 训练模型并从最佳状态/检查点加载模型
for name, model in models.items():
    # 早停需要为每个模型重置
    pl_trainer_kwargs["callbacks"] = [
        EarlyStopping(
            **early_stopping_args,
        )
    ]

    model.fit(
        series=train,
        val_series=val,
        verbose=False,
    )
    # 从检查点加载会返回一个新的模型对象,我们将其存储在 models 字典中
    models[name] = model.load_from_checkpoint(model_name=model.model_name, best=True)
# 我们将预测 `pred_input` 结束后接下来的 `pred_steps` 个点
pred_steps = common_model_args["output_chunk_length"] * 2
pred_input = test[:-pred_steps]

fig, ax = plt.subplots(figsize=(15, 5))
pred_input.plot(label="input")
test[-pred_steps:].plot(label="ground truth", ax=ax)

result_accumulator = {}
# 用每个模型进行预测并计算/存储与测试集的指标
for model_name, model in models.items():
    pred_series = model.predict(n=pred_steps, series=pred_input)
    pred_series.plot(label=model_name, ax=ax)

    result_accumulator[model_name] = {
        "mae": mae(test, pred_series),
        "mse": mse(test, pred_series),
    }

img

结果

在这种情况下,原始 TiDE 与 NHiTS 的准确性相似。包含可逆实例归一化(RINorm)极大地帮助改善了 TiDE 的预测(请记住,这只是一个示例,并不总是保证性能会提高)。

results_df = pd.DataFrame.from_dict(result_accumulator, orient="index")
results_df.plot.bar()
<Axes: >

img

风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

船长Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值