在《使用 Transformers 进行概率时间序列预测》的第一部分里,我们为大家介绍了传统时间序列预测和基于 Transformers 的方法,也一步步准备好了训练所需的数据集并定义了环境、模型、转换和 InstanceSplitter。本篇内容将包含从数据加载器,到前向传播、训练、推理和展望未来发展等精彩内容。

创建 PyTorch 数据加载器

有了数据,下一步需要创建 PyTorch DataLoaders。它允许我们批量处理成对的 (输入, 输出) 数据,即 (past_values , future_values)。

from gluonts.itertools import Cyclic, IterableSlice, PseudoShuffled
from gluonts.torch.util import IterableDataset
from torch.utils.data import DataLoader from typing import Iterable def create_train_dataloader(
config: PretrainedConfig,
freq,
data,
batch_size: int,
num_batches_per_epoch: int,
shuffle_buffer_length: Optional[int] = None,
**kwargs,
) -> Iterable:
PREDICTION_INPUT_NAMES = [
"static_categorical_features",
"static_real_features",
"past_time_features",
"past_values",
"past_observed_mask",
"future_time_features",
] TRAINING_INPUT_NAMES = PREDICTION_INPUT_NAMES + [
"future_values",
"future_observed_mask",
] transformation = create_transformation(freq, config)
transformed_data = transformation.apply(data, is_train=True) # we initialize a Training instance
instance_splitter = create_instance_splitter(
config, "train"
) + SelectFields(TRAINING_INPUT_NAMES) # the instance splitter will sample a window of
# context length + lags + prediction length (from the 366 possible transformed time series)
# randomly from within the target time series and return an iterator.
training_instances = instance_splitter.apply(
Cyclic(transformed_data)
if shuffle_buffer_length is None
else PseudoShuffled(
Cyclic(transformed_data),
shuffle_buffer_length=shuffle_buffer_length,
)
) # from the training instances iterator we now return a Dataloader which will
# continue to sample random windows for as long as it is called
# to return batch_size of the appropriate tensors ready for training!
return IterableSlice(
iter(
DataLoader(
IterableDataset(training_instances),
batch_size=batch_size,
**kwargs,
)
),
num_batches_per_epoch,
)
def create_test_dataloader(
config: PretrainedConfig,
freq,
data,
batch_size: int,
**kwargs,
):
PREDICTION_INPUT_NAMES = [
"static_categorical_features",
"static_real_features",
"past_time_features",
"past_values",
"past_observed_mask",
"future_time_features",
] transformation = create_transformation(freq, config)
transformed_data = transformation.apply(data, is_train=False) # we create a Test Instance splitter which will sample the very last
# context window seen during training only for the encoder.
instance_splitter = create_instance_splitter(
config, "test"
) + SelectFields(PREDICTION_INPUT_NAMES) # we apply the transformations in test mode
testing_instances = instance_splitter.apply(transformed_data, is_train=False) # This returns a Dataloader which will go over the dataset once.
return DataLoader(IterableDataset(testing_instances), batch_size=batch_size, **kwargs)
train_dataloader = create_train_dataloader(
config=config,
freq=freq,
data=train_dataset,
batch_size=256,
num_batches_per_epoch=100,
) test_dataloader = create_test_dataloader(
config=config,
freq=freq,
data=test_dataset,
batch_size=64,
)

让我们检查第一批:

batch = next(iter(train_dataloader))
for k,v in batch.items():
print(k,v.shape, v.type()) >>> static_categorical_features torch.Size([256, 1]) torch.LongTensor
static_real_features torch.Size([256, 1]) torch.FloatTensor
past_time_features torch.Size([256, 181, 2]) torch.FloatTensor
past_values torch.Size([256, 181]) torch.FloatTensor
past_observed_mask torch.Size([256, 181]) torch.FloatTensor
future_time_features torch.Size([256, 24, 2]) torch.FloatTensor
future_values torch.Size([256, 24]) torch.FloatTensor
future_observed_mask torch.Size([256, 24]) torch.FloatTensor

