共计 9529 个字符,预计需要花费 24 分钟才能阅读完成。
目前来看表格类的数据的解决还是树型的构造占据了主导地位。然而在工夫序列预测中,深度学习神经网络是有可能超过传统技术的。
为什么须要更加古代的工夫序列模型?
专为单个工夫序列(无论是多变量还是单变量)创立模型的状况当初曾经很少见了。当初的工夫序列钻研方向都是多元的,并且具备各种散布,其中蕴含更多探索性因素包含:缺失数据、趋势、季节性、波动性、漂移和常见事件等等。
通过间接预测指标变量往往是不够的,咱们劣势还心愿零碎可能产生预测区间,显示预测的不确定性水平。
并且除了历史数据外,所有的变量都应该思考在内,这样能够建设一个在预测能力方面具备竞争力的模型。
所以古代工夫序列模型应该思考到以下几点:
- 模型应该思考多个工夫序列,现实状况下应该思考数千个工夫序列。
- 模型中应该应用单维或多维序列。
- 除了时态数据之外,模型还应该可能应用过来数据。这个限度影响了所有的自回归技术 (ARIMA 模型),包含亚马逊的 DeepAR。
- 非工夫的内部动态因素也应加以思考。
- 模型须要具备高度的适应性。即便工夫序列比较复杂或蕴含一些噪声,模型也能够应用季节性“奢侈”预测器预测。并且应该可能辨别这些实例。
- 如果能够的话模型能够进行多步预测性能。也就是不止预测下一个值们须要预测下几个值。
- 间接对指标变量预测是不够的。模型可能产生预测区间,这样显示预测的不确定性水平。
- 生产环境应该可能顺利地集成最优模型,该模型也应该易于应用和了解。
什么是 Temporal Fusion Transformer?
Temporal Fusion Transformer(TFT) 是一个基于注意力的深度神经网络,它优化了性能和可解释性,顶层架构如下图所示。
TFT 架构的长处如下:
可能应用丰盛的特色:TFT 反对三种不同类型的特色: 外生类别 / 动态变量,也称为时不变特色;具备已知输出到将来的时态数据,仅到目前已知的时态数据;具备未知输出的将来时态数据。
区间预测:TFT 应用分位数损失函数来产生除理论预测之外的预测区间。
异构工夫序列: 容许训练具备不同散布的多个工夫序列。TFT 设计将解决分为两个局部: 部分解决,集中于特定事件的特色和全局解决,记录所有工夫序列的个别特色。
可解释性:TFT 的外围是基于 transformer 的体系结构。该模型引入的多头注意力机制,在须要对模型进行解释时提供了对于特色重要性的额定常识。另外一个性能良好的 DNN 实现是 Multi-Horizon Quantile Recurrent Forecaster(MQRNN)。然而它没有提供如何解释这些特色重要水平的领导。
性能: 在基准测试中,TFT 优于基于 DNN 的模型,如 DeepAR、MQRNN 和深度状态空间模型(Deep Space-State Models)以及传统统计模型 (ARIMA,DSSM 等)。
与传统办法不同,TFT 的多头注意力提供了特色可解释性。通过 TFT 的多头注意力增加一个新的矩阵或分组,容许不同的头共享一些权重,而后能够依据季节性剖析来解释这些全红的含意。
如何应用 Temporal Fusion Transformer 进行预测?
本文将在一个十分小的数据集上训练 TemporalFusionTransformer,以证实它甚至在仅 20k 样本上也能很好地工作。
1、加载数据
本文中将应用 Kaggle Stallion 数据集,该数据集跟踪几种饮料的销售状况。咱们的指标是预测在接下来的六个月中,库存单位 (SKU) 销售的商品数量或由零售商代理机构销售的产品数量。
每月约有 21,000 条历史销售记录。除了过来的销售量,咱们还有销售价格、代理商地位、节假日等非凡日子以及该行业销售总量的数据。
数据集曾经是正确的格局。然而,它还短少一些咱们关注的信息,咱们须要增加是一个工夫索引,它随着每个工夫步长减少一个。
from pytorch_forecasting.data.examples import get_stallion_data
data = get_stallion_data()
# add time index
data["time_idx"] = data["date"].dt.year * 12 + data["date"].dt.month
data["time_idx"] -= data["time_idx"].min()
# add additional features
data["month"] = data.date.dt.month.astype(str).astype("category") # categories have be strings
data["log_volume"] = np.log(data.volume + 1e-8)
data["avg_volume_by_sku"] = data.groupby(["time_idx", "sku"], observed=True).volume.transform("mean")
data["avg_volume_by_agency"] = data.groupby(["time_idx", "agency"], observed=True).volume.transform("mean")
# we want to encode special days as one variable and thus need to first reverse one-hot encoding
special_days = [
"easter_day",
"good_friday",
"new_year",
"christmas",
"labor_day",
"independence_day",
"revolution_day_memorial",
"regional_games",
"fifa_u_17_world_cup",
"football_gold_cup",
"beer_capital",
"music_fest",
]
data[special_days] = data[special_days].apply(lambda x: x.map({0: "-", 1: x.name})).astype("category")
data.sample(10, random_state=521)
2、创立数据集和基线模型
当初须要将咱们的 df 转换为 Forecasting 的 TimeSeriesDataSet。这里须要设置参数确定哪些特色是分类的还是间断的,哪些是动态的还是时变的,还有抉择如何规范化数据。咱们别离对每个工夫序列进行标准化,并确认其始终都是正值。
为了避免归一化带来的前瞻性偏差,通常会应用 EncoderNormalizer,它会在训练时在每个编码器序列上动静缩放。
最初我抉择应用六个月的数据作为验证集。
max_prediction_length = 6
max_encoder_length = 24
training_cutoff = data["time_idx"].max() - max_prediction_length
training = TimeSeriesDataSet(data[lambda x: x.time_idx <= training_cutoff],
time_idx="time_idx",
target="volume",
group_ids=["agency", "sku"],
min_encoder_length=max_encoder_length // 2, # keep encoder length long (as it is in the validation set)
max_encoder_length=max_encoder_length,
min_prediction_length=1,
max_prediction_length=max_prediction_length,
static_categoricals=["agency", "sku"],
static_reals=["avg_population_2017", "avg_yearly_household_income_2017"],
time_varying_known_categoricals=["special_days", "month"],
variable_groups={"special_days": special_days}, # group of categorical variables can be treated as one variable
time_varying_known_reals=["time_idx", "price_regular", "discount_in_percent"],
time_varying_unknown_categoricals=[],
time_varying_unknown_reals=[
"volume",
"log_volume",
"industry_volume",
"soda_volume",
"avg_max_temp",
"avg_volume_by_agency",
"avg_volume_by_sku",
],
target_normalizer=GroupNormalizer(groups=["agency", "sku"], transformation="softplus"
), # use softplus and normalize by group
add_relative_time_idx=True,
add_target_scales=True,
add_encoder_length=True,
)
validation = TimeSeriesDataSet.from_dataset(training, data, predict=True, stop_randomization=True)
batch_size = 128 # set this between 32 to 128
train_dataloader = training.to_dataloader(train=True, batch_size=batch_size, num_workers=0)
train_dataloader = training.to_dataloader(train=True, batch_size=batch_size, num_workers=0)
这里咱们通过复制最有一个值来预测下 6 个月的数据,这个简略的操作咱们将它作为基线模型。
actuals = torch.cat([y for x, (y, weight) in iter(val_dataloader)])
baseline_predictions = Baseline().predict(val_dataloader)
(actuals - baseline_predictions).abs().mean().item()
## 后果
293.0088195800781
3、训练 TFT
当初咱们将创立 TemporalFusionTransformer 模型了。这里应用 PyTorch Lightning 训练模型。
pl.seed_everything(42)
trainer = pl.Trainer(
gpus=0,
# clipping gradients is a hyperparameter and important to prevent divergance
# of the gradient for recurrent neural networks
gradient_clip_val=0.1,
)
tft = TemporalFusionTransformer.from_dataset(
training,
# not meaningful for finding the learning rate but otherwise very important
learning_rate=0.03,
hidden_size=16, # most important hyperparameter apart from learning rate
# number of attention heads. Set to up to 4 for large datasets
attention_head_size=1,
dropout=0.1, # between 0.1 and 0.3 are good values
hidden_continuous_size=8, # set to <= hidden_size
output_size=7, # 7 quantiles by default
loss=QuantileLoss(),
# reduce learning rate if no improvement in validation loss after x epochs
reduce_on_plateau_patience=4,
)
print(f"Number of parameters in network: {tft.size()/1e3:.1f}k")
模型的参数大小:Number of parameters in network: 29.7k
res = trainer.tuner.lr_find(
tft,
train_dataloaders=train_dataloader,
val_dataloaders=val_dataloader,
max_lr=10.0,
min_lr=1e-6,
)
print(f"suggested learning rate: {res.suggestion()}")
fig = res.plot(show=True, suggest=True)
fig.show()
#suggested learning rate: 0.01862087136662867
对于 TemporalFusionTransformer,最佳学习率仿佛略低于默认的学习率。咱们也能够不间接应用倡议的学习率,因为 PyTorch Lightning 有时会被较低学习率的乐音混同,所以咱们应用一个依据教训确定的学习率。
early_stop_callback = EarlyStopping(monitor="val_loss", min_delta=1e-4, patience=10, verbose=False, mode="min")
lr_logger = LearningRateMonitor() # log the learning rate
logger = TensorBoardLogger("lightning_logs") # logging results to a tensorboard
trainer = pl.Trainer(
max_epochs=30,
gpus=0,
enable_model_summary=True,
gradient_clip_val=0.1,
limit_train_batches=30, # coment in for training, running valiation every 30 batches
# fast_dev_run=True, # comment in to check that networkor dataset has no serious bugs
callbacks=[lr_logger, early_stop_callback],
logger=logger,
)
tft = TemporalFusionTransformer.from_dataset(
training,
learning_rate=0.03,
hidden_size=16,
attention_head_size=1,
dropout=0.1,
hidden_continuous_size=8,
output_size=7, # 7 quantiles by default
loss=QuantileLoss(),
log_interval=10, # uncomment for learning rate finder and otherwise, e.g. to 10 for logging every 10 batches
reduce_on_plateau_patience=4,
)
trainer.fit(
tft,
train_dataloaders=train_dataloader,
val_dataloaders=val_dataloader,
)
4、验证
PyTorch Lightning 会主动保留训练的检查点,咱们加载最佳模型。
best_model_path = trainer.checkpoint_callback.best_model_path
best_tft = TemporalFusionTransformer.load_from_checkpoint(best_model_path)
actuals = torch.cat([y[0] for x, y in iter(val_dataloader)])
predictions = best_tft.predict(val_dataloader)
(actuals - predictions).abs().mean()
raw_predictions, x = best_tft.predict(val_dataloader, mode="raw", return_x=True)
for idx in range(10): # plot 10 examples
best_tft.plot_prediction(x, raw_predictions, idx=idx, add_loss_to_title=True);
计算显示的指标:
predictions = best_tft.predict(val_dataloader)
mean_losses = SMAPE(reduction="none")(predictions, actuals).mean(1)
indices = mean_losses.argsort(descending=True) # sort losses
for idx in range(10): # plot 10 examples
best_tft.plot_prediction(x, raw_predictions, idx=indices[idx], add_loss_to_title=SMAPE(quantiles=best_tft.loss.quantiles)
);
5、通过变量计算理论值与预测值
查看模型在不同数据上的体现如何能够让咱们看到模型的问题。
predictions, x = best_tft.predict(val_dataloader, return_x=True)
predictions_vs_actuals = best_tft.calculate_prediction_actual_by_variable(x, predictions)
best_tft.plot_prediction_actual_by_variable(predictions_vs_actuals);
对选定数据进行预测
best_tft.predict(training.filter(lambda x: (x.agency == "Agency_01") & (x.sku == "SKU_01") & (x.time_idx_first_prediction == 15)),
mode="quantiles",
)
raw_prediction, x = best_tft.predict(training.filter(lambda x: (x.agency == "Agency_01") & (x.sku == "SKU_01") & (x.time_idx_first_prediction == 15)),
mode="raw",
return_x=True,
)
best_tft.plot_prediction(x, raw_prediction, idx=0);
对新数据进行预测
encoder_data = data[lambda x: x.time_idx > x.time_idx.max() - max_encoder_length]
last_data = data[lambda x: x.time_idx == x.time_idx.max()]
decoder_data = pd.concat([last_data.assign(date=lambda x: x.date + pd.offsets.MonthBegin(i)) for i in range(1, max_prediction_length + 1)],
ignore_index=True,
)
decoder_data["time_idx"] = decoder_data["date"].dt.year * 12 + decoder_data["date"].dt.month
decoder_data["time_idx"] += encoder_data["time_idx"].max() + 1 - decoder_data["time_idx"].min()
decoder_data["month"] = decoder_data.date.dt.month.astype(str).astype("category") # categories have be strings
new_prediction_data = pd.concat([encoder_data, decoder_data], ignore_index=True)
new_raw_predictions, new_x = best_tft.predict(new_prediction_data, mode="raw", return_x=True)
for idx in range(10): # plot 10 examples
best_tft.plot_prediction(new_x, new_raw_predictions, idx=idx, show_future_observed=False);
6、模型解释
TFT 中内置了解释性能,咱们能够间接应用
interpretation = best_tft.interpret_output(raw_predictions, reduction="sum")
best_tft.plot_interpretation(interpretation)
局部依赖图通常用于更好地解释模型(假如特色独立)。
dependency = best_tft.predict_dependency(val_dataloader.dataset, "discount_in_percent", np.linspace(0, 30, 30), show_progress_bar=True, mode="dataframe"
)
agg_dependency = dependency.groupby("discount_in_percent").normalized_prediction.agg(median="median", q25=lambda x: x.quantile(0.25), q75=lambda x: x.quantile(0.75)
)
ax = agg_dependency.plot(y="median")
ax.fill_between(agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3);
总结
在本文中,咱们解释了 TFT 的理论知识并且应用它进行了一个残缺的训练和预测流程,心愿对你有帮忙,
https://avoid.overfit.cn/post/be0a146627ed45eca224f88fa6452b77
TFT 论文地址:arxiv 1912.09363
作者:Aryan Jadon