很多人在自己整理数据跑模型时,会卡在一个很基础的问题上:手上这批数据到底该怎么切,切多少留出来做测试,为什么切完之后效果看着挺好一上线就拉胯。这篇不聊公式,只讲训练数据和测试数据各自扮演什么角色、划分时按什么原则切、切错了会出现什么症状,以及一套能照着走的检查顺序。
很多人在自己整理数据跑模型时,会卡在一个很基础的问题上:手上这批数据到底该怎么切,切多少留出来做测试,为什么切完之后效果看着挺好一上线就拉胯。这篇不聊公式,只讲训练数据和测试数据各自扮演什么角色、划分时按什么原则切、切错了会出现什么症状,以及一套能照着走的检查顺序。
先把结论摆前面:训练数据和测试数据的区别,不在于难易,而在于「模型见没见过」。训练数据是模型反复看、反复改参数的那批;测试数据是模型从头到尾没碰过、只在最后看一眼的那批。分清楚这条线,后面所有划分原则都是它的延伸。
训练数据和测试数据到底各干什么
训练数据是模型用来调参数的那批样本。模型在这批数据上反复试错、改权重,把规律压进参数里。这个过程相当于把答案摆在它面前让它照着改,所以它对训练数据的表现会越来越好,好到你没法用它来判断真实水平。
测试数据是模型在整个训练过程中完全没见过的样本,用来模拟上线后遇到的新输入。它的作用不是「更难」,而是「没被偷看过」。如果测试数据也参与了调参,那它就不再是测试数据,而变成了另一份训练数据。
- 训练数据负责「学」:模型在这批样本上调整参数,见过多少遍都算。
- 测试数据负责「考」:只在最后用一次,看它在陌生输入上的表现。
- 两者不是难易之分,而是见没见过之分,这是最核心的一条分界线。
- 很多人误以为测试数据是更难的题,其实它只是另一批同分布的题。
- 一旦你拿测试数据的结果去反过来改模型,它就不再干净了。
| 对比项 | 训练数据 | 测试数据 |
|---|---|---|
| 模型是否见过 | 见过,且反复看 | 从头到尾没见过 |
| 用途 | 调参数、学规律 | 模拟上线后的真实输入 |
| 用几次 | 多次,越多轮越好 | 理想情况只用一次 |
| 表现好说明什么 | 拟合能力强 | 可能泛化好,也可能划分有问题 |
| 被污染后 | 无法判断泛化 | 分数虚高,上线打脸 |
为什么常见的是切三份而不是两份
只切训练和测试两份,看起来最省事,但实际操作里几乎没人能忍住只在最后用一次测试集。你会不自觉地在测试集上试好几版模型,试到满意为止。这时候测试集已经变相成了验证集,最后那个分数就不干净了。
切三份的分工是这样的:训练集负责学,验证集负责在训练过程中做选择(调超参、选轮数、决定什么时候停),测试集只在最后用一次。验证集可以反复用,因为它的角色本来就是「陪练」,被反复参考不心疼。
- 先切出测试集从全量数据里先抠出一份,封存起来,训练阶段完全不碰,连看一眼分布变化都尽量少做。
- 再把剩下的切成训练和验证剩下的数据按比例分成两份,训练集用来跑,验证集用来在每个轮次结束后评估并做决策。
- 验证集上试到满意为止调超参、试不同结构、决定早停,这些动作都在验证集上完成,反复试没关系。
- 最后放测试集模型定稿后,只在测试集上跑一次,记录结果。如果之后又改了模型,严格来说这份测试集已经作废,要重新留一份。
- 数据太少就换交叉验证样本量不够时,固定切三份会浪费数据,改用轮换的方式让每份都当过一次验证。
验证集和测试集的分界线,就是「能不能反复看」。能反复看的叫验证集,只看一次的才叫测试集。判断一个团队有没有把这事做对,问一句「你们的最终分数是在哪个集合上得的、用过几次」就够了。
用一个生活例子理解划分原则
把模型当成一个备考的学生。训练数据是练习题,验证数据是模拟考,测试数据是真正的高考。练习题做多了会背答案,模拟考用来调整复习策略,高考只能考一次而且考前不能看。
如果老师把高考题提前透给学生练,考出来的分再高也不能说明水平。数据划分里最常见的错误,本质上都是这种「透题」:同一个来源、同一个人、同一段时间的样本,被分别分进了训练和测试。
- 同一张图的不同裁剪版本,一个进训练一个进测试,等于把原题和改编题都给了学生。
- 同一条记录的不同时间点,分别落在训练和测试,模型等于看到了未来的影子。
- 同一个用户的多条行为,前几条进训练后几条进测试,模型能记住这个人,而不是学会规律。
- 同一段文本的不同切块,随机分到两边,模型做过一遍原文,测试时只是换了切法。
- 判断标准很简单:测试样本和训练样本之间,不能存在任何一条能对上号的线索。
划分数据的本质,是模拟「未来会遇到一批没见过的东西」,所以任何能对上号的线索都必须断开。
最容易踩的坑:数据泄露
数据泄露听起来很技术,说白了就是测试集的信息以某种方式漏进了训练过程。它不是代码写错,而是流程顺序错了。最常见的一种是切分顺序和预处理顺序搞反了。
很多人习惯先把全量数据做归一化、填充缺失值、做特征编码,然后再切训练和测试。这样切出来的两份,统计量(均值、方差、分位数)都是全量算的,里面已经含了测试集的信息。正确顺序是先切,再只用训练集算这些统计量,然后套用到验证和测试上。
| 坑 | 典型表现 | 正确做法 |
|---|---|---|
| 同源样本跨界 | 同图不同裁剪、同一人多条记录分两边 | 按来源或用户整体切,不拆散 |
| 预处理顺序错 | 全量归一化、全量填充后再切 | 先切分,统计量只在训练集算 |
| 时间序列随机切 | 用后面时间段预测前面时间段 | 按时间先后切,训练在早、测试在晚 |
| 反复用测试集 | 每改一版都跑测试集看分数 | 测试集只跑一次,调参用验证集 |
| 分层没做 | 少数类全被切进某一边 | 按类别分层抽样,保证两边占比接近 |
时间序列和用户维度这两类数据,是泄露的重灾区。时序数据必须按时间切,否则就是用未来预测过去,上线后根本拿不到未来数据。带用户的数据要按用户切,同一用户的所有样本只能出现在一边,否则模型是在认人而不是学规律。
切错了会出现什么症状
切分出问题,通常不会直接报错,而是以几种典型症状出现。看懂症状对应哪类问题,比一上来就调模型有效得多。下面这张对照表可以当排查顺序用。
| 症状 | 可能原因 | 先查什么 |
|---|---|---|
| 训练分数远高于测试分数 | 过拟合,或两边分布不一致 | 先看两边分布是否对齐,再看模型复杂度 |
| 两边分数都高但上线就崩 | 测试集和真实场景分布差太远 | 核对测试集来源是否覆盖真实输入类型 |
| 换个随机种子分数忽高忽低 | 数据量太小或划分不稳定 | 改用交叉验证,加大评估样本量 |
| 测试分数异常地高 | 大概率有数据泄露 | 按上一节的清单逐条排查同源和时间 |
| 少数类指标全崩 | 划分时没做分层 | 检查两边类别占比是否接近 |
注意每种症状对应的排查方向不同。训练高测试低,先去查分布和泄露;两边都高但上线崩,问题多半在测试集本身不够代表真实场景;分数随种子抖,是数据量和评估方法的问题。别把这些混在一起调参,越调越乱。
什么情况下这些症状不适用?如果你的任务本身就是随机性很强的(比如某些生成任务),或者评估指标本身方差就大,那分数抖动是正常的,不能一律当成划分问题。判断前先确认任务的可复现性。
各切多少比例才合适
没有万能比例,取决于数据总量和任务对错判的容忍度。大方向是:数据越多,测试集占比可以压得越小,够评估稳定就行,剩下的留给训练更划算;数据越少,固定比例切分越浪费,越该考虑交叉验证。
- 数据量很大时,测试集占比压小一点没关系,关键是评估结果要稳定,多跑几个随机种子看分数波动。
- 数据量很小的时候,固定比例切分会浪费样本,改用交叉验证,让每份数据轮着当验证。
- 类别不平衡时不能纯随机切,要按类别分层抽样,保证每个集合里都有少数类,否则少数类指标没法看。
- 时间序列要留出足够长的时间窗口做测试,太短的窗口覆盖不到周期性变化。
- 比例怎么定,最终看你的数据总量和任务对错判的容忍度,容忍度低就多留点测试。
关于具体阈值,各家做法差异不小,与其记一个数字,不如去站内的资料库里对照一下不同任务的常见做法。想知道某个模型或某个工具在类似任务上的评估口径,可以到 模型资料库(/models) 核对当时的说明,别拿别人的比例直接套自己的数据。
划分数据的自查清单
下面这套顺序建议每次切数据前过一遍,顺序不要颠倒。先定切分单位,再定预处理顺序,再确认测试集的使用次数,最后目测两边分布。这四步能把绝大多数低级错误挡在前面。
- 确认切分单位先想清楚是按单条样本切,还是按用户、按会话、按时间切。带用户或时间维度的数据,绝不能按单条随机切。
- 确认预处理顺序所有统计量(均值、方差、分位数、编码表)只能从训练集算出来,然后套用到验证和测试。切分必须在预处理之前。
- 确认测试集使用次数如果测试集已经被反复用来调参,它就不再是测试集。要么重新留一份从没碰过的,要么承认当前分数只是验证集分数。
- 目测两边分布对比类别占比、长度分布、来源分布,看两边是否大致对齐。肉眼过一遍,比只看一个总分有用。
- 记录划分方式把切分单位、比例、随机种子、预处理顺序写下来。下次换人接手或者分数异常时,这份记录能省很多时间。
自查清单里最容易漏的是最后一条「记录划分方式」。很多人只记住结果,不记住怎么切的,等到分数异常想回溯时,连当时用的哪个随机种子都说不清。把划分方式当成实验的一部分记下来,比多调几个参数值。
下一步如果你想看看不同任务、不同模型常见的评估口径和划分做法,可以到模型资料库对照当时的说明;如果你想确认自己用的中转服务在长上下文或批量测试场景下表现稳不稳,可以到工具目录跑一次速度测试,把划分方法和实际表现对上号。→常见问题
训练数据和测试数据必须来自同一个数据集吗?
理论上要同分布,但不必来自同一个文件。真实场景更常见的是从同一批业务数据里切,或者用同一时期、同一渠道的数据。如果测试数据来自完全不同的渠道或时期,分数会失真,那就不是模型好坏的问题,而是评估口径的问题。
测试集用过了还能再用吗?
偶尔看一次判断方向问题不大,但如果反复拿它调参、选模型、改特征,它实际上已经变成验证集了,最后的分数会偏乐观。稳妥做法是留一份从头到尾没碰过的数据做最终确认,或者干脆重新划一份。
数据量很少的时候怎么切?
固定比例硬切会把本就稀少的样本切得更碎。这种情况一般用交叉验证,轮流留一小份做验证,其余用来训练,最后把各轮结果平均。数据少的时候分数波动本来就大,别盯着小数点后几位看。
为什么我的训练准确率特别高,测试准确率却很低?
常见原因有三个:模型太复杂记住训练数据、两边数据分布不一致、或者切分时发生了泄露导致测试集被污染。排查顺序是先查泄露和切分方式,再看两边分布是否对齐,最后才怀疑过拟合并调整模型复杂度。
时间序列数据能随机切分吗?
不能。随机切会把未来数据混进训练里,等于让模型提前知道答案,评估结果没有参考价值。正确做法是按时间先后切,用过去的数据训练去预测之后的数据,并且训练和测试之间要留一段间隔,避免相邻时间点的信息串味。