可以看出,我们没有将 input_idsattention_mask 提供给编码器 (训练 NLP 模型时也是这种情况),而是提供 past_values,以及 past_observed_maskpast_time_featuresstatic_categorical_featuresstatic_real_features 几项数据。

解码器的输入包括 future_valuesfuture_observed_maskfuture_time_featuresfuture_values 可以看作等同于 NLP 训练中的 decoder_input_ids

我们可以参考 Time Series Transformer 文档 以获得对它们中每一个的详细解释。

前向传播

让我们对刚刚创建的批次执行一次前向传播:

# perform forward pass
outputs = model(
past_values=batch["past_values"],
past_time_features=batch["past_time_features"],
past_observed_mask=batch["past_observed_mask"],
static_categorical_features=batch["static_categorical_features"],
static_real_features=batch["static_real_features"],
future_values=batch["future_values"],
future_time_features=batch["future_time_features"],
future_observed_mask=batch["future_observed_mask"],
output_hidden_states=True
)
print("Loss:", outputs.loss.item())

>>> Loss: 9.141253471374512

目前,该模型返回了损失值。这是由于解码器会自动将 future_values 向右移动一个位置以获得标签。这允许计算预测结果和标签值之间的误差。

另请注意,解码器使用 Causal Mask 来避免预测未来,因为它需要预测的值在 future_values 张量中。

训练模型

是时候训练模型了!我们将使用标准的 PyTorch 训练循环。

这里我们用到了 Accelerate 库,它会自动将模型、优化器和数据加载器放置在适当的 device 上。

from accelerate import Accelerator
from torch.optim import Adam accelerator = Accelerator()
device = accelerator.device model.to(device)
optimizer = Adam(model.parameters(), lr=1e-3) model, optimizer, train_dataloader = accelerator.prepare(
model, optimizer, train_dataloader,
) for epoch in range(40):
model.train()
for batch in train_dataloader:
optimizer.zero_grad()
outputs = model(
static_categorical_features=batch["static_categorical_features"].to(device),
static_real_features=batch["static_real_features"].to(device),
past_time_features=batch["past_time_features"].to(device),
past_values=batch["past_values"].to(device),
future_time_features=batch["future_time_features"].to(device),
future_values=batch["future_values"].to(device),
past_observed_mask=batch["past_observed_mask"].to(device),
future_observed_mask=batch["future_observed_mask"].to(device),
)
loss = outputs.loss # Backpropagation
accelerator.backward(loss)
optimizer.step() print(loss.item())

推理

在推理时,建议使用 generate() 方法进行自回归生成,类似于 NLP 模型。

预测的过程会从测试实例采样器中获得数据。采样器会将数据集的每个时间序列的最后 context_length 那么长时间的数据采样出来,然后输入模型。请注意,这里需要把提前已知的 future_time_features 传递给解码器。

该模型将从预测分布中自回归采样一定数量的值,并将它们传回解码器最终得到预测输出:

model.eval()

forecasts = []

for batch in test_dataloader:
outputs = model.generate(
static_categorical_features=batch["static_categorical_features"].to(device),
static_real_features=batch["static_real_features"].to(device),
past_time_features=batch["past_time_features"].to(device),
past_values=batch["past_values"].to(device),
future_time_features=batch["future_time_features"].to(device),
past_observed_mask=batch["past_observed_mask"].to(device),
)
forecasts.append(outputs.sequences.cpu().numpy())

该模型输出一个表示结构的张量 (batch_size, number of samples, prediction length)。

下面的输出说明: 对于大小为 64 的批次中的每个示例,我们将获得接下来 24 个月内的 100 个可能的值:

forecasts[0].shape

>>> (64, 100, 24)

我们将垂直堆叠它们,以获得测试数据集中所有时间序列的预测:

