向NeuralForecast添加模型
关于如何向NeuralForecast添加新模型的教程
本指南假设您对NeuralForecast有较深入的了解。
我们强烈建议您首先阅读“入门指南”和“NeuralForecast地图”教程!
此外,请参考 贡献指南 了解如何为NeuralForecast贡献代码的基本信息。
介绍
本教程面向希望向NeuralForecast库添加新模型的贡献者。该库现有的模块处理深度学习模型的优化、训练、选择和评估。core
类简化了针对任何数据集构建整个管道的过程,适用于工业和学术界,提供了诸如fit
和predict
等用户友好的方法。
向NeuralForecast添加新模型比从头开始构建新的PyTorch模型更简单。您只需编写forward方法。
它具有以下额外优势:
- NeuralForecast中现有的模块已经实现了深度学习模型训练和评估的基本方面。
- 与PyTorch-Lightning和Tune库集成以实现高效优化和分布式计算。
BaseModel
类提供了通用的优化组件,如早期终止和学习率调度器。- 在Github上定期进行自动性能测试,以确保质量标准。
- 用户可以轻松比较新模型与现有模型的性能和计算。
- 为用户和贡献者提供了接触大量社区的机会。
示例:简化的MLP模型
我们将通过一个示例来介绍如何添加当前MLP
模型的简化版本,该版本不包含外生变量。
在给定的时间戳\(t\),MLP
模型将利用最后\(L\)个历史值\(Y_{t-L:t}\)作为输入,预测单变量目标时间的下一个\(h\)个值\(Y_{t+1:t+h}\)。下图展示了模型的示意图。
0. 准备工作
请按照我们关于贡献的教程来设置您的开发环境。
以下是最重要步骤的简要列表:
- 创建
neuralforecast
库的一个分支。 - 将分支克隆到您的计算机。
- 设置一个包含
neuralforecast
库、核心依赖项和nbdev
包的环境,以便在交互式笔记本中编写您的模型。
1. 继承基类(BaseWindows
)
该库包含 三种 类型的基础模型:BaseWindows
、BaseRecurrent
和 BaseMultivariate
。在本教程中,我们将重点关注 BaseWindows
类,这是库中最常见的模型类型,其对应的模型包括 NBEATS
、NHITS
、TFT
和 PatchTST
。这三种类型之间的主要区别在于 forward
方法的采样过程和输入批次,这决定了模型的类型。
如果您想添加 BaseRecurrent
或 BaseMultivariate
模型,请在我们的 GitHub 上提一个issue。
a. 采样过程
在训练期间,基类从 TimeSeriesLoader
模块接收数据集的时间序列样本。BaseWindows
模型将从随机时间戳开始,采样大小为 input_size+h
的独立窗口。
b. BaseWindows
的超参数
熟悉基类中指定的超参数,包括 h
(预测期)、input_size
和优化超参数如 learning_rate
、max_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
类的属性自动将输出解析为正确的形状(参见下面的示例)。
由于我们使用的是 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
类的基础知识之后,下一步是创建您的特定模型。
主要步骤如下:
- 在
nbs
文件夹中创建文件(https://github.com/Nixtla/neuralforecast/tree/main/nbs)。文件名应为models.YOUR_MODEL_NAME.ipynb
。 - 添加
nbdev
文件的头部。 - 在文件中导入库。
- 定义
__init__
方法,包含模型的继承和特定超参数,并实例化架构。 - 定义
forward
方法,它接收输入批次字典并返回预测结果。
a. 模型类
首先,在nbdev
文件的顶部添加以下两个单元格。
#| default_exp models.mlp
将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
不要忘记在这个单元格中添加#| export
标签。
接下来,创建带有 init
和 forward
方法的类。以下示例展示了简化的 MLP
模型的例子。我们将在代码后解释重要细节。
#| export
class MLP(BaseWindows): # <<---- 继承自 BaseWindows
def __init__(self,
# 继承的超参数没有默认值
h,
input_size,# 模型特定的超参数
= 2,
num_layers = 1024,
hidden_size # 继承的超参数有默认值
= False,
exclude_insample_y = MAE(),
loss = None,
valid_loss int = 1000,
max_steps: float = 1e-3,
learning_rate: int = -1,
num_lr_decays: int =-1,
early_stop_patience_steps: int = 100,
val_check_steps: int = 32,
batch_size: int] = None,
valid_batch_size: Optional[= 1024,
windows_batch_size = -1,
inference_windows_batch_size int = 1,
step_size: str = 'identity',
scaler_type: int = 1,
random_seed: int = 0,
num_workers_loader: bool = False,
drop_last_loader: **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
# 多层感知器
= [nn.Linear(in_features=input_size, out_features=hidden_size)]
layers += [nn.ReLU()]
layers for i in range(num_layers - 1):
+= [nn.Linear(in_features=hidden_size, out_features=hidden_size)]
layers += [nn.ReLU()]
layers self.mlp = nn.ModuleList(layers)
# 带有依赖于损失的维度的适配器
self.out = nn.Linear(in_features=hidden_size,
=h * self.loss.outputsize_multiplier) ## <<--- 使用 outputsize_multiplier 调整输出大小
out_features
def forward(self, windows_batch): # <<--- 接收 windows_batch 字典
# 解析 windows_batch
= windows_batch['insample_y'].clone()
insample_y # MLP
= 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)
y_pred return y_pred
- 不要忘记在每个单元格上添加
#| export
标签。 - 较大的架构,如变换器,可能需要通过使用中间函数来拆分
forward
。
重要注意事项
基类有许多超参数,模型必须为所有超参数提供默认值(h
和input_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
类和附加文件中:
手动将模型添加到以下 初始化文件。
使用 这里的
nbdev
文件 将模型添加到core
类中:- 将模型添加到初始模型列表中:
from neuralforecast.models import ( GRU, LSTM, RNN, TCN, DilatedRNN, MLP, NHITS, NBEATS, NBEATSx, TFT, VanillaTransformer, Informer, Autoformer, FEDformer, StemGNN, PatchTST )
- 将模型添加到
MODEL_FILENAME_DICT
字典中(用于save
和load
函数)。
4. 将模型添加到文档中
将模型添加到必要的文档页面中非常重要,以便每个人都能找到文档:
5. 上传到 GitHub
祝贺你!该模型已经准备好,可以按照上述步骤在库中使用。
请按照我们的贡献指南的最后步骤将模型上传到 GitHub:这里。
其中一位维护者会审查 PR,如有必要会请求更改,并将其合并到库中。
快速检查清单
Give us a ⭐ on Github