训练集、验证集、测试集
把数据切成三份,各干各的活。搞混了,模型在开发阶段看着很棒,到真实世界就崩。
模型会过拟合——把训练数据背下来,遇到新数据就失灵——所以你不能用它在自己训练过的数据上的表现来评判它。解法是一套纪律:把数据切成三份,每份职责不同,绝不让它们互相泄漏。做对了,你的开发阶段数字就能预测现实。做错了,你上线的模型看着优秀,跑起来一塌糊涂。
三份数据,三种职责
训练集 —— 模型从它身上学。梯度下降用这些样本来调整权重。通常占比最大,常见是 70-80%。
验证集 —— 你用它做关于模型的决策,而不是拿它训练。几层?学习率多少?什么时候停?你把各种选项试一遍,在验证集上检查,留下效果最好的那个。模型从不从这些样本里学,但你会。
测试集 —— 封存到最后。只用一次,用来估计成品模型在真正的新数据上表现如何。它是真实世界的替身。
为什么是三份而不是两份,这一点微妙但重要。训练集对泛化能力的度量毫无价值。验证集有用——但你一旦开始对着它调参,就已经在间接地拟合它了。所以你需要第三份数据,它既没沾过学习、也没沾过调参,好给出一个诚实的最终数字。
验证集为什么会被「用坏」
这里有个坑,很多人栽在上面。假设你试了 50 个模型变体,挑了在验证集上得分最高的那个。那个胜出的分数现在偏乐观了。50 次尝试里,总有某个模型在这份特定数据上表现好是运气,而你恰恰就是照着这份运气选的。
这是高一个层级的过拟合——不是模型过拟合了训练数据,而是你通过反复做选择过拟合了验证集。你对着验证集做的决定越多,它的数字就越不可信。测试集的存在理由正在于此,它必须保持未被触碰,原因也在于此:它是唯一一个没被选择压力污染过的测量值。开发过程中偷看它一眼,你就毁掉了手里唯一诚实的估计。
头号大错:泄漏
数据泄漏是指验证集或测试集的信息溜进了训练,它造成的失效最危险——藏到部署之后才发作。
具体的发生方式:
- 重复或近乎重复的样本被切分到了不同集合里。模型在训练时实际上就见过了测试样本,测试分数自然被抬高。
- 切分之前用整个数据集做归一化。 这样缩放就带上了测试集的信息。归一化的统计量只在训练集上算,再应用到其他集合。
- 按时间排序的数据被随机切分。 如果你是在预测未来,用测试时间段之后的样本做训练,就等于让模型偷看。时间序列数据必须按时间切,不能打乱。
泄漏总是让你的开发数字好过现实。正是这种不对称让它如此危险:它从不以「变差」的方式给你警告。差距只在模型遇到真实数据、让人失望时才显露出来。
切分点在哪里
切分要有意义,三个集合就必须像模型真正会遇到的数据。由此有几条推论:
通常随机切分 —— 这样每个集合都是公平的样本。但数据里存在结构时,要尊重它:时间序列按时间切,成组的样本(同一个病人的所有记录、同一个视频的所有帧)整组留在同一个集合里,否则模型可以背下一整组,然后又被考同一组。
让比例保持诚实。 如果某个类别很罕见,要保证它在三个集合里都以相近的比例出现,否则你的验证和测试数字会在一小撮样本上抖个不停。
数据稀缺时:交叉验证
单次切分浪费数据——五分之一的数据从不参与训练——而且小验证集给出的分数噪声很大。交叉验证(cross-validation)两个问题都能解决。把数据分成 k 折,训练 k 次,每次留出不同的一折做验证、用其余的做训练。最后把 k 个分数取平均。
每一轮下来,每个样本都既做过训练也做过验证,而取平均能抹平任何单次切分的运气。代价是要训练 k 遍,所以交叉验证在中小数据集上很常见,到了 LLM 预训练这个量级就不现实了——那里有一个留出集就够了。在这之上,仍然要另留一个测试集封存起来。
这就是评测的标准答案
关于模型质量的每一个诚实结论,最终都要追溯到这套纪律。当你读到某个模型在某个基准测试上拿了多少分,决定这个数字有没有意义的问题是:这个基准真的被留出了吗,还是漏进了训练?这份担心——基准测试污染——就是训练/测试边界在整个领域尺度上被打破。整个评测层都建在这里打下的地基上。
要点回顾
- 训练教会模型,验证指导你关于模型的决策,测试给出唯一一次诚实的最终估计。
- 你会因为对着验证集调参而逐渐过拟合它,所以一个未被触碰的测试集必不可少。
- 数据泄漏——集合间的重复样本、用整个数据集做归一化、时间上偷看未来——会抬高你的数字,并藏到部署后才发作。
- 切分要让各个集合像真实数据:一般随机切,时间序列按时间切,成组数据按组切。
- 交叉验证通过轮换留出折来重复利用稀缺数据;小数据集上是标配,预训练规模上不现实。
下一篇: AI 中的概率 —— 语言模型用来表达不确定性、产出输出的语言。