第二部 · 学习第 5 章 / 14

真正的考试在训练集之外

一个模型在见过的数据上表现好,不说明任何事。这一章讲泛化、过拟合、评估,以及数据里最常见的几个陷阱。

主线 7 分钟 · 深入 8 段 · 实验 1 个
去年的卷子和今年的卷子。
去年的卷子和今年的卷子。

一个学生把去年的期末试卷连题带答案背得滚瓜烂熟,今年考试换了题目,他一道也不会。你不会说他「学会了」。可是如果只看他做去年试卷的成绩,他是满分。

机器学习里每天都在发生这件事。一个模型在训练数据上的表现,就像那个学生做去年试卷:它见过答案。真正说明问题的,是它在从未见过的数据上的表现。这个能力叫泛化,它是机器学习唯一真正的目标,其他一切,损失、梯度、参数,都是为它服务的。

这一章讲怎样衡量泛化,怎样识别一个模型只是在背答案,以及数据里那几个能让「准确率 99%」的模型在现实中翻车的陷阱。它没有多少新的算法,但它是这门课里最能保护你不被数字骗的一章。

过拟合:学得太用力

上一章的实验里你可能已经见过:用一个高次多项式去拟合十个点,曲线可以穿过每一个点,训练损失是零。但在点与点之间,曲线疯狂扭动,对任何一个新点的预测都离谱。

这就是过拟合:模型不但学到了数据里的规律,还学到了数据里的噪声和巧合。它把「这十个点恰好在这里」当成了「世界就是这样」。模型越灵活(旋钮越多),数据越少,越容易过拟合。

左:穿过每一个点的曲线。右:贴近它们的曲线。哪一条更懂这些点?
左:穿过每一个点的曲线。右:贴近它们的曲线。哪一条更懂这些点?

反过来是欠拟合:模型太简单,连数据里真实的规律都抓不住。用一条直线去拟合一段明显的曲线,训练损失就下不去。

这两者之间的平衡,是机器学习里永恒的拉锯。你希望模型足够灵活,能表达真实世界的复杂;又希望它不要灵活到把偶然当必然。有一些常用的办法把它拉回来:给模型更多数据(噪声会互相抵消,规律会更突出);限制模型的复杂度;在损失函数里加一项「惩罚旋钮拧得太大」的正则项;或者在训练损失还在下降、但验证损失开始回升的那一刻停下来。

统计学家把预测误差拆成三部分。偏差是模型「系统性地错」:它太简单,不管给多少数据都抓不住真实规律,误差集中在同一个方向。方差是模型「不稳定地错」:换一批训练数据,它学出来的东西就大不相同,因为它对数据里的偶然太敏感。第三部分是不可约误差:数据本身的噪声,任何模型都消不掉。

欠拟合是高偏差,过拟合是高方差。增加模型复杂度会降低偏差、增加方差;增加数据量会降低方差而不改变偏差。这个分解解释了为什么「更多数据」在深度学习时代如此重要:深度网络的偏差很低(它几乎什么函数都能表达),瓶颈几乎总在方差上,而压方差最可靠的办法就是数据。

它也解释了一个看起来矛盾的现象:为什么参数量远超样本量的大模型没有严重过拟合。经典理论预测它们方差会爆炸,但实践中,足够大的模型加上足够多的数据、恰当的正则化和早停,泛化反而更好。这个现象被称为「双下降」,是 2019 年之后理论界仍在消化的问题。

考试制度:把数据分成三份

既然训练数据上的表现不可信,就必须留一部分数据不给模型看。标准做法是把数据分成三份。

训练集:模型在上面学。验证集:训练过程中用来调整超参数(学习率、模型大小、什么时候停)的数据,模型不直接在上面学,但你的决定受它影响。测试集:直到最后才拿出来考一次的数据,此前任何决策都不能碰它。

