ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

集成学习入门:Bagging、Boosting与Stacking的核心原理与实战选择

集成学习入门:Bagging、Boosting与Stacking的核心原理与实战选择

做数据分析这几年,我见过太多模型调参到怀疑人生的时刻。不管你怎么换特征、怎么做归一化,单个模型的结果就是卡在一个位置不动了——后来我慢慢明白,那很可能不是模型不够努力,而是单个模型的“视角”天生有盲区。真正让结果再上一个台阶的,往往不是把某个模型磨到极致,而是集成学习:把一群模型组合起来,让它们互相补位。这篇文章是集成学习系列的基础篇,把最核心的知识框架一次性搭好:为什么集成有效、Bagging、Boosting、Stacking三大流派怎么分工、偏差方差到底在说什么,以及实际使用时最容易踩的坑。这一节我们先打好地基,后面几节再逐个展开具体算法和实战项目。

1. 单模型的瓶颈:为什么“一个人”赢不了

1.1 一个模型不够用,通常卡在三种毛病上

单个模型效果不好,翻来覆去逃不过三种情况:欠拟合、过拟合、结果不稳定。

欠拟合就是模型学不动。表达能力太弱,比如拿线性模型去拟合强非线性的数据,或者特征里本身信息不足,模型看到了也学不进去。欠拟合最直观的表现是训练集和测试集误差都偏高,两头不讨好。

过拟合则完全相反。模型太“较真”,把训练数据里的随机噪声都背了下来,训练集上表现漂亮,一换新数据就崩。这种模型本质上是把训练集里的偶然当成了规律,方差极大,预测结果会随着训练集变动而剧烈波动。

还有一种是“不上不下但来回晃”的情况。模型看起来有七八十分,但每次重新训练,结果都在上下浮动。这类模型通常还不至于过拟合,但稳定性堪忧,属于方差偏高。实际建模时,很多人只关注“平均分数”,忽略了“换一批数据分数漂不漂”,结果模型上线后表现忽高忽低。

问题主要现象对应的误差成分
欠拟合训练集和测试集误差都偏高偏差起主导
过拟合训练集误差低、测试集误差高方差起主导
不稳定每次重训结果差异明显方差起主导

1.2 群体决策为什么会比个体更可靠

生活中的“集成思想”其实无处不在。比如一群朋友聚餐选餐厅,一个人定,很容易被自己的口味偏好带偏;让几个口味不同、关注点也完全不同的人一起讨论,最后的选择往往更均衡。集成学习也是一样的道理:每个模型相当于一个决策者,它有自己擅长的模式和固有的盲区,组合起来就能互相纠偏。

但组合不是随便堆。要让群体真正发挥效果,一个关键前提被很多人忽略:成员要“好而不同”。好,指每个模型至少不能太差;不同,指模型犯的错误最好不要集中在同一批样本上。如果两个模型总是在同一个地方犯错,组合起来就没有任何进步。

做个简单的数学直觉测试。假设我们有若干个完全独立的分类器,每个的准确率只有60%,用多数投票来决定最终结果,准确率会是多少?我算过几个常见规模的结果:

集成规模单个准确率多数投票后准确率
3个60%64.8%
5个60%68.3%
7个60%71.0%

以5个为例,多数正确至少需要3个人答对:三对人的概率是10 * 0.6³ * 0.4²约等于0.3456,四对人约0.2592,五人对约0.0778,合计约0.6826。一群60%的“普通人”通过投票可以逼近70%,这正是集成学习威力的最直接证明。当然,现实里的模型不可能完全独立,效果没有理论上这么夸张,但方向是对的。

2. 三大流派的分水岭:Bagging、Boosting与Stacking

2.1 Bagging:并行干活,各练各的

Bagging是Bootstrap Aggregation的简称,核心动作是“自助采样”。训练每个基学习器时,从原始数据里有放回地抽取一份和原始数据集大小相同的样本集;每个模型用自己那份样本集训练。因为有放回,不同模型拿到的数据会有重叠但不完全相同。一个样本在n次独立抽取中一次都没被抽到的概率大约是36.8%,也就是说每个模型大约只能看到63.2%的原始样本,剩下约36.8%的样本没有参与该树的训练,这部分被称为袋外样本,可以当作白送的验证集。