forecasts = np.vstack(forecasts)
print(forecasts.shape) >>> (366, 100, 24)

我们可以根据测试集中存在的样本值,根据真实情况评估生成的预测。这里我们使用数据集中的每个时间序列的 MASEsMAPE 指标 (metrics) 来评估:

from evaluate import load
from gluonts.time_feature import get_seasonality mase_metric = load("evaluate-metric/mase")
smape_metric = load("evaluate-metric/smape") forecast_median = np.median(forecasts, 1) mase_metrics = []
smape_metrics = []
for item_id, ts in enumerate(test_dataset):
training_data = ts["target"][:-prediction_length]
ground_truth = ts["target"][-prediction_length:]
mase = mase_metric.compute(
predictions=forecast_median[item_id],
references=np.array(ground_truth),
training=np.array(training_data),
periodicity=get_seasonality(freq))
mase_metrics.append(mase["mase"]) smape = smape_metric.compute(
predictions=forecast_median[item_id],
references=np.array(ground_truth),
)
smape_metrics.append(smape["smape"])
print(f"MASE: {np.mean(mase_metrics)}")

>>> MASE: 1.361636922541396

print(f"sMAPE: {np.mean(smape_metrics)}")

>>> sMAPE: 0.17457818831512306

我们还可以单独绘制数据集中每个时间序列的结果指标,并观察到其中少数时间序列对最终测试指标的影响很大:

plt.scatter(mase_metrics, smape_metrics, alpha=0.3)
plt.xlabel("MASE")
plt.ylabel("sMAPE")
plt.show()

为了根据基本事实测试数据绘制任何时间序列的预测,我们定义了以下辅助绘图函数:

import matplotlib.dates as mdates

def plot(ts_index):
fig, ax = plt.subplots() index = pd.period_range(
start=test_dataset[ts_index][FieldName.START],
periods=len(test_dataset[ts_index][FieldName.TARGET]),
freq=freq,
).to_timestamp() # Major ticks every half year, minor ticks every month,
ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1, 7)))
ax.xaxis.set_minor_locator(mdates.MonthLocator()) ax.plot(
index[-2*prediction_length:],
test_dataset[ts_index]["target"][-2*prediction_length:],
label="actual",
) plt.plot(
index[-prediction_length:],
np.median(forecasts[ts_index], axis=0),
label="median",
) plt.fill_between(
index[-prediction_length:],
forecasts[ts_index].mean(0) - forecasts[ts_index].std(axis=0),
forecasts[ts_index].mean(0) + forecasts[ts_index].std(axis=0),
alpha=0.3,
interpolate=True,
label="+/- 1-std",
)
plt.legend()
plt.show()

例如:

plot(334)

我们如何与其他模型进行比较? Monash Time Series Repository 有一个测试集 MASE 指标的比较表。我们可以将自己的结果添加到其中作比较:

Dataset SES Theta TBATS ETS (DHR-)ARIMA PR CatBoost FFNN DeepAR N-BEATS WaveNet Transformer (Our)
Tourism Monthly 3.306 1.649 1.751 1.526 1.589 1.678 1.699 1.582 1.409 1.574 1.482 1.361

请注意,我们的模型击败了所有已知的其他模型 (另请参见相应 论文 中的表 2) ,并且我们没有做任何超参数优化。我们仅仅花了 40 个完整训练调参周期来训练 Transformer。

当然,我们应该谦虚。从历史发展的角度来看,现在认为神经网络解决时间序列预测问题是正途,就好比当年的论文得出了 “你需要的就是 XGBoost” 的结论。我们只是很好奇,想看看神经网络能带我们走多远,以及 Transformer 是否会在这个领域发挥作用。这个特定的数据集似乎表明它绝对值得探索。

下一步

我们鼓励读者尝试我们的 Jupyter Notebook 和来自 Hugging Face Hub 的其他时间序列数据集,并替换适当的频率和预测长度参数。对于您的数据集,需要将它们转换为 GluonTS 的惯用格式,在他们的 文档 里有非常清晰的说明。我们还准备了一个示例 Notebook,向您展示如何将数据集转换为 Hugging Face 数据集格式。

正如时间序列研究人员所知,人们对“将基于 Transformer 的模型应用于时间序列”问题很感兴趣。传统 vanilla Transformer 只是众多基于注意力 (Attention) 的模型之一,因此需要向库中补充更多模型。

目前没有什么能妨碍我们继续探索对多变量时间序列 (multivariate time series) 进行建模,但是为此需要使用多变量分布头 (multivariate distribution head) 来实例化模型。目前已经支持了对角独立分布 (diagonal independent distributions),后续会增加其他多元分布支持。请继续关注未来的博客文章以及其中的教程。

路线图上的另一件事是时间序列分类。这需要将带有分类头的时间序列模型添加到库中,例如用于异常检测这类任务。

当前的模型会假设日期时间和时间序列值都存在,但在现实中这可能不能完全满足。例如 WOODS 给出的神经科学数据集。因此,我们还需要对当前模型进行泛化,使某些输入在整个流水线中可选。

最后,NLP/CV 领域从大型预训练模型 中获益匪浅,但据我们所知,时间序列领域并非如此。基于 Transformer 的模型似乎是这一研究方向的必然之选,我们迫不及待地想看看研究人员和从业者会发现哪些突破!


英文原文: Probabilistic Time Series Forecasting with Transformers

译者、排版: zhongdongy (阿东)

下篇 | 使用 🤗 Transformers 进行概率时间序列预测的更多相关文章

  1. facebook开源的prophet时间序列预测工具---识别多种周期性、趋势性(线性,logistic)、节假日效应,以及部分异常值

    简单使用 代码如下 这是官网的quickstart的内容,csv文件也可以下到,这个入门以后后面调试加入其它参数就很简单了. import pandas as pd import numpy as n ...

  2. Python中利用LSTM模型进行时间序列预测分析

    时间序列模型 时间序列预测分析就是利用过去一段时间内某事件时间的特征来预测未来一段时间内该事件的特征.这是一类相对比较复杂的预测建模问题,和回归分析模型的预测不同,时间序列模型是依赖于事件发生的先后顺 ...

  3. 时间序列预测之--ARIMA模型

    什么是 ARIMA模型 ARIMA模型的全称叫做自回归移动平均模型,全称是(ARIMA, Autoregressive Integrated Moving Average Model).也记作ARIM ...

  4. Kesci: Keras 实现 LSTM——时间序列预测

    博主之前参与的一个科研项目是用 LSTM 结合 Attention 机制依据作物生长期内气象环境因素预测作物产量.本篇博客将介绍如何用 keras 深度学习的框架搭建 LSTM 模型对时间序列做预测. ...

  5. 使用tensorflow的lstm网络进行时间序列预测

    https://blog.csdn.net/flying_sfeng/article/details/78852816 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog. ...

  6. 基于 Keras 用 LSTM 网络做时间序列预测

    目录 基于 Keras 用 LSTM 网络做时间序列预测 问题描述 长短记忆网络 LSTM 网络回归 LSTM 网络回归结合窗口法 基于时间步的 LSTM 网络回归 在批量训练之间保持 LSTM 的记 ...

  7. (数据科学学习手札40)tensorflow实现LSTM时间序列预测

    一.简介 上一篇中我们较为详细地铺垫了关于RNN及其变种LSTM的一些基本知识,也提到了LSTM在时间序列预测上优越的性能,本篇就将对如何利用tensorflow,在实际时间序列预测任务中搭建模型来完 ...

  8. Pytorch循环神经网络LSTM时间序列预测风速

    #时间序列预测分析就是利用过去一段时间内某事件时间的特征来预测未来一段时间内该事件的特征.这是一类相对比较复杂的预测建模问题,和回归分析模型的预测不同,时间序列模型是依赖于事件发生的先后顺序的,同样大 ...

  9. 腾讯技术工程 | 基于Prophet的时间序列预测

    预测未来永远是一件让人兴奋而又神奇的事.为此,人们研究了许多时间序列预测模型.然而,大部分的时间序列模型都因为预测的问题过于复杂而效果不理想.这是因为时间序列预测不光需要大量的统计知识,更重要的是它需 ...

  10. TensorFlow实现时间序列预测

    常常会碰到各种各样时间序列预测问题,如商场人流量的预测.商品价格的预测.股价的预测,等等.TensorFlow新引入了一个TensorFlow Time Series库(以下简称为TFTS),它可以帮 ...

