4、样本内与样本外:如何正确划分训练集、验证集和测试集

说实话,这个题目我每次讲课时都觉得特别重要。

为什么?因为太多人栽在这个坑里了。我自己就见过不少团队,策略回测曲线漂亮得不行,一上实盘就崩。问题出在哪?十有八九是数据划分出了问题。

你想想看,如果你用未来的数据去训练模型,那不就是作弊吗?但现实中,这种"未来信息泄露"往往藏得很深,不是一眼能看出来的。

为什么划分这么重要?

先讲个我自己的经历。几年前我做CTA策略,回测年化收益40%,最大回撤不到8%。我当时觉得稳了,直接上实盘。结果呢?三个月亏了15%。

复盘时我发现,问题出在数据划分上。我用的是滚动窗口,但窗口重叠太多,导致训练集和测试集之间信息泄露。说白了,模型在测试集上"见过"类似的市场状态。

所以,正确划分数据不是形式主义,是生死线。

核心原则:任何测试集的信息都不能以任何形式流入训练过程。包括特征工程、参数调优、模型选择等环节。

三种常见划分方式

我习惯把划分方式分成三类。每种都有适用场景,也有坑。

划分方式 适用场景 我的建议
固定比例划分 数据量大、市场结构稳定 适合初学者,但容易过时
滚动窗口 时序性强的策略 我最常用的方式,注意窗口大小
扩展窗口 长期趋势策略 适合低频策略,计算量大

固定比例划分:简单但危险

很多新手喜欢用70%训练、15%验证、15%测试。嗯,这个比例本身没问题,问题在于怎么切。

我见过有人随机打乱数据再切分。这在横截面数据里可以,但在时间序列里就是灾难。你想想,把2020年的数据和2015年的数据混在一起,模型不就学到了未来的信息吗?

注意:时间序列数据必须按时间顺序划分。绝对不能随机打乱。这是原则问题。

正确的做法是按时间切分。比如2010-2017年做训练,2018年做验证,2019-2020年做测试。这样模型在测试集上的表现才真实。

滚动窗口:我最推荐的方式

我个人习惯用滚动窗口。为什么?因为它能模拟策略在真实环境中的表现。

具体做法是这样的:

# 滚动窗口划分示例
# 假设我们有2010-2020年的日线数据

# 第一轮
训练集: 2010-01 到 2014-12
验证集: 2015-01 到 2015-06
测试集: 2015-07 到 2015-12

# 第二轮
训练集: 2010-07 到 2015-06
验证集: 2015-07 到 2015-12
测试集: 2016-01 到 2016-06

# 以此类推...

这样做的好处是,你能看到策略在不同市场环境下的表现。如果某轮测试结果特别差,别急着优化,先看看那段时间市场是不是出了什么异常。

小技巧:我建议窗口步长设为测试集长度的一半。这样既能保证足够的训练数据,又能避免窗口重叠太多。

扩展窗口:适合低频策略

如果你做的是月频或季频策略,扩展窗口可能更合适。

它的逻辑是:训练集不断累积,测试集始终是最近的一段。比如:

# 扩展窗口示例

# 第一轮
训练集: 2010-01 到 2014-12
测试集: 2015-01 到 2015-06

# 第二轮
训练集: 2010-01 到 2015-06
测试集: 2015-07 到 2015-12

# 第三轮
训练集: 2010-01 到 2015-12
测试集: 2016-01 到 2016-06

这种方式的好处是训练数据越来越多,模型越来越稳定。但缺点也很明显——计算量越来越大,而且对早期数据的依赖会越来越强。

避坑指南:我踩过的几个坑

这些年我踩过不少坑,挑几个典型的说说:

  • 我曾经在特征工程时用了全样本的均值和标准差做标准化。结果测试集的信息泄露到了训练集。后来我改成用训练集的统计量做标准化,问题才解决。
  • 我曾经在调参时反复用测试集评估效果。调了十几次后,测试集变成了训练集的一部分。嗯,这个坑特别隐蔽,很多人都会犯。
  • 我曾经忽略了数据中的幸存者偏差。回测时用的都是现在还在的股票,但历史上退市的股票被忽略了。这导致策略看起来很好,实盘却不行。

记住:测试集只能用一次。如果你用它调了参数,那它就不再是测试集了,而是验证集。你需要一个新的、从未见过的测试集。

知识体系图

下面这张图展示了数据划分的核心逻辑。我画了很久,希望能帮你理清思路。

数据划分核心逻辑 原始时间序列数据 训练集 (70%) 验证集 (15%) 测试集 (15%) 用途 模型训练 参数学习 用途 参数调优 模型选择 用途 最终评估 仅用一次 ⚠ 警告:测试集只能使用一次! 任何基于测试集结果的调整都会导致信息泄露

实际操作建议

说了这么多理论,来点实际的。我总结了几条操作建议:

  1. 先切分,再处理。任何数据预处理都要在切分之后做。用训练集的统计量去标准化测试集。
  2. 留足缓冲期。训练集和测试集之间最好留一段空白。比如训练集到2018年底,测试集从2019年3月开始。这样可以避免短期相关性带来的干扰。
  3. 多轮验证。别只做一次划分。用滚动窗口做多轮验证,看看策略在不同时间段的表现是否一致。
  4. 记录每次划分。我习惯把每次划分的时间段、数据量、市场状态都记下来。这样复盘时能快速定位问题。

我的习惯:每次做策略研究,我都会先花30分钟把数据划分方案想清楚。这30分钟往往能省下后面几天的调试时间。

数据划分看起来是个小问题,但影响巨大。我见过太多人因为这一步没做好,导致整个策略研究白费。所以,别嫌麻烦,认真对待每一次划分。

记住一句话:测试集是你最后的底牌。别在出牌前就把它亮出来。

专注资料整理