📢 转载信息
原文链接:https://machinelearningmastery.com/5-ways-to-use-cross-validation-to-improve-time-series-models/
原文作者:Jason Brownlee
在时间序列预测中,我们经常需要对模型性能进行准确的评估。然而,由于时间序列数据中的观测值之间存在时间依赖性,标准的K折交叉验证(Cross-Validation, CV)方法可能导致评估结果过于乐观。
在处理时间序列数据时,我们不能随机地将数据分成训练集和测试集,因为这会打破数据的内在顺序,使模型在测试集上表现得像是在“作弊”,因为它利用了未来的信息(数据泄漏)。
本文将介绍五种专门用于时间序列预测的交叉验证方法,以确保模型评估的可靠性。
⚠️ 关键概念:时间序列交叉验证的挑战
标准的数据随机划分(如K折CV)假设数据点是独立同分布的(i.i.d.)。在时间序列中,这个假设不成立。对时间序列进行随机分割,模型在训练集中可能会看到未来的数据点,从而在测试集上给出过于乐观的性能估计。
正确的交叉验证策略必须确保:
- 训练集中的所有观测值都发生在测试集观测值之前。
- 训练集和测试集之间不存在数据泄漏。
以下是五种有效的方法。
1. 前向验证(Forward Chaining Cross-Validation)
前向验证,也称为“前向链式验证”或“滚动原点交叉验证”,是时间序列交叉验证最常用的方法之一。它通过逐步增加训练集的大小来模拟真实世界中的预测过程。
工作原理:
- 选择一个初始的训练集大小(通常是最小的合理大小)。
- 创建一个测试集,紧随训练集之后。
- 训练模型,评估性能。
- 扩展训练集,将前一个测试集也包含进去。
- 选择下一个测试集,紧随新的训练集之后。
- 重复此过程,直到所有数据都被用于测试。
示例:
假设我们有100个数据点,我们想要进行3次交叉验证(folds)。
- Fold 1: 训练集 = [1-50],测试集 = [51-60]
- Fold 2: 训练集 = [1-60],测试集 = [61-70]
- Fold 3: 训练集 = [1-70],测试集 = [71-80]
这种方法确保了训练集始终是测试集在时间上的先驱。
2. 滑动窗口交叉验证(Sliding Window Cross-Validation)
滑动窗口是前向验证的一种变体,它保持训练集的固定大小,并通过“滑动”这个窗口来迭代数据。
工作原理:
- 定义一个固定的训练集大小(W)和一个固定的测试集大小(H)。
- 第一个训练集为 $T_1 = [1...W]$,测试集为 $V_1 = [W+1...W+H]$。
- 下一个训练集是 $T_2 = [2...W+1]$,测试集是 $V_2 = [W+2...W+H+1]$。
- 训练集和测试集一起向前移动一步,保持它们的大小不变。
何时使用:
当您认为最近的数据点比早期的观测值更具代表性时,滑动窗口方法非常有用。它强调了较新的数据,同时保持了固定的历史背景大小。
3. 修正的K折交叉验证(Modified K-Fold Cross-Validation)
标准K折交叉验证将数据分成K个大小相等的块,并轮流将其中一个块用作测试集。对于时间序列,我们不能随机选择块。
修正方法:
为了保持时间顺序,我们需要确保测试集中的所有数据都晚于训练集中的所有数据。我们可以将数据按时间顺序分成K个大小相等的块,并按顺序进行测试。
假设 $K=5$。数据被分割为 $B_1, B_2, B_3, B_4, B_5$。
- Fold 1: 训练集 = $B_1, B_2, B_3, B_4$,测试集 = $B_5$
- Fold 2: 训练集 = $B_1, B_2, B_3$,测试集 = $B_4$ (注意:这里通常会使用更大的训练集,但核心思想是测试集必须是时间上最新的)
更标准的修正方法是:
- Fold 1: 训练集 = $B_1$,测试集 = $B_2$
- Fold 2: 训练集 = $B_1, B_2$,测试集 = $B_3$
- Fold 3: 训练集 = $B_1, B_2, B_3$,测试集 = $B_4$
- Fold 4: 训练集 = $B_1, B_2, B_3, B_4$,测试集 = $B_5$
这实际上与前向验证非常相似,只是划分的粒度不同(按数据块而不是按单个时间步)。
4. 块交叉验证(Blocked Cross-Validation)
块交叉验证旨在解决时间序列数据中存在的“自相关性”问题。
问题:
即使我们保证了训练集在时间上早于测试集,如果测试集内部的数据点是高度相关的(例如,如果测试集 $V$ 中所有点都来自同一个时间段,比如一个特定的季节或一周),模型评估也可能不准确。
解决方案:
将整个数据集划分为几个不重叠的“块”,这些块在时间上是连续的,并且足够大,以包含数据的内在时间结构。
- 将数据分成 $K$ 个块 $B_1, B_2, ..., B_K$。
- 在第 $k$ 次迭代中,使用 $B_k$ 作为测试集,将所有其他块 $B_1, ..., B_{k-1}, B_{k+1}, ..., B_K$ 合并作为训练集。
关键限制:
这种方法要求我们在训练集中保留测试集之前的所有数据。因此,在实际操作中,我们通常需要一个“验证集”来存储早期数据,或者使用前向验证/滑动窗口方法。
在实践中,如果序列被分成 $K$ 个块,只有在 $B_k$ 是数据序列的最后 $1/K$ 部分时,这种方法才完全符合时间序列的要求。否则,它可能引入数据泄漏。
5. 时间序列保持交叉验证(Time Series Holdout Cross-Validation)
这是最简单、最直接的方法,本质上是将标准数据集拆分扩展到多个时间点的保持集中。
工作原理:
我们定义一个固定的验证时间点或验证窗口,将早于该时间点的所有数据用于训练,将晚于该时间点(或在该窗口内)的数据用于验证。
步骤:
- 选择一个“截止点”(Cutoff point)或一组截止点。
- 训练集: 所有发生在截止点之前的数据。
- 测试集: 所有发生在截止点之后的数据。
这更像是标准的训练/测试分割,但强调了时间顺序。如果我们需要多次评估,我们可以使用多个不同的截止点(如在方法1和2中描述那样)。
何时使用:
当您只需要对模型进行一次最终评估,并且拥有足够多的历史数据时,这非常有效。它最能模拟模型在生产环境中(未来)的性能。
总结与建议
对于时间序列预测,随机的K折交叉验证是不适用的。您需要采用能够尊重数据时间顺序的方法。
对于大多数时间序列项目,前向验证(Forward Chaining)是最可靠、最常用的方法,因为它能系统地评估模型在不同时间点上的表现,并利用所有可用数据。
如果您对模型的近期表现更感兴趣,可以考虑滑动窗口,它能让模型始终基于一个固定长度的“近期历史”进行训练。
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区