端到端演练
MLForecast 提供的所有功能的详细描述。
数据设置
对于本例,我们将使用 M4 每小时数据集的一个子集。您可以在这里找到包含完整数据集的 Notebook。
unique_id | ds | y | |
---|---|---|---|
0 | H196 | 1 | 11.8 |
1 | H196 | 2 | 11.4 |
2 | H196 | 3 | 11.1 |
3 | H196 | 4 | 10.8 |
4 | H196 | 5 | 10.6 |
… | … | … | … |
4027 | H413 | 1004 | 99.0 |
4028 | H413 | 1005 | 88.0 |
4029 | H413 | 1006 | 47.0 |
4030 | H413 | 1007 | 41.0 |
4031 | H413 | 1008 | 34.0 |
EDA
我们将查看我们的序列,以便为变换和特征提取思路。
我们可以使用 MLForecast.preprocess
方法来探索不同的变换。看起来这些序列在一天的小时上具有很强的季节性,因此我们可以从前一天同一小时的值中减去当前值来消除它。这可以通过 mlforecast.target_transforms.Differences
变换器来完成,我们将其通过 target_transforms
传递。
unique_id | ds | y | |
---|---|---|---|
24 | H196 | 25 | 0.3 |
25 | H196 | 26 | 0.3 |
26 | H196 | 27 | 0.1 |
27 | H196 | 28 | 0.2 |
28 | H196 | 29 | 0.2 |
… | … | … | … |
4027 | H413 | 1004 | 39.0 |
4028 | H413 | 1005 | 55.0 |
4029 | H413 | 1006 | 14.0 |
4030 | H413 | 1007 | 3.0 |
4031 | H413 | 1008 | 4.0 |
这已经从每个值中减去了滞后 24,现在我们可以看看我们的序列是什么样子了。
添加特征
滞后
看起来季节性已经消除,现在我们可以尝试添加一些滞后特征了。
unique_id | ds | y | lag1 | lag24 | |
---|---|---|---|---|---|
48 | H196 | 49 | 0.1 | 0.1 | 0.3 |
49 | H196 | 50 | 0.1 | 0.1 | 0.3 |
50 | H196 | 51 | 0.2 | 0.1 | 0.1 |
51 | H196 | 52 | 0.1 | 0.2 | 0.2 |
52 | H196 | 53 | 0.1 | 0.1 | 0.2 |
… | … | … | … | … | … |
4027 | H413 | 1004 | 39.0 | 29.0 | 1.0 |
4028 | H413 | 1005 | 55.0 | 39.0 | -25.0 |
4029 | H413 | 1006 | 14.0 | 55.0 | -20.0 |
4030 | H413 | 1007 | 3.0 | 14.0 | 0.0 |
4031 | H413 | 1008 | 4.0 | 3.0 | -16.0 |
滞后变换
滞后变换定义为一个字典,其中键是滞后,值是我们想要应用于该滞后的变换。滞后变换可以是来自 mlforecast.lag_transforms
模块的对象,也可以是 numba JIT 编译的函数(这样计算特征就不会成为瓶颈,并且我们可以在使用多线程时绕过 GIL),我们在 window-ops 包中实现了一些,但您也可以自己实现。
unique_id | ds | y | expanding_mean_lag1 | rolling_mean_lag24_window_size48 | rolling_mean_48_lag24 | |
---|---|---|---|---|---|---|
95 | H196 | 96 | 0.1 | 0.174648 | 0.150000 | 0.150000 |
96 | H196 | 97 | 0.3 | 0.173611 | 0.145833 | 0.145833 |
97 | H196 | 98 | 0.3 | 0.175342 | 0.141667 | 0.141667 |
98 | H196 | 99 | 0.3 | 0.177027 | 0.141667 | 0.141667 |
99 | H196 | 100 | 0.3 | 0.178667 | 0.141667 | 0.141667 |
… | … | … | … | … | … | … |
4027 | H413 | 1004 | 39.0 | 0.242084 | 3.437500 | 3.437500 |
4028 | H413 | 1005 | 55.0 | 0.281633 | 2.708333 | 2.708333 |
4029 | H413 | 1006 | 14.0 | 0.337411 | 2.125000 | 2.125000 |
4030 | H413 | 1007 | 3.0 | 0.351324 | 1.770833 | 1.770833 |
4031 | H413 | 1008 | 4.0 | 0.354018 | 1.208333 | 1.208333 |
您可以看到这两种方法得到相同的结果,您可以使用任何您觉得最舒服的方法。
日期特征
如果您的时间列由时间戳组成,那么提取像星期、周几、季度等特征可能很有意义。您可以通过传递一个包含 pandas 时间/日期组件字符串的列表来实现。您也可以传递将时间列作为输入的函数,我们将在下面展示。
unique_id | ds | y | hour_index | |
---|---|---|---|---|
24 | H196 | 25 | 0.3 | 1 |
25 | H196 | 26 | 0.3 | 2 |
26 | H196 | 27 | 0.1 | 3 |
27 | H196 | 28 | 0.2 | 4 |
28 | H196 | 29 | 0.2 | 5 |
… | … | … | … | … |
4027 | H413 | 1004 | 39.0 | 20 |
4028 | H413 | 1005 | 55.0 | 21 |
4029 | H413 | 1006 | 14.0 | 22 |
4030 | H413 | 1007 | 3.0 | 23 |
4031 | H413 | 1008 | 4.0 | 0 |
目标变换
如果您想在计算特征之前对目标进行一些变换,并在预测后重新应用它,您可以使用 target_transforms
参数,它接受一个变换列表。您可以在 mlforecast.target_transforms
中找到已实现的变换,或者按照目标变换指南中的描述实现您自己的变换。
unique_id | ds | y | lag1 | |
---|---|---|---|---|
1 | H196 | 2 | -1.493026 | -1.383286 |
2 | H196 | 3 | -1.575331 | -1.493026 |
3 | H196 | 4 | -1.657635 | -1.575331 |
4 | H196 | 5 | -1.712505 | -1.657635 |
5 | H196 | 6 | -1.794810 | -1.712505 |
… | … | … | … | … |
4027 | H413 | 1004 | 3.062766 | 2.425012 |
4028 | H413 | 1005 | 2.523128 | 3.062766 |
4029 | H413 | 1006 | 0.511751 | 2.523128 |
4030 | H413 | 1007 | 0.217403 | 0.511751 |
4031 | H413 | 1008 | -0.126003 | 0.217403 |
我们可以定义一个朴素模型来测试这个
unique_id | ds | 朴素 | |
---|---|---|---|
0 | H196 | 1009 | 16.8 |
1 | H256 | 1009 | 13.4 |
2 | H381 | 1009 | 207.0 |
3 | H413 | 1009 | 34.0 |
我们将此与序列的最后值进行比较
unique_id | ds | y | |
---|---|---|---|
1007 | H196 | 1008 | 16.8 |
2015 | H256 | 1008 | 13.4 |
3023 | H381 | 1008 | 207.0 |
4031 | H413 | 1008 | 34.0 |
训练
一旦您决定了要使用的特征、变换和模型,您就可以使用 MLForecast.fit
方法,它将执行预处理然后训练模型。模型可以指定为一个列表(这将使用它们的类名命名它们,如果存在重复的类则添加索引)或者一个字典,其中键是您想给模型的名称,即 将存储其预测结果的列名,而值是模型本身。
这计算了特征并使用它们训练了三个不同的模型。我们现在可以计算我们的预测了。
预测
unique_id | ds | avg | q75 | q25 | |
---|---|---|---|---|---|
0 | H196 | 1009 | 16.295257 | 16.357148 | 16.315731 |
1 | H196 | 1010 | 15.910282 | 16.007322 | 15.862261 |
2 | H196 | 1011 | 15.728367 | 15.780183 | 15.658180 |
3 | H196 | 1012 | 15.468414 | 15.513598 | 15.399717 |
4 | H196 | 1013 | 15.081279 | 15.133848 | 15.007694 |
… | … | … | … | … | … |
187 | H413 | 1052 | 100.450617 | 124.211150 | 47.025017 |
188 | H413 | 1053 | 88.426800 | 108.303409 | 44.715380 |
189 | H413 | 1054 | 59.675737 | 81.859964 | 19.239462 |
190 | H413 | 1055 | 57.580356 | 72.703301 | 21.486674 |
191 | H413 | 1056 | 42.669879 | 46.018271 | 24.392357 |
保存和加载
MLForecast 类具有 MLForecast.save
和 MLForecast.load
方法来存储然后加载预测对象。
更新序列值
训练好一个预测对象后,您可以使用之前的方法保存和加载它。如果在您想要使用它时已经知道目标的后续值,您可以使用 MLForecast.update
方法来合并这些值,这将允许您在计算预测时使用这些新值。
- 如果对于当前存储的序列没有提供新值,则只保留之前的值。
- 如果包含新序列,它们将被添加到现有序列中。
unique_id | ds | 朴素 | |
---|---|---|---|
0 | H196 | 1009 | 16.8 |
1 | H256 | 1009 | 13.4 |
2 | H381 | 1009 | 207.0 |
3 | H413 | 1009 | 34.0 |
unique_id | ds | 朴素 | |
---|---|---|---|
0 | H196 | 1010 | 17.0 |
1 | H256 | 1010 | 14.0 |
2 | H381 | 1009 | 207.0 |
3 | H413 | 1009 | 34.0 |
评估模型性能
交叉验证
为了估计我们的模型在预测未来数据时的表现如何,我们可以执行交叉验证,它包括在数据的不同子集上独立训练几个模型,使用它们预测验证集并衡量其性能。
由于我们的数据依赖于时间,我们通过移除序列的最后部分并将它们用作验证集来划分数据。此过程在 MLForecast.cross_validation
中实现。
unique_id | ds | cutoff | y | LGBMRegressor | |
---|---|---|---|---|---|
0 | H196 | 817 | 816 | 15.3 | 15.383165 |
1 | H196 | 818 | 816 | 14.9 | 14.923219 |
2 | H196 | 819 | 816 | 14.6 | 14.667834 |
3 | H196 | 820 | 816 | 14.2 | 14.275964 |
4 | H196 | 821 | 816 | 13.9 | 13.973491 |
… | … | … | … | … | … |
763 | H413 | 1004 | 960 | 99.0 | 65.644823 |
764 | H413 | 1005 | 960 | 88.0 | 71.717097 |
765 | H413 | 1006 | 960 | 47.0 | 76.704377 |
766 | H413 | 1007 | 960 | 41.0 | 53.446638 |
767 | H413 | 1008 | 960 | 34.0 | 54.902634 |
我们可以计算每个分割上的 RMSE。
LGBMRegressor | |
---|---|
cutoff | |
816 | 29.418172 |
864 | 34.257598 |
912 | 13.145763 |
960 | 35.066261 |
以及跨分割的平均 RMSE。
您可以通过这种方式快速尝试不同的特征并评估它们。我们可以尝试移除差分并使用滞后 1 的指数加权平均值代替扩展平均值。
LightGBMCV
本着评估模型性能的精神,LightGBMCV
允许我们在数据的不同分区上训练几个 LightGBM 模型。与 MLForecast.cross_validation
的主要区别在于:
- 它只能训练 LightGBM 模型。
- 它同时训练所有模型,并提供跨整个预测窗口的每次迭代的平均误差,这使我们能够找到最佳迭代。
正如您所见,这按迭代次数(由 eval_every
参数控制)给出了误差,并执行了早停(可以通过 early_stopping_evals
和 early_stopping_pct
配置)。如果您设置 compute_cv_preds=True
,则会使用找到的最佳迭代计算折叠外预测,并将其保存在 cv_preds_
属性中。
unique_id | ds | y | Booster | window | |
---|---|---|---|---|---|
0 | H196 | 817 | 15.3 | 15.473182 | 0 |
1 | H196 | 818 | 14.9 | 15.038571 | 0 |
2 | H196 | 819 | 14.6 | 14.849409 | 0 |
3 | H196 | 820 | 14.2 | 14.448379 | 0 |
4 | H196 | 821 | 13.9 | 14.148379 | 0 |
… | … | … | … | … | … |
187 | H413 | 1004 | 99.0 | 61.425396 | 3 |
188 | H413 | 1005 | 88.0 | 62.886890 | 3 |
189 | H413 | 1006 | 47.0 | 57.886890 | 3 |
190 | H413 | 1007 | 41.0 | 38.849009 | 3 |
191 | H413 | 1008 | 34.0 | 44.720562 | 3 |
您可以使用此类快速尝试不同的特征和超参数配置。一旦找到有效的组合,您可以通过从 LightGBMCV
对象创建 MLForecast
对象,然后使用这些特征和超参数在所有数据上训练模型,如下所示: