模型训练集 验证集 测试集怎么分

模型与算力

讲清训练集、验证集、测试集三份数据各自干什么、按什么顺序切、比例怎么定,并给出一套可以照着做的切分与复查清单。

刚上手建模的人最容易在这里翻车:数据只有一份,随手切完就开训,结果调参调到测试集上,最后报出来的效果自己都不敢信。这篇不背定义,讲的是三份数据的分工边界、切分顺序、常见比例区间,以及切完之后该怎么复查有没有串味。具体数字以你自己数据的规模和去站内模型库对照为准。

刚上手做模型的人,最容易在数据切分这一步翻车:手里就一份数据,随手切三块,训完调、调完再训,最后报出来的效果自己都不敢信。问题不在模型,在于三份数据的边界没划清。这篇不讲定义,只讲每份数据各干什么、按什么顺序切、比例怎么定,以及切完之后怎么复查有没有串味。

先把一句话记住:训练集让模型学,验证集让你做决定,测试集只负责最后报一次成绩。你基于哪份数据的表现做了决定,那份数据就已经参与训练过程了。具体规模该留多少、指标怎么对照,可以到 /rankings 看当时的口径和数据,不要照搬别人的数字。

三份数据各管一件事,别混着用

训练集是模型唯一允许反复看的那份数据,模型在这上面错得越多改得越多。验证集是给你自己看的,用来选超参、定迭代轮次、挑特征组合。测试集则只在模型彻底定稿后跑一次,跑完就封存,用来对外报成绩。三者的区别不是数据本身,而是你什么时候、出于什么目的去碰它。

训练集
模型可以反复看,用它更新参数;看多少遍都不算作弊
验证集
你拿它做选择,比如换超参、停在第几轮、丢哪个特征
测试集
定稿后跑一次,用来对外报成绩;跑完不许再回头改模型
  • 判断有没有用错的标准很简单:如果某个决定是「看了某份数据表现之后才做的」,那这份数据就已经进了训练流程,不能再当最终成绩的依据。
  • 验证集是过程指标,会随着你调参上下波动,这是正常的;测试集是结论指标,只应出现一次。
  • 如果发现自己反复去看测试集结果并据此调整,那它实际上已经变成第二个验证集,报出来的数字偏乐观。
  • 想对模型效果有个相对可信的判断,可以把验证集上的表现和最终测试结果并排看,差距大到不合理就说明切分或流程有问题。

切分顺序和比例:先切测试,再切验证

推荐顺序是先留出测试集并封存,再从剩下的部分里划验证集。反过来先切验证再切测试,容易因为两次切分的边界漂移,让两份数据分布对不上。切之前最好固定一次随机种子并把切分脚本存下来,保证每一步都可复现。

  1. 封存测试集从全量数据里先划走一份只做最终评估,把它放到单独目录或单独字段标记,流程里不读它。
  2. 从剩余数据里切验证集在剩下这份上再划一份做验证,其余留作训练。两步都记录随机种子和样本 ID。
  3. 检查三份数据的标签分布把每份的标签比例拉出来并排比一眼,差异明显就换分层抽样或按时间切。
  4. 写进切分脚本一起跑把切分逻辑固化成可重跑的命令,换数据时只换输入路径,避免每次手动切。

比例没有标准答案,主要看数据规模。数据量很小的时候,测试留一成到两成、验证留一成到两成比较常见,其余都给训练;数据量到了几十万条以上,验证和测试各留很小一部分通常也够用。真要定具体数字,建议结合你任务的指标波动情况到 /tools/speed-test 跑一下看当前规模下的稳定性。

数据情况切分做法注意点
样本数很少(几百到几千)训练占大头,验证和测试各留小份,配合交叉验证别把测试切得太碎,留够能触发一次完整评估的量
常规规模(几万到几十万)训练为主,验证和测试各留固定比例固定随机种子,保证可复现
类别严重不平衡按标签分层抽样每类在验证和测试里都要有代表样本
时序数据按时间先后切:早的训、中间的验、最晚的测不能打乱顺序,否则未来信息会漏进训练
存在分组结构(同用户、同设备)按组整体切,同组样本只出现在一份里否则同一对象既训又测,等于开卷考试

时序和分组这两条要特别小心。时序任务随机打乱,会把未来信息漏进训练集,线下效果好看上线就崩。涉及同一用户、同一设备、同一文档的样本,要整组切到一起,不能让同一个对象同时出现在训练和测试里,否则模型等于提前见过答案。

切完先做一遍串味检查,再开训

切分本身只是第一步,切完之后至少要做几项检查,确认三份数据之间没有互相泄露。很多看起来效果很好的模型,问题都出在这里,而不是模型本身有多强。检查不用很复杂,按下面的顺序过一遍就行,花不了多少时间。

  • 查重复:训练集和验证、测试之间做一次近似去重,完全重复和近重复(同一份内容改几个字)都算泄露。
  • 查分布:把三份数据的标签分布、关键字段分布并排比一眼,差异明显说明切分方式有问题。
  • 查预处理顺序:归一化、填充缺失、编码的统计量只能用训练集算,再套到验证和测试上;先在全量上算完再切,就已经泄露了。
  • 查 ID 交叉:用样本或对象 ID 做一次集合交集,训练和测试之间不该有共享 ID。
  • 留底线:测试集先别动,等模型和超参全部定稿再跑一次,跑完记录结果,不要再回头改。

预处理顺序这条最容易被忽略,也最隐蔽。均值、方差、编码映射这些东西,只要是在全量数据上算出来的,验证和测试的统计信息就通过预处理环节进入了训练。正确做法是先切分,所有统计量只在训练集上拟合,然后 transform 到验证和测试上。

数据量小怎么办:几种能落地的替代做法

数据量小是很多入门项目的常态,硬切三份确实会让每一份都薄得没法用。这时候不是放弃切分,而是换一种更省数据的方式,同时把结论里的话说诚实。

交叉验证
把训练数据轮着分块,每块当一次验证,最后取平均,适合只有几百到几千条的场景
留一法
每次只留一个样本做验证,适合样本极少但训练开销能接受的场景
验证测试合并
数据实在撑不起三份时可先合并,但要在结论里写明这是乐观估计
  1. 先判断交付目标只是内部看趋势,可以用交叉验证代替固定验证集;要对外报数或上线,测试集不能省。
  2. 选一个省数据的方案交叉验证或留一法选一个,把每一折的分数分布记下来,看方差大不大。
  3. 保留一份干净的测试哪怕只有几百条,也单独留出一份只跑一次,宁可小也不能脏。
  4. 在结论里写清口径说明验证用了哪种方式、测试跑了多少次,让人知道这个数字的置信度大概什么样。

有一种情况可以不用太纠结测试集:任务本身是探索性的,没有明确的上线判定标准,那严格封存测试集的意义有限,重点应该放在验证集的稳定性上。但只要涉及对外交付或上线决策,测试集的干净程度就不能打折。想了解常见的评估口径和踩坑记录,可以到 /sites 对照看看别人是怎么做的。

下一步把你现在的切分脚本跑一遍,把三份数据的标签分布和 ID 交集打出来,对照上面清单确认没有串味,再开始训练。

常见问题

验证集和测试集到底差在哪,能不能用同一份?

能合并,但代价是最后报出来的成绩偏乐观,因为你在调参过程中已经反复看过它了,相当于提前偷看答案。分开的价值就在于那份测试集从头到尾没被你动过,它给出的数字才是对新数据效果的诚实估计。如果只是内部评估趋势、不上线,合并一份也能接受,但结论要标注清楚。

数据太少,切完训练集只剩几百条,还怎么分?

优先考虑交叉验证而不是硬切三份:让每一块数据轮流当一次验证,取多次结果的平均,比一次性切分稳定得多。测试集可以留得很薄甚至暂时不留,但要明确这是阶段性的做法。等数据补上来之后,再按先切测试、后切验证的顺序重做一遍,别一直沿用临时方案。

随机切分看起来最简单,为什么有时候不能这么做?

两类情况要避开随机切:一是数据带时间顺序,随机切会让模型提前看到未来信息,线下效果好但上线崩;二是同一个用户、文档或设备对应多条记录,随机切会把同一对象的样本分到训练和测试两边,等于泄题。这两种情况分别要用按时间切和按组切,切完再复查一遍分布是否一致。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。