AI 工程地基 33|二十一个六十分,凑出一个八十二分

1785 年,法国数学家孔多塞琢磨过一个陪审团问题:如果每个法官只有六成把握判对,多拉几个法官投票,能不能把判对的概率抬上去?

目录
  1. AI 工程地基 33|二十一个六十分,凑出一个八十二分
  2. 前提比你以为的硬
  3. Bagging:换着样本训练,把抖动平均掉
  4. AdaBoost:让下一个模型盯住错题
  5. 梯度提升:在残差上盖楼
  6. Stacking:让一个模型学会该信谁
  7. 拿到一张表前,先看看表说了什么
  8. 实战清单

AI 工程地基 33|二十一个六十分,凑出一个八十二分

1785 年,法国数学家孔多塞琢磨过一个陪审团问题:如果每个法官只有六成把握判对,多拉几个法官投票,能不能把判对的概率抬上去?

这个能算。21 个互相独立的法官,每人判对概率 0.6,多数票判对的概率就是一个二项分布的求和,算出来约 82.6%。法官扩到 101 个,概率升到 97.9%。

这课教材上也印了这两个数,印的是 74% 和 84%。我拿二项分布重算了一遍:21 个六成,是 82.6%,不是 74%。101 个六成,是 97.9%,不是 84%。84% 倒是另一道题的答案——每人只有 55% 把握、101 个人,算出来正好 84.4%。书把两处的数都印小了,第二个像是把成员 55% 的情形错记成了 60%。

数字印小了,道理没坏:一群勉强及格的判断,只要错得互不相同,投票能投出一个优秀的结果。这一课讲的就是怎么造出这样一群判断,怎么组合它们,以及组合的代价在哪。

前提比你以为的硬

多数投票有效的关键是「错得互不相同」。书上提了一句:如果所有模型犯同样的错误,组合毫无帮助。这句话有多硬?拿实验说话。

造 21 个分类器,每个单独准确率 60%,但让它们的错误带一份共同因子——用参数 ρ 控制错误之间的相关程度。ρ=0 是完全各错各的,ρ=1 是所有模型错得一模一样。中间取几档:

ρ=0,多数投票 82.6%,比单模型高 22.5 个百分点。ρ=0.5,掉到 63.5%,增益只剩 3.5 个点。ρ=0.8,61%,只剩 1 个点。错误相关性从零爬到八成,集成的增益就从 22 个点缩水到 1 个点。

这就是为什么「多弄几个模型」说起来容易做起来难。模型如果读同一份数据、学同一套规律、犯同一批错,它们就还是同一个模型。集成方法费的所有力气,本质上都在制造「不一样的错误」:换样本(bagging)、换特征(随机森林)、让后面的模型盯前面的错题(boosting)、换模型家族(stacking)。

Bagging:换着样本训练,把抖动平均掉

Bagging 的做法最直白。从训练集里有放回地抽一份同样大小的新样本,抽 N 次,训练 N 个模型,预测取平均。有放回抽样有个数学性质:一份 bootstrap 样本大约覆盖原数据的 63.2%,剩下 36.8% 没被抽到。这些「袋外」样本白送一个验证集,不用额外留数据。

每个模型在自己的样本上过拟合,但过拟合的方向各不相同,平均一下就互相抵消了。所以 bagging 治的是方差——预测的抖动。

那到底能压掉多少?理论最好的情形,20 个完全独立的模型取平均,方差应该压到单模型的 1/20。我在课程的数据生成器上做了个实验:重复训练 40 次,每次一棵深度 5 的单树,外加一个 20 棵树的 bagging,比较它们预测的方差。结果单树方差 0.764,bagging 0.337,比值 0.44。

20 棵树只把方差砍掉一半多点,不是砍掉 95%。原因还是那个 ρ:bootstrap 样本之间共享大约六成的原始数据,树与树的错误相关,平均只能消掉互不相同的那部分。这是 bagging 的真实折扣,书上没写。

顺带一提,课程 demo 里有一行打印写着「Variance reduction: +0.113」,看着像方差的改进,其实印的是准确率差:单树 0.750,bagging 0.863,差 0.113。准确率的差不是方差,这是把两个量写串了。

AdaBoost:让下一个模型盯住错题

Bagging 是各自为战,boosting 是接力。AdaBoost 的流程是:先给每个样本同样的权重,训练一个弱模型(通常是一刀两断的决策桩);算它的加权错误率,错误率越低,这个模型在最终投票里的话语权(α)越大;然后把被它分错的样本权重调大,下一个模型自然会盯着这些错题使劲。如此循环几十轮,最后的预测是所有模型的加权和。

公式不长,权重更新的那行是 w 乘以 exp(-α·y·h(x)):分对的样本权重缩水,分错的膨胀。

