1 多变量时间序列预测
1.1 加载数据集
多变量时间序列预测的工作方式类似于单变量时间序列预测。主要的区别在于必须指定目标单变量的索引进行预测。例如,对于一个包含5个变量的时间序列,可能希望预测第3个变量的值(通过指定 target_seq_index = 2
来表示)。
首先加载用于时间序列预测的多变量 SeattleTrail
数据集。
from merlion.utils import TimeSeries
from ts_datasets.forecast import SeattleTrail
time_series, metadata = SeattleTrail()[0]
train_data = TimeSeries.from_pd(time_series[metadata["trainval"]])
test_data = TimeSeries.from_pd(time_series[~metadata["trainval"]])
print(f"Time series is {train_data.dim}-dimensional")
Time series is 5-dimensional
time_series
BGT North of NE 70th Total | Ped South | Ped North | Bike North | Bike South | |
---|---|---|---|---|---|
timestamp | |||||
2014-01-01 00:00:00 | 15.0 | 0.0 | 2.0 | 2.0 | 11.0 |
2014-01-01 01:00:00 | 9.0 | 1.0 | 0.0 | 1.0 | 7.0 |
2014-01-01 02:00:00 | 9.0 | 0.0 | 0.0 | 0.0 | 9.0 |
2014-01-01 03:00:00 | 19.0 | 0.0 | 0.0 | 0.0 | 19.0 |
2014-01-01 04:00:00 | 19.0 | 0.0 | 0.0 | 0.0 | 19.0 |
... | ... | ... | ... | ... | ... |
2019-12-31 19:00:00 | 6.0 | 2.0 | 1.0 | 1.0 | 2.0 |
2019-12-31 20:00:00 | 2.0 | 0.0 | 0.0 | 0.0 | 2.0 |
2019-12-31 21:00:00 | 3.0 | 1.0 | 2.0 | 0.0 | 0.0 |
2019-12-31 22:00:00 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 |
2019-12-31 23:00:00 | 2.0 | 1.0 | 0.0 | 1.0 | 0.0 |
52584 rows × 5 columns
1.2 模型初始化与训练
在这里,将使用三个模型:
-
DefaultForecaster(它会自动检测输入的时间序列是单变量还是多变量);
-
ARIMA(一个经典的单变量算法)用于预测特定的单变量;
-
ForecasterEnsemble,用于选择两者中表现较好的模型。
所有模型的训练都设置了最大允许的预测步长为100步。请注意,所有多变量预测模型都可以用于单变量时间序列;而通过适当指定 target_seq_index
,单变量模型也可以用于多变量时间序列。此外,所有情况下的API都是相同的。这段代码运行约10分钟。
from merlion.evaluate.forecast import ForecastMetric
from merlion.models.factory import ModelFactory
from merlion.models.ensemble.combine import ModelSelector
from merlion.transform.resample import TemporalResample
# Time series is sampled hourly, so max_forecast_steps = 24 means we can predict up to 1 day in the future
target_seq_index = 2
max_forecast_steps = 24
kwargs = dict(target_seq_index=target_seq_index,
max_forecast_steps=max_forecast_steps)
model1 = ModelFactory.create("DefaultForecaster", **kwargs)
model2 = ModelFactory.create("Arima", **kwargs)
# This ModelSelector combiner picks the best model based on sMAPE
model3 = ModelFactory.create("ForecasterEnsemble", models=[model1, model2], transform=TemporalResample(),
combiner=ModelSelector(metric=ForecastMetric.sMAPE))
for model in [model1, model2, model3]:
print(f"Training {type(model).__name__}...")
train_pred, train_stderr = model.train(train_data)
1.3 模型推断与定量评估
与单变量模型类似,我们可以调用 model.forecast()
来获取预测结果,并可能得到模型的标准误差。我们可以利用这些结果来评估模型的性能。请注意,ModelSelector
能够成功选择两个模型中表现较好的一个。
from merlion.evaluate.forecast import ForecastMetric
for model in [model1, model2, model3]:
forecast, stderr = model.forecast(
test_data.time_stamps[:max_forecast_steps])
rmse = ForecastMetric.RMSE.value(
ground_truth=test_data, predict=forecast, target_seq_index=target_seq_index)
smape = ForecastMetric.sMAPE.value(
ground_truth=test_data, predict=forecast, target_seq_index=target_seq_index)
print(f"{type(model).__name__}")
print(f"RMSE: {rmse:.4f}")
print(f"sMAPE: {smape:.4f}")
print()
DefaultForecaster
RMSE: 12.4991
sMAPE: 173.7479
Arima
RMSE: 10.2208
sMAPE: 140.2772
ForecasterEnsemble
RMSE: 10.2208
sMAPE: 140.2772
我们还可以使用 ForecastEvaluator 来以模拟实时部署的方式评估模型。在这里,我们先在训练数据上训练初始模型,然后使用滑动窗口的方法获取模型在训练数据上的预测结果。滑动窗口为1天(horizon="1d"
表示我们希望模型在每个时间步预测未来1天的值,cadence="1d"
表示我们每天从模型中获取一次预测)。请注意,这里并不会重新训练模型(retrain_freq=None
)。这段代码运行约10分钟。
from merlion.evaluate.forecast import ForecastEvaluator, ForecastEvaluatorConfig
for model in [model1, model2]:
print(f"{type(model).__name__} Sliding Window Evaluation")
evaluator = ForecastEvaluator(model=model, config=ForecastEvaluatorConfig(
horizon="1d", cadence="1d", retrain_freq=None))
train_result, test_pred = evaluator.get_predict(
train_vals=train_data, test_vals=test_data)
rmse = evaluator.evaluate(ground_truth=test_data,
predict=test_pred, metric=ForecastMetric.RMSE)
smape = evaluator.evaluate(
ground_truth=test_data, predict=test_pred, metric=ForecastMetric.sMAPE)
print(f"RMSE: {rmse:.4f}")
print(f"sMAPE: {smape:.4f}")
print()
DefaultForecaster Sliding Window Evaluation
RMSE: 19.3241
sMAPE: 164.4164
Arima Sliding Window Evaluation
RMSE: 13.1032
sMAPE: 112.2607
2 使用外生回归变量进行预测
外生回归变量(exogenous regressors)是指在预测目标变量时,可以使用的其他影响因素。这些变量可以是与目标变量相关的外部因素,通常可以提高模型的预测性能。在多变量时间序列预测中,可以将外生回归变量作为输入,帮助模型捕捉到更多的信息,从而改善预测结果。
考虑一个多变量时间序列 X ( 1 ) , … , X ( t ) X^{(1)},\ldots,X^{(t)} X(1),…,X(t),其中每个 X ( i ) ∈ R d X^{(i)} \in \mathbb{R}^d X(i)∈Rd 是一个 d d d 维向量。在多变量预测中,我们的目标是预测第 k k k 个单变量的未来值 X k ( t + 1 ) , … , X k ( t + h ) X_k^{(t+1)},\ldots,X_k^{(t+h)} Xk(t+1),…,Xk(t+h)。
外生回归变量 Y ( i ) Y^{(i)} Y(i) 是一组我们事先已知值的附加变量。使用外生回归变量进行预测的任务是预测我们的目标单变量 X k ( t + 1 ) , … , X k ( t + h ) X_k^{(t+1)},\ldots,X_k^{(t+h)} Xk(t+1),…,Xk(t+h),需要的数据为:
- 时间序列 X ( 1 ) , … , X ( t ) X^{(1)},\ldots,X^{(t)} X(1),…,X(t) 的过去值
- 外生回归变量 Y ( 1 ) , … , Y ( t ) Y^{(1)},\ldots,Y^{(t)} Y(1),…,Y(t) 的过去值
- 外生回归变量 Y ( t + 1 ) , … , Y ( t + h ) Y^{(t+1)},\ldots,Y^{(t+h)} Y(t+1),…,Y(t+h) 的未来值
例如,可以考虑预测某个商店特定商品的销售情况。内生变量 X ( i ) ∈ R 4 X^{(i)} \in \mathbb{R}^4 X(i)∈R4 可能包含销售数量(目标单变量)、外部温度、消费者价格指数和当前失业率。外生变量 Y ( i ) ∈ R 6 Y^{(i)} \in \mathbb{R}^6 Y(i)∈R6 是商店可以控制或已知的变量。它们可能包括某一天是否为假期,以及关于商店进行的各种降价促销的信息。
为了更具体,让我们用一些真实数据来说明这一点。
# This is the same dataset used in the custom dataset tutorial
from ts_datasets.forecast import CustomDataset
csv = 'E:\Merlion\data\walmart\walmart_mini.csv'
dataset = CustomDataset(rootdir=csv, index_cols=["Store", "Dept"], test_frac=0.10)
ts, md = dataset[-1]
display(ts)
Weekly_Sales | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | CPI | Unemployment | IsHoliday | |
---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||
2010-02-05 | 39602.47 | 40.19 | 2.572 | NaN | NaN | NaN | NaN | NaN | 210.752605 | 8.324 | False |
2010-02-12 | 37984.44 | 38.49 | 2.548 | NaN | NaN | NaN | NaN | NaN | 210.897994 | 8.324 | True |
2010-02-19 | 38889.43 | 39.69 | 2.514 | NaN | NaN | NaN | NaN | NaN | 210.945160 | 8.324 | False |
2010-02-26 | 41137.74 | 46.10 | 2.561 | NaN | NaN | NaN | NaN | NaN | 210.975957 | 8.324 | False |
2010-03-05 | 39883.50 | 47.17 | 2.625 | NaN | NaN | NaN | NaN | NaN | 211.006754 | 8.324 | False |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2012-09-28 | 37104.67 | 79.45 | 3.666 | 7106.05 | 1.91 | 1.65 | 1549.10 | 3946.03 | 222.616433 | 6.565 | False |
2012-10-05 | 36361.28 | 70.27 | 3.617 | 6037.76 | NaN | 10.04 | 3027.37 | 3853.40 | 222.815930 | 6.170 | False |
2012-10-12 | 35332.34 | 60.97 | 3.601 | 2145.50 | NaN | 33.31 | 586.83 | 10421.01 | 223.015426 | 6.170 | False |
2012-10-19 | 35721.09 | 68.08 | 3.594 | 4461.89 | NaN | 1.14 | 1579.67 | 2642.29 | 223.059808 | 6.170 | False |
2012-10-26 | 34260.76 | 69.79 | 3.506 | 6152.59 | 129.77 | 200.00 | 272.29 | 2924.15 | 223.078337 | 6.170 | False |
143 rows × 11 columns
from merlion.utils import TimeSeries
# Get the 内生 variables X and split them into train & test
endog = ts[["Weekly_Sales", "Temperature", "CPI", "Unemployment"]]
train = TimeSeries.from_pd(endog[md.trainval])
test = TimeSeries.from_pd(endog[~md.trainval])
# Get the 外生 variables Y
exog = TimeSeries.from_pd(ts[["IsHoliday", "MarkDown1", "MarkDown2", "MarkDown3", "MarkDown4", "MarkDown5"]])
在这里,我们的任务是预测每周的销售额。我们希望我们的模型能够考虑可能影响消费者需求的变量(例如温度、消费者价格指数和失业率),因为对这些变量的了解可以提高销售预测的质量。这将是一个多变量预测问题,在这里进行讨论。
原则上,我们可以将降价促销和假期添加到多变量模型中。然而,作为零售商,我们事先知道哪些天是假期,并且我们自己控制降价促销。在许多情况下,通过提供这些变量的未来值以及过去值,我们可以获得更好的预测。此外,我们可能希望建模未来降价促销的变化如何影响未来销售。这就是为什么我们应该将这些变量建模为外生回归变量的原因。
所有 Merlion 预测器都支持一个 API,该 API 在训练和推理时都接受外生回归变量,尽管只有部分模型实际支持该功能。使用此功能非常简单,只需在 train()
和 forecast()
中指定一个可选参数 exog_data
。我们将在下面展示如何在流行的 Prophet 模型中使用该功能,并演示添加外生回归变量如何提高预测的质量。
from merlion.evaluate.forecast import ForecastMetric
from merlion.models.forecast.prophet import Prophet, ProphetConfig
# Train a model without exogenous data
model = Prophet(ProphetConfig(target_seq_index=0))
model.train(train)
pred, err = model.forecast(test.time_stamps)
smape = ForecastMetric.sMAPE.value(
test, pred, target_seq_index=model.target_seq_index)
print(f"sMAPE (w/o exog) = {smape:.2f}")
# Train a model with exogenous data
exog_model = Prophet(ProphetConfig(target_seq_index=0))
exog_model.train(train, exog_data=exog)
exog_pred, exog_err = exog_model.forecast(test.time_stamps, exog_data=exog)
exog_smape = ForecastMetric.sMAPE.value(
test, exog_pred, target_seq_index=exog_model.target_seq_index)
print(f"sMAPE (w/ exog) = {exog_smape:.2f}")
sMAPE (w/o exog) = 3.97
sMAPE (w/ exog) = 3.17
在我们结束本教程之前,我们注意到外生变量包含大量缺失数据:
display(exog.to_pd())
IsHoliday | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | |
---|---|---|---|---|---|---|
time | ||||||
2010-02-05 | 0.0 | NaN | NaN | NaN | NaN | NaN |
2010-02-12 | 1.0 | NaN | NaN | NaN | NaN | NaN |
2010-02-19 | 0.0 | NaN | NaN | NaN | NaN | NaN |
2010-02-26 | 0.0 | NaN | NaN | NaN | NaN | NaN |
2010-03-05 | 0.0 | NaN | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... |
2012-09-28 | 0.0 | 7106.05 | 1.91 | 1.65 | 1549.10 | 3946.03 |
2012-10-05 | 0.0 | 6037.76 | NaN | 10.04 | 3027.37 | 3853.40 |
2012-10-12 | 0.0 | 2145.50 | NaN | 33.31 | 586.83 | 10421.01 |
2012-10-19 | 0.0 | 4461.89 | NaN | 1.14 | 1579.67 | 2642.29 |
2012-10-26 | 0.0 | 6152.59 | 129.77 | 200.00 | 272.29 | 2924.15 |
143 rows × 6 columns
Merlion 模型会对外生变量应用可选的 exog_transform
,然后将外生变量重采样到与内生变量相同的时间戳。这个重采样是通过 exog_missing_value_policy
和 exog_aggregation_policy
实现的,这些策略可以在接受外生回归变量的任何模型的配置中指定。我们可以通过检查配置来查看这些参数的默认值:
print(f"Default exog_transform: {type(exog_model.config.exog_transform).__name__}")
print(f"Default exog_missing_value_policy: {exog_model.config.exog_missing_value_policy}")
print(f"Default exog_aggregation_policy: {exog_model.config.exog_aggregation_policy}")
Default exog_transform: MeanVarNormalize
Default exog_missing_value_policy: MissingValuePolicy.ZFill
Default exog_aggregation_policy: AggregationPolicy.Mean
以下是每个参数的含义及其功能:
- exog_transform: MeanVarNormalize
- 含义:
MeanVarNormalize
是一种数据预处理方法,用于对外生变量进行均值和方差归一化。具体而言,它会将每个外生变量的均值调整为 0,并将方差调整为 1。 - 功能:这种变换可以帮助模型更好地学习数据,因为它消除了不同变量之间的量纲差异,使得所有变量在相同的尺度上进行比较和处理。
- exog_missing_value_policy: MissingValuePolicy.ZFill
- 含义:
MissingValuePolicy.ZFill
指的是在处理缺失值时采用的策略。ZFill
意味着用零填充缺失值。 - 功能:通过用零替换缺失的数据点,模型可以继续处理数据,而不会因为缺失值而导致错误或数据不完整。这种策略在某些情况下是合理的,尤其是当缺失值不影响数据的整体趋势时。
- exog_aggregation_policy: AggregationPolicy.Mean
- 含义:
AggregationPolicy.Mean
指的是在对外生变量进行重采样时所采用的聚合策略。Mean
表示对多个时间点的数据取平均值。 - 功能:在重采样过程中,如果在某个时间点存在多个外生变量值,使用均值聚合可以生成该时间点的代表值,从而保持数据的一致性和连续性。这有助于避免由于单一极端值而对模型预测造成的影响。
总结
exog_transform
用于标准化外生变量。exog_missing_value_policy
指定缺失值的填充策略。exog_aggregation_policy
决定重采样时如何处理多个值。
这些策略的组合可以提高模型对外生变量的处理能力,从而改善整体预测的质量。
因此,在这种情况下,我们首先对外生数据应用均值方差归一化。然后,我们通过用零填充缺失值(ZFill)来进行缺失值填充,并通过对任何相关窗口取均值来对外生数据进行下采样。