Bagging的训练过程天然适合并行,因为每个基模型互不依赖,谁也不用等谁。组合方式也很简单:分类问题多数投票,回归问题取平均。为什么Bagging有效?前面说过,多个独立模型的预测取平均后,方差会显著缩小;Bagging正是通过数据扰动让基模型之间尽可能不相关,从而把整体方差压下来。它特别适合那种“方差大、不稳定”的模型,典型代表就是不剪枝的长决策树。

2.2 Boosting:串行修炼,专攻短板

Boosting的思路和Bagging完全相反。它一批一批地训练模型:先训练第一个,然后根据第一个模型的错误调整训练重点,让下一个模型更关注之前做错的样本;训练完第二个后再继续,如此反复迭代,最后把每轮的成果按权重组合起来。

因为后一个模型必须等前一个训完才能接着做,Boosting本质上是串行的,不像Bagging那样可以轻松并行化。Boosting最大的意义是“从弱到强”:单个弱学习器只要比随机猜测好一点,经过多轮迭代加权,组合后的模型可以被推得很强。AdaBoost、GBDT,以及工业界主流的梯度提升树实现,都是这条路的产物。

2.3 Stacking:让不同类型的模型学会分工

Stacking和Bagging、Boosting的底层逻辑都不一样。它不靠同一类模型的“重复劳动”,而是先用多个不同类型的模型当底层,比如线性模型、近邻法、树模型、核方法混合着上;然后把它们的预测结果拼成新的特征,交给一个第二层模型。

第二层模型学习的不是具体规律,而是“什么时候该相信哪个底层模型”。它能看到每个模型在哪些样本上靠谱、在哪些样本上不靠谱,从而学出一套权重或规则。这个思路很灵活,但有一个非常致命的坑:第二层的输入特征必须来自交叉验证,不能直接用训练集上的预测。否则第一层模型早就把训练集背下来了,喂给第二层的特征几乎保真,第二层当时学得很开心,一到新数据就打回原形。

2.4 实际选型时怎么挑流派

三者的对照关系可以这样记:

维度BaggingBoostingStacking
训练方式并行串行分层两阶段
主要作用降低方差降低偏差融合异质模型
对基模型要求强一些更好弱模型也够用多样性更重要
典型代表随机森林系列AdaBoost、GBDT族两层组合模型
并行难度容易较难中等

一个实用的判断题:如果当前模型“一换数据就飘”,优先试Bagging;如果当前模型“怎么调都不够准、像欠拟合”,优先试Boosting;如果手头已经有几个擅长不同规律的模型,而且它们的错误恰好互补,用Stacking把它们串起来。这不是铁律,但按这个方向去试,往往比乱套模板快得多。

3. 偏差-方差分解:解释“为什么集成会赢”

3.1 误差可以拆成三块

用打靶来理解偏差和方差很直观:靶心是真实规律,弹着点是模型的预测。

偏差高,说明弹着点整体偏向某个方向,系统性地不准;方差高,说明弹着点非常分散,虽然平均位置可能还行,但每一枪落在哪里很飘。

更形式化一点,对任意样本x,模型预测h(x)与真实标签y的期望平方误差可以分解成三块:

期望平方误差 = 偏差的平方 + 方差 + 噪声方差

第一项是模型平均预测与真实规律的系统性偏离,第二项是模型围绕自身均值波动的程度,第三项是数据本身不可去除的随机噪声。模型能优化和干预的,只有前两项。很多人调参时总想干掉所有误差,但噪声那部分无论如何都躲不掉,不要在这上面浪费精力。

3.2 Bagging为什么主要压方差

假设有m个独立同分布、方差为σ²的模型,取平均之后,整体方差会变成σ²除以m。模型之间的相关性越低,这个压缩效果越明显。Bagging通过自助采样让每棵树看到的样本不同,通过随机特征让每次分裂的属性不同,本质上都是为了把树和树之间的相关性按下去。

所以随机森林类方法特别适合那种“单棵树很生猛、但也很激进”的基模型。决策树如果完全不剪枝、长到很深,偏差低而方差高,这恰好是Bagging的主场。反过来,如果基模型本身已经又稳又准,Bagging的提升空间就很有限。

3.3 Boosting为什么主要压偏差

Boosting每一轮都在做什么?训练集没有变,但模型在逐步往目标函数上贴。拿GBDT来说,下一棵树拟合的是当前模型的负梯度方向,翻译成人话就是“现在哪里误差还大,我就集中火力补哪里”。