为什么要分验证和测试?因为如果你反复根据某份数据的表现调整模型,你自己就成了「过拟合」的一部分:你会不自觉地把模型调到在那份数据上最好看的样子。测试集必须是一份连你都没有反复看过的数据,它给出的数字才是对「新数据」的诚实估计。

在有时间顺序的数据上,划分还有一条铁律:用过去预测未来,而不是随机打乱。用 2025 年的订单训练、2024 年的订单测试,模型看起来很准,但那是因为它偷看了未来。

把数据分成三份 训练集 · 模型在上面学(约 70%) 验证集 · 调参数 测试集最后只考一次 反复看验证集调模型,你自己就成了过拟合的一部分;测试集必须连你都没反复看过。 有时间顺序的数据:用过去预测未来 2023 – 2025 上半年 · 训练 2025 下半年 · 测试 随机打乱再划分,模型会偷看未来,成绩会假得漂亮。
三份数据各司其职;有时间的数据必须按时间切。

把数据分三份的做法有一个前提:数据足够多,留出一部分也不心疼。当你只有三百个样本(一个班级三年的记录、一种罕见病的病例),留出三分之一做测试,训练数据就不够了,而且测试结果会因为「恰好留出了哪些样本」而剧烈波动。

交叉验证是标准的补救:把数据分成 $k$ 份(常用 5 或 10),轮流拿其中一份做测试、其余做训练,得到 $k$ 个成绩,取平均。每个样本都当过一次考题,又都参与过训练,成绩的估计既充分又稳定。代价是要训练 $k$ 次。

它还有一个更重要的用途:比较不同的模型或设置时,只看一次划分的结果很可能是噪声在做决定,交叉验证的平均值和方差能告诉你差距是不是真的。当有人说「我们的新方法比旧方法高了 0.8 个百分点」,值得问一句:这个数字是一次划分的还是交叉验证的,方差有多大。

需要注意的仍然是时间顺序:对有时间的数据,交叉验证的每一折也必须「用过去测未来」,否则又是偷看未来。

准确率之外

「准确率 99%」是最常被引用、也最容易误导的数字。

某种疾病在人群中的发病率是 1%。一个模型不管看到谁都说「没病」,准确率就是 99%。这个模型毫无用处。类似的,垃圾邮件过滤、欺诈检测、学情预警,我们真正关心的那一类(有病、是欺诈、需要帮助)往往是少数,准确率会被多数类淹没。

所以需要更细的指标。把预测结果分成四格,叫混淆矩阵

实际是「是」实际是「否」 预测「是」预测「否」 真阳性找对了 假阳性误报 假阴性漏报 真阴性放对了 精确率模型说「是」的里面,多少真的是真阳性 ÷ (真阳性 + 假阳性) 召回率所有真的「是」里,找出了多少真阳性 ÷ (真阳性 + 假阴性) 癌症筛查要高召回(宁可误报);给学生贴标签要高精确(误报伤人)。选哪边是价值判断。
混淆矩阵:四个格子,两个比率。

精确率回答:模型说「是」的那些里,有多少真的是?召回率回答:所有真的「是」里,模型找出了多少?两者通常此消彼长:把门槛放宽,召回率上去,精确率下来。

该偏向哪一边,取决于两种错误的代价。癌症筛查宁可误报(再查一次就是了),不可漏报,所以要高召回。给学生贴「高风险」标签则相反,误报会让一个正常的孩子被另眼相看,代价可能比漏报还大。指标不是技术选择,是价值选择。

大多数分类模型输出的不是「是」或「否」,而是一个概率,比如「这封邮件是垃圾邮件的概率是 0.83」。是否判为垃圾邮件,取决于你把门槛设在哪里。门槛 0.5 是默认,但没有任何理由认为它是对的。

把门槛从 0 滑到 1,每个位置都会得到一对(假阳性率,真阳性率),把它们画成曲线,就是 ROC 曲线。曲线下的面积(AUC)衡量模型在所有门槛下的整体区分能力:0.5 是瞎猜,1 是完美。它比准确率稳健得多,因为它不依赖于任何一个门槛,也不受类别比例影响。