随机推荐

  1. 春秋云境 CVE-2022-24663复现

    靶标介绍: 远程代码执行漏洞,任何订阅者都可以利用该漏洞发送带有"短代码"参数设置为 PHP Everywhere 的请求,并在站点上执行任意 PHP 代码.P.S. 存在常见用户 ...

  2. 关于linux更改root用户下面的鼠标样式

    前言 这几天一直研究关于 lightmd 显示管理器(也就是刚进入系统的用户登录界面)的主题配置问题,我发现鼠标样式和登录个人用户的鼠标样式不一样(之前我也发现了,懒得捣鼓)今天抽出时间研究了一下,这 ...

  3. 2020最新Java面试题及答案(带完整目录).pdf

    一.JVM 二.Java集合 三.Java多线程并发 四.Java基础 五.Spring原理 六.微服务 七.Netty与RPC 八.网络 九.日志 十.RabbitMQ 十一.MongoDB 十二. ...

  4. VUE 使用md5对用户登录密码进行加密传输

    VUE 使用md5对用户登录密码进行加密传输到数据库 前言 第一步 npm下载js-md5依赖包 第二步 引入js-md5 直接在需要使用md5加密的页面引入 全局挂载,将js-md5添加到vue原型 ...

  5. nuxt.js安装使用

    首先要学会看文档,https://www.nuxtjs.cn/guide/configuration 一.创建项目,并运行 终端运行 npx create-nuxt-app <项目名> ( ...

  6. JavaScript:显式转换数据类型:如何转换为数值、字符串和布尔值类型?

    JS的运算符以及某些内置函数,会自动进行数据类型的转换,方便计算,即隐式转换数据类型: 但是很多时候,我们希望可以手动控制数据类型的转换,即显示转换数据类型: 转换为字符串 String()函数 使用 ...

  7. BBS项目(二): 登录功能 首页导航条搭建 首页主体部分 个人站点页面搭建 文章分类与标签 日期归档

    目录 登录功能 pillow模块生成验证码 前端发送ajax请求 后端auth模块校验 sweetalert弹窗提示登录失败 首页导航条搭建 修改密码 退出登录 首页主体部分 首页前端框架搭建 adm ...

  8. [python]《Python编程快速上手:让繁琐工作自动化》学习笔记1

    1. 模式匹配与正则表达式笔记(第7章)(代码下载) 正则表达式,又称规则表达式.(英语:Regular Expression,在代码中常简写为regex.regexp或RE),计算机科学的一个概念. ...

  9. Hadoop详解(01)-概论

    Hadoop详解(01)概论 概念 大数据(Big Data):指无法在一定时间范围内用常规软件工具进行捕捉.管理和处理的数据集合,是需要新处理模式才能具有更强的决策力.洞察发现力和流程优化能力的海量 ...

  10. 【深入浅出 Yarn 架构与实现】4-4 RM 管理 Application

    在 YARN 中,Application 是指应用程序,它可能启动多个运行实例,每个运行实例由 -个 ApplicationMaster 与一组该 ApplicationMaster 启动的任务组成, ...