这意味着,Boosting是一个不断压缩偏差的过程,模型从最初的欠拟合状态被一步一步推向拟合。这也解释了为什么Boosting在偏差主导的场景里收益最大——比如你手里只有很浅的决策树,它们单看都太“笨”了,但经过多轮迭代之后可以被补成强模型。

3.4 偏差和方差的此消彼长

模型调复杂,偏差下降,方差上升;调简单,方差下降,偏差上升。这就是机器学习中最经典的跷跷板。单个模型能做的,只是在某个中间位置寻找相对合脚的平衡点。

集成学习高明的地方在于,它提供了一条绕开跷跷板的路径:不强行在单个模型上找最优平衡,而是用多个“偏科”的模型互相搭配。Bagging在保住低偏差的前提下把方差压下来,Boosting在保住低方差的前提下把偏差降下去。理解到这个程度,你回头看调参,会清楚自己每一步到底在动哪一头。

4. 随机森林的“双随机”机制与使用心得

4.1 样本随机加特征随机

随机森林是Bagging配CART树的产物,但它的“随机”有两层。

第一层是样本随机,就是我们前面讲的自助采样。每棵树都有自己的训练集,样本不完全一样,学到的规律自然有差异;每棵树大约有36.8%的样本没参与训练,这些袋外样本可以用来做内置评估,相当于白送一个验证集,省时间。

第二层是特征随机。每次构造树节点分裂时,不是从全部特征里找最优切分,而是先随机抽一小撮特征作为候选,再从那几个候选特征里挑最优。分类任务里,候选特征个数通常取特征总数的平方根;回归任务里常用三分之一左右。

第二层随机常常被忽略,但其实特别关键。如果某个强特征一直存在,几乎每棵树都会优先拿它分裂,最后所有树都长得像兄弟姐妹,Bagging的方差压缩效果就会大打折扣。只有双随机同时起作用,才能既保证每棵树有一定质量,又保证树和树之间足够有差异。

4.2 树该长多深

随机森林里的树,通常主张“长得很深,不做太多剪枝”。原因是:单棵树已经通过随机扰动控制住了相互之间的相关性,方差主要靠大量树的平均来吸收;如果一上来就把每棵树限制得很浅,树与树之间的差异确实会更明显,但单棵树的偏差也同时变大了,集成效果未必更好。

我的实际操作习惯是:先把树的深度放开,看看随机森林的整体表现;确认过拟合明显之后,再去限制叶节点最小样本数或树深度,而不是一开始就剪枝成型。在大多数开源机器学习库里,随机森林的默认参数通常已经比较能打,优先调“特征随机范围”往往比调深度性价比更高。

4.3 随机森林在实战里的定位

随机森林在结构化数据上一直是非常稳的选择。它对异常值相对不敏感,因为单棵树的极端结果会被平均过程冲淡;它也不需要精细到苛刻的调参,几百棵树组合起来,线上表现通常不会太差。

它附带的一大福利是直接输出特征重要性。不过要注意,这个重要性排序只能当参考:数值型特征在分裂中更容易占到便宜,基数高的离散型特征重要性也容易被抬高。要真正判断一个特征要不要留,最好跑个简单的剔除验证再说。不要让特征重要性完全替你做决策。

5. Boosting家族如何一步一步修正错误

5.1 AdaBoost:给错题不断加权

Boosting早期的代表是AdaBoost。它给每个样本挂一个权重,一开始大家的权重都相等;第一轮训练完成后,把分错样本的权重调大,把分对样本的权重调小;下一轮的弱学习器在优化目标时,就更努力去迁就那些高权重样本,相当于盯住错题本反复练。每轮还会根据当前错误率给弱学习器算一个话语权,错误越少,话语权越大,最后把所有模型按话语权加权投票。

这套思路非常优雅,但也有一个致命弱点:异常值会变成“最扎眼的错题样本”,权重一轮轮被放大,模型可能被极少数异常点绑架。如果你的数据噪声大、异常值多,用AdaBoost要多留个心眼。

5.2 GBDT和梯度下降的关系

GBDT比AdaBoost又进了一步。它不再纠结于修改样本权重,而是把“当前模型的损失”看成一座山坡,每新增一棵树,就往损失下降最快的方向迈一步。那个方向就是负梯度。