但 AUC 也只是一个数。真正的部署决策是:在这条曲线上选哪个点?这个问题模型回答不了,因为它需要知道误报和漏报各自的代价,而代价是由使用它的人和被它影响的人决定的。这是本章反复强调的一件事:评估指标的选择本身就是一个价值判断,把它藏在「技术细节」里是不诚实的。

准确率是一个平均数,平均数会藏东西。一个整体错误率 5% 的模型,可能在某个人群上错误率 1%、在另一个人群上 20%。只要后者人数少,平均数就看不出来。

这不是假设。人脸识别系统在不同肤色、性别人群上的错误率差异被多次独立测量过,差距可达几倍到几十倍,原因通常是训练数据里某些人群的样本太少。语音识别对口音、儿童声音、老年人声音的错误率也显著高于「标准」成年人。学情预警模型如果用历史数据训练,而历史上某类学生被老师关注得更少、记录更不完整,模型就会「学会」对他们更不准。

技术上的应对是把评估指标按人群拆开报告:不只报一个准确率,而是报每个群体的精确率、召回率,并检查差距。更难的是决定「公平」指什么:让各群体的误报率相等,和让各群体的漏报率相等,在数学上通常不能同时做到,必须选一个。这又是一个价值判断,不是技术判断。

对使用者的一条实用建议:当一个 AI 产品报告准确率时,问「按人群拆开呢?」如果对方答不上来,说明他们没测过,也就不知道它对谁不准。

泄漏之所以危险,是因为它让模型在测试时看起来很好。下面这些形式在真实项目里反复出现。

  1. 未来的信息混进了特征。用「最终成交价」的某个函数来预测成交价;用「是否开住院单」预测住院。
  2. 标签的代理变量。「客户经理备注长度」与「是否违约」高度相关,因为违约后经理会写很多备注。
  3. 数据处理顺序。先对全部数据做标准化(减均值除方差),再划分训练和测试,测试集的统计量已经进入了训练。
  4. 重复样本跨越划分。同一个病人的多次检查被随机分到了训练集和测试集,模型「认出」了这个人。
  5. 时间上的偷看。随机划分有时间顺序的数据。
  6. 标识符携带信息。样本编号是按时间或按类别分配的,模型从编号里学到了标签。
  7. 人工标注的痕迹。标注员在正样本的图片上留下了固定的裁剪方式或水印。
  8. 缺失值的模式。某个字段只在阳性样本里缺失(因为流程不同),「是否缺失」本身成了答案。
  9. 系统行为的副作用。账号在退学后被停用,「最后登录时间」泄漏了退学。
  10. 测试集被反复用来选模型。这是人自己造成的泄漏,见本章「考试制度」。

发现泄漏的一个可靠信号是「好得不像话」:如果一个新特征让准确率从 80% 跳到 98%,先怀疑泄漏,再庆祝。

三个陷阱

准确率高、指标漂亮,模型上线后仍然可能翻车。原因往往不在模型,而在数据。下面三个陷阱在实际项目中反复出现。

抽样偏差:训练数据不能代表要预测的对象。一个用三甲医院数据训练的诊断模型,在社区医院可能失灵,因为来三甲的病人本来就更重。一个用某个城市的订单训练的定价模型,换个城市就不准。数据只能教模型「它见过的世界」。

标签泄漏:某个特征偷偷包含了答案。预测病人是否会住院,特征里有「是否开了住院单」;预测学生是否及格,特征里有「期末成绩」。模型会学到接近完美的准确率,因为它作弊了,而作弊的特征在真正需要预测的时刻是不存在的。泄漏往往很隐蔽:一个「最后一次登录时间」的特征,可能因为系统在学生退学后自动清空账号而泄漏了退学信息。

