向NeuralForecast添加模型

关于如何向NeuralForecast添加新模型的教程

前提条件

本指南假设您对NeuralForecast有较深入的了解。

我们强烈建议您首先阅读“入门指南”和“NeuralForecast地图”教程!

此外,请参考 贡献指南 了解如何为NeuralForecast贡献代码的基本信息。

介绍

本教程面向希望向NeuralForecast库添加新模型的贡献者。该库现有的模块处理深度学习模型的优化、训练、选择和评估。core类简化了针对任何数据集构建整个管道的过程,适用于工业和学术界,提供了诸如fitpredict等用户友好的方法。

向NeuralForecast添加新模型比从头开始构建新的PyTorch模型更简单。您只需编写forward方法。

它具有以下额外优势:

  • NeuralForecast中现有的模块已经实现了深度学习模型训练和评估的基本方面。
  • 与PyTorch-Lightning和Tune库集成以实现高效优化和分布式计算。
  • BaseModel类提供了通用的优化组件,如早期终止和学习率调度器。
  • 在Github上定期进行自动性能测试,以确保质量标准。
  • 用户可以轻松比较新模型与现有模型的性能和计算。
  • 为用户和贡献者提供了接触大量社区的机会。

示例:简化的MLP模型

我们将通过一个示例来介绍如何添加当前MLP模型的简化版本,该版本不包含外生变量。

在给定的时间戳\(t\)MLP模型将利用最后\(L\)个历史值\(Y_{t-L:t}\)作为输入,预测单变量目标时间的下一个\(h\)个值\(Y_{t+1:t+h}\)。下图展示了模型的示意图。

图 1. 带有自回归输入的三层多层感知器(MLP)。

0. 准备工作

请按照我们关于贡献的教程来设置您的开发环境。

以下是最重要步骤的简要列表:

  1. 创建 neuralforecast 库的一个分支。
  2. 将分支克隆到您的计算机。
  3. 设置一个包含 neuralforecast 库、核心依赖项和 nbdev 包的环境,以便在交互式笔记本中编写您的模型。