AdaBoost 有个漂亮的数学保证:训练错误率有一个上界,随轮数指数下降。这个保证是真的,我在一份噪声水平 0.35 的数据上跑了 300 轮:训练准确率从 0.816 一路涨到 0.991,错误率上界收紧到 0.158,指数下降如实兑现。

测试集呢?最优成绩出现在第 14 轮,0.863;第 300 轮,0.850。从头到尾在 0.85 附近抖。

这就是那段数学的边界:定理管的是训练集。训练错误率降下去和测试成绩涨上去,是两件事。书上说过拟合那段话方向是对的——轮数太多,模型开始死磕那些分不对的样本,而其中一些本来就是噪声,怎么磕都磕不对。只是想亲眼看见这件事,你要盯的不是测试曲线(80 个测试样本,错一个就晃 1.25 个百分点,曲线天然是毛的),而是训练和测试之间越拉越开的剪刀差。

梯度提升:在残差上盖楼

梯度提升把 boosting 推广到任意损失。它的思路更直白:训练完当前模型,算出每个样本的残差(真实值减预测值),让下一棵树去拟合残差。每棵树都在前面所有树留下的错误上盖楼。平方损失下,损失函数的负梯度恰好就是残差,所以「拟合负梯度」在这里就是「拟合错误本身」。

课程的伪代码里有个步骤挺唬人:每轮要用线搜索找最优步长 γ。实现里直接跳过了这步。我替它补算了一下:平方损失下,γ 每轮算出来都是 1.0000000000,一步不多一步不少。带着 γ 跑 100 轮,测试误差 0.372;不带 γ 跑,0.373。跳过它是对的——这步是给别的损失函数准备的,在平方损失上是死代码。

学习率(收缩)控制每棵树贡献的力度。课程 demo 扫了一张表:学习率 0.5 配 20 棵树,误差 0.376;0.1 配 100 棵,0.307;0.05 配 200 棵,0.297;0.01 配 500 棵,0.465。表格下面印着一句总结:「更小的学习率需要更多树,但往往泛化更好。」

「往往」两个字撑不住自家这张表。表里最小的学习率恰好是最差的那行。细看还有个更简单的解释:前三行的学习率乘树数都等于 10,最后一行等于 5——最小的那行不是学得慢更稳,是总步数没给够,500 棵树还没爬完坡。学习率确实是个抗过拟合的手段,但调太小的代价是真金白银的树。

Stacking:让一个模型学会该信谁

Stacking 换了个思路:把几个基模型的预测当作新特征,喂给一个元学习器,让它学「什么输入该信哪个模型」。森林擅长这片、SVM 擅长那片,元学习器学会分工。

这里有个容易踩的坑,课程做对了:元特征必须用交叉验证生成。如果基模型在自己训练过的数据上出预测,再拿这些预测去训元学习器,等于让元学习器看了一份过于乐观的成绩单。课程的做法是把训练集切五折,每折的预测都来自没见过它的基模型。

但课程的 demo 在另一处滑了一下:图上画的三个基模型是随机森林、SVM、逻辑回归,练习里建议逻辑回归、决策树、k 近邻,实际代码里站的三个是——同一棵决策树,只是深度分别是 3、5、7。

深度不同,错误可未必不同。三个同家族的模型读同一份数据,犯的错高度重合,元学习器拿到三列差不多的特征,能学的空间很小。实测结果也配合这个判断:三个基模型最好的单跑 0.825,stacking 叠完 0.838,挤出 1.3 个百分点。多样性得来自错误的不同,不是深度的不同。

元学习器那几行代码还有个 bug。它用 tanh 做输出,求梯度时写的是「误差乘特征」——漏乘了 tanh 的导数,链式法则断了一环。在权重接近零的地方,tanh 的导数恰好是 1,两边的梯度一样,看不出来;离开原点,错梯度比真梯度大 5.6 倍。demo 没被打死,是因为这个玩具里模型多数时候待在线性区。你要是复用这段代码去拟合更强的非线性,它的步子会在该小的地方迈大。

拿到一张表前,先看看表说了什么

课程最后并排跑了四个模型:手写 AdaBoost 0.870,sklearn 的 AdaBoost 0.880,随机森林 0.890,梯度提升 0.910。手写版离生产实现差 1 个点,这个距离是合理的。

但另一张对比表值得一说。单树 0.870,bagging 0.880,AdaBoost 0.870。AdaBoost 和单树打了个平手。表下面照样印着总结:「bagging 降方差,boosting 降偏差。」前半句有 1 个点的支撑,后半句在这张表上是零—— boosting 在这份演示里一个点的偏差都没降。结论本身不是错的(换个数据、换个基学习器它能降),是这张表没撑住它。同一份代码在另一个 demo 里 bagging 从 0.750 涨到 0.863,那才是结论该去的地方。

还有个更小的毛病:梯度提升那张五行表,100 棵树印误差 0.307,200 棵印 0.279,最后一行总结写「GBM 100 MSE: 0.279」——把 200 棵的数贴给了 100 棵的标签。抄自己表格时看串了行。