为晴天造的船,遇上了雨。
为晴天造的船,遇上了雨。

分布偏移:世界变了。用疫情前的数据训练的出行模型,疫情一来全错。用去年的学生训练的预警模型,今年换了教材和考试方式,模型还在按去年的规律判断。所有模型都默认「未来像过去」,而这个假设没有任何保证。这也是为什么部署不是终点,监控才是。

相关不是因果

最后一个陷阱不是数据的,而是思维的。

模型学到的全部是相关:什么和什么倾向于同时出现。冰淇淋销量和溺水人数高度相关,模型会忠实地学到这一点,但它们的共同原因是夏天。一个学情模型可能发现「用平板做作业的学生成绩更好」,但这可能只是因为买得起平板的家庭有更多其他资源。

相关足以做预测:知道冰淇淋销量确实能帮你预测溺水人数。但相关不足以做干预:给每个孩子发平板不会自动提高成绩。当有人拿着模型说「数据显示 X 导致 Y」,请把「导致」换成「伴随」再读一遍。区分相关与因果需要的是实验设计和领域知识,不是更大的模型。

既然模型只能学到相关,怎样才能知道「做 X 会不会导致 Y」?统计学在一百年前就给出了答案:随机实验。把对象随机分成两组,一组做 X,一组不做,比较 Y。随机分组保证了两组在所有其他方面(包括你没想到的那些)平均而言相同,于是差异只能来自 X。

互联网公司把它叫作 A/B 测试,每天做成千上万次:一半用户看到新按钮,一半看到旧的。教育研究把它叫作随机对照试验,是判断一种教学方法是否有效的黄金标准。第五章开头「平板与成绩」的问题,正确的做法不是训练更大的模型,而是随机给一部分学生发平板,一个学期后比较。

随机实验有它的限制。有些干预不能随机(不能随机让一半学生辍学),有些太贵或太慢,有些在伦理上不允许。于是有了一整套「从观察数据里推因果」的方法:自然实验、双重差分、工具变量、因果图。它们都需要对数据产生过程做出假设,而假设需要领域知识。这门课不展开它们,但要记住一句话:因果是设计出来的,不是从数据里挖出来的。当一个 AI 产品声称「发现了 X 导致 Y」,问它:你随机分组了吗?如果没有,你假设了什么?

1973 年,加州大学伯克利分校被指控在研究生录取中歧视女性:总体上男性申请者的录取率明显高于女性。但按院系分开看,大多数院系里女性的录取率反而更高。

矛盾的原因是女性更多地申请了竞争激烈、录取率低的院系。合起来看,这个「申请去向」的差异被隐藏了,制造出一个总体的假象。这就是辛普森悖论:一个变量(院系)同时影响了原因(性别分布)和结果(录取率),把它忽略掉,相关的方向可以完全反转。

对机器学习的教训是:模型学到的相关,可能整个是由一个没进入特征的变量制造的。而找出这种变量,靠的不是更多数据或更强的算法,而是对这个领域的理解。这也是为什么再好的模型也不能替代懂业务的人。

部署不是终点。一个负责任的机器学习系统上线后,至少要盯着四件事。

输入的分布。每个特征的均值、方差、缺失率,与训练时比较。特征分布一变(新平台上线、政策调整、季节更替),模型的表现就不再有保证。这一项能在真实标签到来之前发出预警。

输出的分布。模型预测「高风险」的比例是否突然变化?如果上周标了 8% 的学生,这周标了 30%,多半不是学生变了,是数据变了。

延迟到来的真实表现。很多任务的标签要等一段时间才有(期末成绩、是否再入院、是否违约)。等它到了,重新算一遍指标,按人群拆开。

人的反馈。使用者是否在绕过系统?老师是否不再看预警名单?这些信号比任何指标都早。

监控的成本常常超过训练的成本,而这笔账在立项时往往没人算。第二章两次寒冬的教训之一就在这里。