1. 继承基类(BaseWindows

该库包含 三种 类型的基础模型:BaseWindowsBaseRecurrentBaseMultivariate。在本教程中,我们将重点关注 BaseWindows 类,这是库中最常见的模型类型,其对应的模型包括 NBEATSNHITSTFTPatchTST。这三种类型之间的主要区别在于 forward 方法的采样过程和输入批次,这决定了模型的类型。

Important

如果您想添加 BaseRecurrentBaseMultivariate 模型,请在我们的 GitHub 上提一个issue。

a. 采样过程

在训练期间,基类从 TimeSeriesLoader 模块接收数据集的时间序列样本。BaseWindows 模型将从随机时间戳开始,采样大小为 input_size+h 的独立窗口。

b. BaseWindows 的超参数

熟悉基类中指定的超参数,包括 h(预测期)、input_size 和优化超参数如 learning_ratemax_steps 等。以下列表呈现了与窗口采样相关的超参数:

  • h (h):要预测的未来值的数量。
  • input_size (L):作为模型输入使用的历史值的数量。
  • batch_size (bs):训练期间加载器采样的时间序列数量。
  • valid_batch_size (v_bs):推理(验证和测试)期间加载器采样的时间序列数量。
  • windows_batch_size (w_bs):在训练期间(来源于先前的时间序列)采样的独立窗口数量,用于形成批次。
  • inference_windows_batch_size (i_bs):在推理期间采样的独立窗口数量,用于形成每个批次。用于控制 GPU 内存。

c. 输入和输出批次形状

forward 方法接收一个包含以下键的字典数据批次:

  • insample_y:时间序列的历史值。
  • insample_mask:指示时间序列可用值的掩码(可用时为1,缺失时为0)。
  • futr_exog:未来的外生协变量(如有)。
  • hist_exog:历史外生协变量(如有)。
  • stat_exog:静态外生协变量(如有)。

以下表格呈现了每个张量的形状:

张量 BaseWindows
insample_y (w_bs, L)
insample_mask (w_bs, L)
futr_exog (w_bs, L+h, n_f)
hist_exog (w_bs, L, n_h)
stat_exog (w_bs,n_s)

forward 函数应返回一个包含每个窗口下一个 h 个时间戳预测的单一张量。使用 loss 类的属性自动将输出解析为正确的形状(参见下面的示例)。

Tip

由于我们使用的是 nbdev,您可以轻松在代码中添加打印语句,并在训练期间查看张量的形状。

d. BaseWindows 的方法

BaseWindows 类包含针对所有基于窗口模型的几个常见方法,从而简化新模型的开发,防止代码重复。该类最重要的方法有:

  • _create_windows:将来自 TimeSeriesLoader 的时间序列解析为大小为 input_size+h 的独立窗口。
  • _normalization:基于 scaler 类型对每个窗口进行归一化。
  • _inv_normalization:对预测结果进行反归一化。
  • training_step:模型的训练步骤,在训练期间由 PyTorch-Lightning 的 Trainer 类调用(fit 方法)。
  • validation_step:模型的验证步骤,在验证期间由 PyTorch-Lightning 的 Trainer 类调用。
  • predict_step:模型的预测步骤,在推理期间由 PyTorch-Lightning 的 Trainer 类调用(predict 方法)。

2. 创建模型文件和类

在熟悉了 BaseWindows 类的基础知识之后,下一步是创建您的特定模型。

主要步骤如下:

  1. nbs 文件夹中创建文件(https://github.com/Nixtla/neuralforecast/tree/main/nbs)。文件名应为 models.YOUR_MODEL_NAME.ipynb
  2. 添加 nbdev 文件的头部。
  3. 在文件中导入库。
  4. 定义 __init__ 方法,包含模型的继承和特定超参数,并实例化架构。
  5. 定义 forward 方法,它接收输入批次字典并返回预测结果。

a. 模型类

首先,在nbdev文件的顶部添加以下两个单元格

#| default_exp models.mlp
Important

mlp更改为你的模型名称,使用小写字母和下划线。稍后当你运行nbdev_export时,它将在neuralforecast/models/目录中创建一个YOUR_MODEL.py脚本。

%load_ext autoreload
%autoreload 2

接下来,添加模型的依赖项。

#| export
from typing import Optional

import torch
import torch.nn as nn

from neuralforecast.losses.pytorch import MAE
from neuralforecast.common._base_windows import BaseWindows
Tip

不要忘记在这个单元格中添加#| export标签。

接下来,创建带有 initforward 方法的类。以下示例展示了简化的 MLP 模型的例子。我们将在代码后解释重要细节。

#| export
class MLP(BaseWindows): # <<---- 继承自 BaseWindows
    def __init__(self,
                 # 继承的超参数没有默认值
                 h,
                 input_size,
                 # 模型特定的超参数
                 num_layers = 2,
                 hidden_size = 1024,
                 # 继承的超参数有默认值
                 exclude_insample_y = False,
                 loss = MAE(),
                 valid_loss = None,
                 max_steps: int = 1000,
                 learning_rate: float = 1e-3,
                 num_lr_decays: int = -1,
                 early_stop_patience_steps: int =-1,
                 val_check_steps: int = 100,
                 batch_size: int = 32,
                 valid_batch_size: Optional[int] = None,
                 windows_batch_size = 1024,
                 inference_windows_batch_size = -1,
                 step_size: int = 1,
                 scaler_type: str = 'identity',
                 random_seed: int = 1,
                 num_workers_loader: int = 0,
                 drop_last_loader: bool = False,
                 **trainer_kwargs):
    # 继承 BaseWindows 类
    super(MLP, self).__init__(h=h,
                              input_size=input_size,
                              ..., # <<--- 添加所有继承的超参数
                              random_seed=random_seed,
                              **trainer_kwargs)

    # 架构
    self.num_layers = num_layers
    self.hidden_size = hidden_size

    # 多层感知器
    layers = [nn.Linear(in_features=input_size, out_features=hidden_size)]
    layers += [nn.ReLU()]
    for i in range(num_layers - 1):
        layers += [nn.Linear(in_features=hidden_size, out_features=hidden_size)]
        layers += [nn.ReLU()]
    self.mlp = nn.ModuleList(layers)

    # 带有依赖于损失的维度的适配器
    self.out = nn.Linear(in_features=hidden_size, 
                         out_features=h * self.loss.outputsize_multiplier) ## <<--- 使用 outputsize_multiplier 调整输出大小

    def forward(self, windows_batch): # <<--- 接收 windows_batch 字典
        # 解析 windows_batch
        insample_y = windows_batch['insample_y'].clone()
        # MLP
        y_pred = self.mlp(insample_y)
        # 重塑并映射到损失域
        y_pred = y_pred.reshape(batch_size, self.h, self.loss.outputsize_multiplier)
        y_pred = self.loss.domain_map(y_pred)
        return y_pred
Tip
  • 不要忘记在每个单元格上添加 #| export 标签。
  • 较大的架构,如变换器,可能需要通过使用中间函数来拆分 forward

重要注意事项

基类有许多超参数,模型必须为所有超参数提供默认值(hinput_size除外)。如果您不确定使用什么默认值,我们建议您从现有模型中复制大多数优化和采样超参数的默认值。您可以随时更改默认值。

forward步骤末尾的reshape方法用于调整输出形状。loss类包含一个outputsize_multiplier属性,用于根据loss自动调整预测的输出大小。例如,对于多分位数损失(MQLoss),模型需要为每个时间段输出每个分位数。

最后,请始终在forward的末尾包含y_pred = self.loss.domain_map(y_pred)。这对于将输出映射到损失函数的域(和形状)是必要的。例如,如果损失函数是MAE,那么它将形状为(batch_size, h, 1)的输出映射到(batch_size, h)

b. 测试和文档

nbdev允许在开发过程中测试和记录模型。它允许用户在笔记本内迭代开发,在相同环境中测试代码。请参考现有模型,例如完整的MLP模型 here。这些文件已经包含了在开发过程中使用的测试、文档和使用示例。

c. 使用nbdev将新模型导出到库中

根据贡献指南,下一步是将开发笔记本中的新模型导出到neuralforecast文件夹中的实际脚本。

要导出模型,请在终端中运行nbdev_export。您应该会在neuralforecast/models/文件夹中看到一个包含您模型的新文件。

3. 核心类和附加文件

最后,将模型添加到 core 类和附加文件中:

  1. 手动将模型添加到以下 初始化文件

  2. 使用 这里的 nbdev 文件 将模型添加到 core 类中:

    1. 将模型添加到初始模型列表中:
    from neuralforecast.models import (
    GRU, LSTM, RNN, TCN, DilatedRNN,
    MLP, NHITS, NBEATS, NBEATSx,
    TFT, VanillaTransformer,
    Informer, Autoformer, FEDformer,
    StemGNN, PatchTST
    )
    1. 将模型添加到 MODEL_FILENAME_DICT 字典中(用于 saveload 函数)。

4. 将模型添加到文档中

将模型添加到必要的文档页面中非常重要,以便每个人都能找到文档:

  1. 将模型添加到模型概览表中。
  2. 将模型添加到侧边栏以供API参考。
  3. 将模型添加到mint.json中。

5. 上传到 GitHub

祝贺你!该模型已经准备好,可以按照上述步骤在库中使用。

请按照我们的贡献指南的最后步骤将模型上传到 GitHub:这里

其中一位维护者会审查 PR,如有必要会请求更改,并将其合并到库中。

快速检查清单

  • 熟悉 BaseWindows 类的超参数和 forward 方法的输入/输出形状。
  • nbs 文件夹中创建一个包含你的模型类的笔记本:models.YOUR_MODEL_NAME.ipynb
  • 添加标题并导入库。
  • 实现 initforward 方法。
  • 使用 nbdev_export 导出模型。
  • 将模型添加到这个 初始化文件 中。
  • 将模型添加到 core这里
  • 遵循贡献指南创建 PR 以上传模型。

Give us a ⭐ on Github