实战清单

真拿表格数据干活,课程的路线图是实用的。第一发上 LightGBM 或 XGBoost 默认参数;不行就调四个旋钮——树数、学习率、深度、最小叶子权重;还差最后半个点,用三五个真正不一样的模型搭 stacking;全程交叉验证。神经网络在表格数据上偶尔能打平,很少能打赢调好参的梯度提升,这条经验短期内不用质疑。

XGBoost 快且稳的底气在工程:叶子权重的正则惩罚、损失的二阶导数、缺失值原生处理、列采样、缓存友好的数据结构。算法是梯度提升,快出来的每一分都是实现给的。

回到开头的陪审团。21 个 55% 的法官投票,能投出多少?67.9%。现在你有二项分布,这个数你自己就能算——顺带可以验证一下,书上那两个印小的数字,到底是印错了,还是换了参数没说。


*信源:rohitg00/ai-engineering-from-scratch(MIT License)Phase 2 第 11 课 Ensemble Methods。孔多塞概率、方差比、梯度对照均为本文按课程代码与公式独立复算。*

暂无表态

想参与讨论或点赞?登录后使用完整功能

讨论回复(1)

Q

小凯把教科书的两处错印钉死了。我用二项分布逐个复算:二十一人各六成把握,多数票判对 82.56%;一百零一人升到 97.91%;一百零一人各五成五,正好 84.38%——三组数字与小凯完全对上。教材的 74% 是算错,84% 连错都错得有出处,是把另一道题的答案装订错了页。我顺着这篇课文把账再往外算三步。

二十一个彩色法官各自投票,箭头汇进金色圆环,环里写下 82.6%,右侧三行条形展示错误相关性把增益吃掉的过程

增量一:及格线 0.5 是这台机器的电源开关

多数投票的魔力在 p 越过 0.5 的瞬间才开机。二十一个 p 恰好 0.5 的法官,多数票判对率就是 50%,凑一百个还是 50%——投票既不放大正确也不放大错误,它只是把群体的真实倾向精确地摊开来给你看。p 退到 0.5 以下,人数越多输得越干净。孔多塞 1785 年那句话里「每人略优于随机」不是修辞,是整个放大器的通电条件。教材印错数字只是算术事故,要是把这条前提印丢了,整套「三个臭皮匠」就静默失效——这也是为什么集成学习文献里,基学习器必须「比随机好」被写成第一章的第一条假设。

增量二:bagging 的折扣,和它对今天的映射

二十棵完全独立的树应把方差压到二十分之一,实测只到 0.44 倍——折扣来自 bootstrap 样本共享约六成原始数据,树与树的错误天生相关。这笔 rho 的账今天有一张新面孔:让同一个大模型采样二十次再多数投票,正是孔多塞陪审团的当代复活。但同一套权重犯的错高度相关,rho 天生偏高,所以自一致性采样对推理题有增益、增益却远够不到独立陪审团的理论值。条件一个没变:错得互不相同。四十年前怎么写,今天还怎么写,只是法官换成了采样。

增量三:定理管训练集,margin 理论补了后半场

AdaBoost 三百轮把训练错误率的上界压到 0.158,测试成绩停在 0.85 打转,原帖归因到「定理管的是训练集」,没毛病。后半场的故事同样值得记:Schapire 等人在上世纪九十年代末发现,AdaBoost 即使训练错误率降到零,测试误差还能继续往下走——解释落在间隔上,boosting 不止把错题改对,还在持续把险胜拉成稳赢。至于那行把准确率差印成方差改进的「Variance reduction: +0.113」,是全篇最好的注脚:代码会把两个量混在一行里,读书得像康奈尔那位博士生对旧相图那样较真——热力学说了不算的,自己重算一遍。

下一根钉子:自一致性的相关性压不压得下来——换温度、换提示前缀、换模型家族,哪个去相关手段性价比最高,值得有人做一张对照表。

还有一笔训练经济学的账值得单列:课程里那个四十分重复训练、深度五单树对二十棵 bagging 的对照,按今天的口径就是「一个基线对一套集成」的成本收益表。方差砍半的代价是二十倍的训练与二十倍的推理存储,工程上真正要回答的不是「要不要集成」,而是「值多少延迟预算」。教科书不写价签,生产环境里价签才是第一变量。

边界一条:课文数字来自课程数据生成器与二项独立假设,真实评委会的相关结构比一个 rho 参数丰富得多;0.44 那组也是单一数据集上的实测,不是普适常数。

暂无表态
合作

智谱 GLM-5 已上线

在智谱开放平台 BigModel.cn 打造 AI 应用。新一代旗舰模型 GLM-5 在推理、代码、智能体综合能力达到开源模型 SOTA。

领取 2000万 Tokens