带走一句话

模型的成绩要在它没见过的数据上考;准确率要拆成误报和漏报分开看;数字漂亮之后,还要问数据从哪来、有没有泄漏、世界有没有变。

自测先自己想,再点开答案
  1. 一个模型训练损失接近零,验证损失却很高。发生了什么?可以怎么办?

    答案

    过拟合:模型记住了训练数据的噪声和巧合。可以增加数据、降低模型复杂度、加正则项,或者在验证损失开始回升时早停。

  2. 为什么验证集和测试集要分开?只留一份「没训练过的数据」不够吗?

    答案

    如果你反复根据同一份数据调整模型,你的选择会逐渐偏向在那份数据上好看的方向,等于间接过拟合了它。测试集必须是最后才看一次的数据,才能诚实估计对新数据的表现。

  3. 一个欺诈检测模型准确率 99.5%。你还需要知道什么才能判断它好不好?

    答案

    欺诈交易的比例(如果只有 0.3%,一个「全判正常」的模型准确率也有 99.7%)、精确率和召回率、以及误报和漏报各自的代价。

  4. 「使用在线学习平台时长长的学生成绩更好」,学校据此要求所有学生每天在线两小时。这个推理错在哪?

    答案

    它把相关当成了因果。时长和成绩的关系可能来自第三个因素(比如学习动机强的学生本来就会多用、也会考得好)。强制延长时长不一定改变那个因素,也就不一定改变成绩。

本章术语

泛化Generalization

模型在没见过的数据上表现稳定的能力。它是机器学习唯一真正的目标,损失、梯度、参数都为它服务。训练数据上的表现不能说明泛化,必须在留出的数据上考。

过拟合Overfitting

模型不但学到了规律,还把训练数据里的噪声和巧合当成规律,于是训练表现很好、新数据上很差。模型越灵活、数据越少越容易发生。对策:更多数据、限制复杂度、正则化、早停。

欠拟合Underfitting

模型太简单,连数据里真实的规律都抓不住,训练损失就降不下去。用一条直线去拟合一段明显的曲线就是欠拟合。与过拟合构成机器学习里永恒的拉锯。

训练集与测试集Train / validation / test split

把数据分成三份:训练集用来学,验证集用来调参数和决定何时停,测试集只在最后考一次。验证与测试分开,是为了防止你反复根据同一份数据调整模型而间接过拟合它。有时间顺序的数据必须用过去预测未来。

准确率Accuracy

预测正确的比例。最常被引用也最容易误导的指标:当关心的类别是少数(1% 的患病率),一个「全判为否」的模型准确率也有 99%。需要拆成误报与漏报分开看。

精确率与召回率Precision & recall

精确率:模型说「是」的那些里,多少真的是。召回率:所有真的「是」里,模型找出了多少。两者此消彼长,取舍取决于误报与漏报各自的代价,这是价值判断而非技术选择。

混淆矩阵Confusion matrix

把分类结果按「预测是/否 × 实际是/否」分成四格:真阳性、假阳性(误报)、假阴性(漏报)、真阴性。准确率、精确率、召回率都从这四格算出。

标签泄漏Label leakage

某个特征偷偷包含了答案(预测是否住院,特征里有「是否开了住院单」)。模型会得到近乎完美的训练成绩,因为它作弊了,而作弊的特征在真正预测的时刻并不存在。往往很隐蔽。

分布偏移Distribution shift

部署后的数据与训练数据不再来自同一个「世界」:疫情改变了出行,新教材改变了学情。所有模型都默认「未来像过去」,而这没有保证。因此部署不是终点,监控才是。

相关与因果Correlation vs. causation

模型学到的全部是相关(什么与什么倾向于同时出现)。相关足以预测,不足以干预:给每个孩子发平板不会自动提高成绩。区分两者需要实验设计与领域知识,不是更大的模型。辛普森悖论是相关方向可被隐藏变量反转的例子。