为什么要在概念上区分“负梯度”和“残差”?因为残差只是平方损失下的一个特例。换成绝对损失、Huber损失或对数损失时,“下一步往哪走”就不能用简单的残差描述了,但负梯度永远有意义。所以GBDT本质上是函数空间里的梯度下降,每一棵树就是一步梯度的更新。这也是GBDT能覆盖回归、分类、排序等各种任务的原因——你只需定义好损失函数,优化路径会自动浮现出来。

5.3 工业界梯度提升实现为什么能赢得默认首选

后来工业界的梯度提升实现在GBDT框架上做了几件重要的事:利用二阶导数加速收敛,类似牛顿法的思路;加入正则项控制单棵树复杂度;支持列采样和行子采样,进一步降低过拟合;还能自动处理缺失值。

最关键的提升是效率。在大规模数据面前,传统逐个枚举切分点的做法太慢,于是有的实现用直方图算法简化计算,有的用单边采样只对梯度大的样本认真训练,有的把互斥特征捆绑起来降维。这些手段让Boosting在千万级样本上也能快速迭代,慢慢成了结构化数据处理里的默认选择。

5.4 Boosting的过拟合风险与常见对策

Boosting不是迭代越多越好。训练后期,模型往往会开始背训练集中的噪声,泛化表现反而下跌。几个我实际踩过之后的经验:

  • 把学习率设低一些,轮数设高一些,配合早停判断什么时候加不动了。
  • 通过限制每棵树的叶子数、最小样本数,压住单棵树的复杂度。
  • 想让方差再小一点,就开行采样、列采样。
  • 训练之前先做异常值诊断。Boosting对异常值比Bagging敏感得多,极端点尤其明显。

如果你发现训练集误差持续下降、验证集误差却在反弹,基本就是迭代过头了。早停不是玄学,是Boosting用户最常用、也最值得用的防过拟合手段。

6. 基学习器的多样性:“好而不同”才是集成的灵魂

6.1 多样性从哪里来

要让基学习器之间“不同”,手段其实不少。按我的习惯可以归成几类:

  • 样本扰动:用自助采样,让每份训练数据不一样。
  • 特征扰动:随机挑选特征子空间,让每棵树看问题的角度不同。
  • 算法扰动:混用线性模型、树模型、近邻、核方法等,模型类型本身就不一样。
  • 参数扰动:同一个算法,给不同深度、不同学习率、不同随机种子。
  • 输出扰动:对标签做形式变换,比如排序转换、少量翻转。

在实际项目里,同质集成最常用的是样本扰动加特征扰动;异构集成则主要靠算法扰动。具体选哪几种,要在“增加差异”和“保持质量”之间找平衡,并不是扰动越多越好。

6.2 “好而不同”是最容易被忽略的那句话

集成学习最容易被忽略的边界条件,就是“好而不同”四个字。

“好”强调每个基学习器要基本可靠。如果硬塞进一个效果很差的模型,它每次投票都相当于投错误票,反而给整体拖后腿。“不同”强调错误要互补。两个模型准确率一模一样,如果错也错在同样的样本上,集成后准确率还是原来那个数,白忙一场。

判断到底“不同”到什么程度,最直接的方法就是看两个模型在验证集上的预测差异。如果它们的预测相关性接近1,集成几乎没有意义;如果错误分布比较散,集成收益立刻就能看到。我见过不少人把高度相关的模型硬拼在一起,完了还奇怪效果为什么没涨,根子就在这儿。

6.3 异构集成的两个实操提醒

第一,小心概率输出的分布差异。不同模型预测出来的分数不在一个量纲上:树模型的概率输出通常比较陡,集中在两头;线性模型的概率输出相对平,贴近中间。如果直接把它们的原始概率平均,量纲偏激的那个会暗中主导结果。稳妥的做法是先把每个模型的预测按样本排序,转成统一的分位秩,再去做平均,或者让第二层模型自己去学权重。

第二,Stacking的第二层模型别搞得太复杂。第二层的数据量其实就是原始样本量,没有海量特征,用线性模型或者很浅的树通常已经够用。第二层越复杂,越容易把第一层留下的噪声一并学了进去,得不偿失。常见的一个大坑我已经提过,再强调一次:生成第一层元特征时一定要用K折交叉验证,让第二层看到的预测是“从没见过该样本”时得到的预测。这一步只要偷懒,Stacking基本必翻车。

这一节把基础框架搭完,后面就能挨个拆具体算法了。下一节我们先拿Bagging和随机森林开刀,把它从理论到参数掰开揉碎,练到可以直接上手用的程度。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进