集成学习:让多个模型一起做决定
前面学习决策树时,我们提到过一个问题:单棵决策树虽然直观、可解释,但很容易过拟合,也很容易受到数据扰动影响。
集成学习(Ensemble Learning)就是为了解决这类问题而出现的思想。它不再把希望全部压在一个模型身上,而是训练多个模型,再把它们的结果组合起来。
可以把它理解成:一个人判断可能会偏,很多人从不同角度判断,再投票或取平均,结果通常更稳定。
1. 什么是集成学习
集成学习的核心思想很简单:
训练多个基模型,再把这些模型的预测结果组合成最终结果。
这里的基模型也叫弱学习器(Weak Learner)或基学习器(Base Learner)。它不一定真的很弱,只是相对于最终组合模型来说,每个单独模型只负责贡献一部分判断。
例如一个分类任务中,我们训练了 5 个模型,它们分别预测:
模型 1:通过
模型 2:通过
模型 3:不通过
模型 4:通过
模型 5:通过
如果使用多数投票,最终结果就是“通过”。
如果是回归任务,例如预测房价,多个模型分别预测:
90 万、94 万、92 万、96 万、93 万
最终可以取平均值:
$$
\hat{y}=\frac{90+94+92+96+93}{5}=93
$$
这就是集成学习最朴素的形式。
2. 为什么多个模型会更好
单个模型可能会犯两类典型错误:
- 偏差大:模型太简单,学不到数据规律,容易欠拟合。
- 方差大:模型太复杂,对训练数据很敏感,容易过拟合。
决策树就是一个典型的高方差模型。训练数据稍微变一下,树的结构可能就明显不同。
集成学习通常希望通过组合多个模型来降低错误:
- Bagging 主要降低方差,让模型更稳定。
- Boosting 主要降低偏差,让模型一步步变强。
- Stacking 尝试学习“不同模型应该怎么组合”。
它们都是集成学习,但思路并不一样,下方会对各个思路进行介绍。
3. 投票与平均
最简单的组合方式是投票和平均。
3.1 分类任务:投票
分类任务中,如果每个模型输出一个类别,可以使用多数投票:
$$
\hat{y} = mode(h_1(x), h_2(x), …, h_T(x))
$$
其中:
- $h_t(x)$ 表示第 $t$ 个模型对样本 $x$ 的预测。
- $T$ 表示模型数量。
- $mode$ 表示取出现次数最多的类别。
如果模型能输出概率,也可以先平均概率,再选择概率最大的类别。
例如二分类中,三个模型给出的正类概率分别是:
0.70, 0.62, 0.81
平均概率为:
$$
\frac{0.70+0.62+0.81}{3}=0.71
$$
如果阈值是 0.5,最终预测为正类。
3.2 回归任务:平均
回归任务中,常见做法是直接平均多个模型的预测:
$$
\hat{y}=\frac{1}{T}\sum_{t=1}^{T}h_t(x)
$$
平均可以抵消一部分随机误差。只要多个模型不是以完全相同的方式犯错,组合结果就可能比单个模型更稳。
4. Bagging:并行训练多个模型
Bagging 是 Bootstrap Aggregating 的缩写。它的核心思想是:
从原始数据中有放回地抽样,训练多个模型,再把结果平均或投票。
“有放回抽样”可以这样理解:每次抽一个样本后,再把它放回去,下一次仍然可能抽到它。
假设原始训练集有 1000 个样本,Bagging 会反复抽样,得到多个新的训练集:
训练集 A:抽 1000 次,有些样本会重复,有些样本没被抽到
训练集 B:再抽 1000 次,得到另一份不同的数据
训练集 C:继续抽样
然后分别训练多个模型:
数据 A -> 模型 A
数据 B -> 模型 B
数据 C -> 模型 C
最后把它们的预测组合起来。
Bagging 的关键是让每个模型看到的数据不完全一样,从而产生差异。模型之间越有差异,组合后越有可能降低方差。
4.1 一个实际例子:用 Bagging 预测房价
假设我们要预测一套房子的价格,训练数据中有这些特征:
面积、楼层、房龄、距离地铁距离、所在区域、是否有电梯
如果只训练一棵决策树,它可能刚好学到一些很偶然的规则,比如:
如果区域 = A 且楼层 > 20,则房价很高
但这条规则可能只是训练集里的巧合。换一批数据,这棵树的结构可能就变了。
Bagging 的做法是:
- 从原始房价数据中有放回抽样,生成第 1 份训练集,训练树 1。
- 再有放回抽样,生成第 2 份训练集,训练树 2。
- 重复很多次,得到很多棵树。
- 每棵树都对同一套新房子预测价格。
- 最后把这些价格取平均。
例如 5 棵树的预测结果是:
树 1:92 万
树 2:88 万
树 3:95 万
树 4:90 万
树 5:93 万
Bagging 的最终预测为:
$$
\hat{y}=\frac{92+88+95+90+93}{5}=91.6
$$
这里每棵树都可能有自己的偏差,但平均以后,某一棵树的偶然错误会被其他树抵消。对新手来说,可以先把 Bagging 理解成“让很多个不完全一样的模型一起估价,然后取平均”。
4.2 Bootstrap 抽样的数学含义
假设原始训练集为:
$$
D={(x_1,y_1),(x_2,y_2),…,(x_n,y_n)}
$$
Bagging 会从 $D$ 中有放回地抽取 $n$ 次,得到一份新的训练集 $D_t$。因为是有放回抽样,同一个样本可能被抽中多次,也可能一次都没有被抽中。
对于某一个固定样本,它在一次抽样中没有被抽到的概率是:
$$
1-\frac{1}{n}
$$
连续抽 $n$ 次都没有抽到它的概率是:
$$
\left(1-\frac{1}{n}\right)^n
$$
当 $n$ 很大时:
$$
\left(1-\frac{1}{n}\right)^n \approx e^{-1} \approx 0.368 (求极限)
$$
也就是说,一份 Bootstrap 数据集中大约有 $36.8%$ 的原始样本没有出现,约 $63.2%$ 的原始样本至少出现一次。这些没被抽到的样本也叫袋外样本(Out-of-Bag Samples),可以用来估计模型表现。
4.3 Bagging 为什么能降低方差
假设有 $T$ 个基模型,它们的预测分别为:
$$
h_1(x),h_2(x),…,h_T(x)
$$
回归任务中,Bagging 的最终预测是平均值:
$$
H(x)=\frac{1}{T}\sum_{t=1}^{T}h_t(x)
$$
如果每个基模型的方差都是 $\sigma^2$,并且模型之间相互独立,那么平均后的方差为:
$$
Var(H)=\frac{\sigma^2}{T}
$$
这说明模型数量越多,平均结果越稳定。
真实情况中,不同模型不可能完全独立。假设任意两个模型之间的相关系数为 $\rho$,那么平均后的方差可以近似写成:
$$
Var(H)=\rho\sigma^2+\frac{1-\rho}{T}\sigma^2
$$
这个公式很重要(我们的目的是要方差尽量小),它说明了两件事:
- 增加模型数量 $T$ 可以降低后一项方差。
- 降低模型之间的相关性 $\rho$ 也很重要。
这就是为什么随机森林不仅要对样本做随机抽样,还要对特征做随机选择。它的目的就是让树和树之间不要太像。
5. 随机森林:Bagging + 决策树
随机森林(Random Forest)是 Bagging 最经典的应用。
它训练很多棵决策树,但每棵树都有两层随机性:
- 样本随机:每棵树使用 Bootstrap 抽样得到的数据。
- 特征随机:每次节点划分时,只从一部分特征中选择最优划分。
单棵决策树容易过拟合,但很多棵“看过不同数据、关注不同特征”的树组合起来,就会稳定很多。
随机森林做分类时,通常使用投票:
树 1:通过
树 2:不通过
树 3:通过
树 4:通过
树 5:不通过
最终:通过
做回归时,通常使用平均:
树 1:91
树 2:95
树 3:92
树 4:90
最终:92
随机森林的优点是工程上很实用:不太需要特征缩放,对非线性关系友好,稳定性通常比单棵树好很多。
5.1 一个实际例子:随机森林如何判断学生是否通过
假设我们要预测学生是否能通过考试,特征包括:
学习时长、睡眠时长、作业完成率、上课出勤率、历史平均分
单棵决策树可能会先问:
学习时长是否大于 4 小时?
另一棵树可能因为抽到的数据不同、可选特征不同,先问:
作业完成率是否大于 80%?
还有一棵树可能先问:
历史平均分是否大于 70?
对于一个新学生:
学习 3.5 小时
睡眠 7 小时
作业完成率 90%
出勤率 95%
历史平均分 76
不同树可能给出不同判断:
树 1:不通过
树 2:通过
树 3:通过
树 4:通过
树 5:不通过
最终随机森林投票为“通过”。
这里的关键不是每棵树都完美,而是每棵树从不同角度看问题。有的树更重视学习时长,有的树更重视作业完成率,有的树更重视历史成绩。组合起来后,模型不容易被某一个偶然规则带偏。
6. 随机森林的重要参数
使用随机森林时,可以重点关注几个参数。
| 参数 | 含义 | 直观理解 |
|---|---|---|
n_estimators |
树的数量 | 树越多通常越稳定,但训练更慢 |
max_depth |
每棵树最大深度 | 控制单棵树复杂度 |
max_features |
每次划分可用的特征数量 | 增加树之间的差异 |
min_samples_leaf |
叶子节点最少样本数 | 防止叶子节点过小 |
bootstrap |
是否使用有放回抽样 | 随机森林通常为 True |
一般来说,n_estimators 可以适当设大一些,例如 100、300、500;如果过拟合明显,可以限制 max_depth 或增大 min_samples_leaf。
7. Boosting:串行训练多个模型
Bagging 中,多个模型通常是并行训练的,彼此之间没有先后依赖。
Boosting 不一样。它是一种串行思想:
后一个模型重点学习前一个模型没学好的部分。
可以把它想象成做错题本:
- 第一个模型先做一遍题。
- 找出它做错或做得不好的地方。
- 第二个模型重点补这些错误。
- 后面的模型继续修正前面整体模型的不足。
最终模型不是简单地“大家同时投票”,而是一步步累加出来的。
Boosting 的一般形式可以写成:
$$
F_T(x)=\sum_{t=1}^{T}\alpha_t h_t(x)
$$
其中:
- $h_t(x)$ 是第 $t$ 个基模型。
- $\alpha_t$ 是这个模型的权重。
- $F_T(x)$ 是最终组合模型。
Boosting 往往能取得很强的预测效果,但如果参数控制不好,也可能过拟合。
7.1 Boosting 的数学形式
Boosting 不是一次性训练一个复杂模型,而是一步步构造加法模型:
$$
F_0(x), F_1(x), F_2(x), …, F_T(x)
$$
每一轮都在原有模型基础上加一个新的基模型:
$$
F_t(x)=F_{t-1}(x)+\alpha_t h_t(x)
$$
其中:
- $F_{t-1}(x)$ 表示前 $t-1$ 轮已经得到的整体模型。
- $h_t(x)$ 表示第 $t$ 轮新训练出来的基模型。
- $\alpha_t$ 表示第 $t$ 个基模型的权重或步长。
训练目标是让整体损失尽可能小:
$$
\sum_{i=1}^{n}L(y_i,F_T(x_i))
$$
所以 Boosting 的本质可以理解为:每一轮都问自己一个问题:
如果现在的整体模型还不够好,下一步应该加一个什么模型,才能让损失下降?
7.2 一个实际例子:Boosting 如何一步步改错
还是用“是否通过考试”的例子。假设有 6 个学生:
| 学生 | 真实结果 | 第 1 个模型预测 |
|---|---|---|
| A | 通过 | 通过 |
| B | 通过 | 不通过 |
| C | 不通过 | 不通过 |
| D | 通过 | 通过 |
| E | 不通过 | 通过 |
| F | 不通过 | 不通过 |
第 1 个模型错了 B 和 E。
Bagging 会让其他模型独立训练,不会特别盯着 B 和 E。但 Boosting 会说:
B 和 E 是当前模型没学好的样本,下一轮要重点处理它们。
于是第 2 个模型训练时,会更关注 B 和 E。它可能学到:
B 虽然学习时长不长,但作业完成率很高,所以可能通过。
E 虽然学习时长较长,但历史平均分很低,所以可能不通过。
第 2 个模型不是从零替代第 1 个模型,而是补第 1 个模型的不足。最终预测可以理解成:
最终判断 = 第 1 个模型的判断 + 第 2 个模型的修正 + 第 3 个模型的修正 + ...
所以 Boosting 的味道和 Bagging 很不一样。Bagging 像“多个人分别判断后投票”,Boosting 更像“先做一版答案,再一轮轮检查错题并修改”。
如果把这个过程写成公式,就是:
$$
F_2(x)=F_1(x)+\alpha_2h_2(x)
$$
假设对学生 B 来说,第 1 个模型给出的分数是:
$$
F_1(x_B)=-0.4
$$
这里分数小于 0,表示模型倾向于预测“不通过”。但学生 B 的真实结果是“通过”,所以这个预测错了。
第 2 个模型专门补这个错误,给 B 一个正向修正:
$$
h_2(x_B)=1,\quad \alpha_2=0.7
$$
那么更新后的分数为:
$$
F_2(x_B)=F_1(x_B)+\alpha_2h_2(x_B)
=-0.4+0.7\times1=0.3
$$
更新后 $F_2(x_B)>0$,最终就会更倾向于预测“通过”。这就是 Boosting 中“后一个模型修正前一个模型错误”的具体计算方式。
8. AdaBoost:提高错分样本的权重
AdaBoost 是较早的 Boosting 算法。它的核心思想是:上一轮分错的样本,下一轮要更重视。
假设第一轮模型把一些样本分错了,那么这些样本的权重会变大。第二轮训练时,模型会更关注这些“难样本”。
流程可以概括为:
- 初始化所有样本权重相同。
- 训练一个弱分类器。
- 根据分类错误率计算该分类器的权重。
- 提高分错样本的权重,降低分对样本的权重。
- 重复训练多个分类器。
- 最终按分类器权重加权投票。
如果一个分类器表现好,它在最终投票中权重更高;如果表现差,它的权重更低。
8.1 AdaBoost 的权重更新
AdaBoost 会给每个样本分配一个权重。第 $t$ 轮时,第 $i$ 个样本的权重记为:
$$
w_i^{(t)}
$$
第 $t$ 个弱分类器的加权错误率为:
$$
\epsilon_t=\sum_{i=1}^{n}w_i^{(t)}I(h_t(x_i)\neq y_i)
$$
其中 $I(\cdot)$ 是指示函数,如果分类错误就取 1,否则取 0。
分类器权重为:
$$
\alpha_t=\frac{1}{2}\ln\frac{1-\epsilon_t}{\epsilon_t}
$$
可以看到:
- 如果错误率 $\epsilon_t$ 小,说明分类器表现好,$\alpha_t$ 会比较大。
- 如果错误率接近 $0.5$,说明分类器接近乱猜,$\alpha_t$ 会比较小。
样本权重更新公式可以写成:
$$
w_i^{(t+1)}=w_i^{(t)}\exp(-\alpha_t y_i h_t(x_i))
$$
这里通常把标签写成 $y_i\in{-1,1}$,分类器输出也写成 $h_t(x_i)\in{-1,1}$。
如果样本被分对,那么 $y_i h_t(x_i)=1$,权重会乘上 $\exp(-\alpha_t)$,变小。
如果样本被分错,那么 $y_i h_t(x_i)=-1$,权重会乘上 $\exp(\alpha_t)$,变大。
这就是“错题更重要”的数学表达。
8.2 一个实际例子:AdaBoost 如何调整权重
假设我们有 5 个训练样本,任务是判断学生是否通过考试。为了方便理解,一开始每个样本权重都一样:
| 学生 | 真实结果 | 初始权重 |
|---|---|---|
| A | 通过 | 0.2 |
| B | 通过 | 0.2 |
| C | 不通过 | 0.2 |
| D | 通过 | 0.2 |
| E | 不通过 | 0.2 |
第 1 个弱分类器可能只会看一个简单规则:
学习时长 >= 4 小时 -> 通过
否则 -> 不通过
假设它预测错了 B 和 E,那么 AdaBoost 会提高 B、E 的权重,降低 A、C、D 的权重。下一轮训练时,模型会更在意 B、E,因为它们现在“分量更重”。
这件事不是口头说“提高”这么简单,而是可以直接算出来。
第 1 轮中 B 和 E 被分错,其他样本分对。因为每个样本初始权重都是 0.2,所以第 1 个弱分类器的加权错误率为:
$$
\epsilon_1 = w_B + w_E = 0.2 + 0.2 = 0.4
$$
弱分类器的权重为:
$$
\alpha_1=\frac{1}{2}\ln\frac{1-\epsilon_1}{\epsilon_1}
=\frac{1}{2}\ln\frac{1-0.4}{0.4}
=\frac{1}{2}\ln 1.5
\approx 0.203
$$
接下来更新每个样本的权重。AdaBoost 的权重更新公式是:
$$
w_i^{(2)}=w_i^{(1)}\exp(-\alpha_1 y_i h_1(x_i))
$$
如果样本分对,$y_i h_1(x_i)=1$:
$$
w_i^{(2)}=0.2\times \exp(-0.203)\approx0.2\times0.816=0.163
$$
如果样本分错,$y_i h_1(x_i)=-1$:
$$
w_i^{(2)}=0.2\times \exp(0.203)\approx0.2\times1.225=0.245
$$
所以归一化前的权重大致是:
| 学生 | 第 1 轮是否分错 | 下一轮权重变化 |
|---|---|---|
| A | 分对 | 0.163 |
| B | 分错 | 0.245 |
| C | 分对 | 0.163 |
| D | 分对 | 0.163 |
| E | 分错 | 0.245 |
但权重需要重新归一化,让它们加起来等于 1。归一化系数为:
$$
Z=0.163+0.245+0.163+0.163+0.245=0.979
$$
归一化后:
| 学生 | 更新后权重 |
|---|---|
| A | $\frac{0.163}{0.979}\approx0.166$ |
| B | $\frac{0.245}{0.979}\approx0.250$ |
| C | $\frac{0.163}{0.979}\approx0.166$ |
| D | $\frac{0.163}{0.979}\approx0.166$ |
| E | $\frac{0.245}{0.979}\approx0.250$ |
可以看到,B 和 E 的权重从 0.2 增加到了约 0.25;A、C、D 的权重从 0.2 降低到了约 0.166。
于是第 2 个弱分类器会尝试寻找能分对 B、E 的规则。例如它可能不再只看学习时长,而是看“作业完成率”或“历史平均分”。
最终投票时,也不是每个弱分类器一票同权。表现好的分类器权重大,表现差的分类器权重小。也就是说,AdaBoost 同时做了两件事:
- 对样本:分错的样本下一轮更重要。
- 对模型:错误率低的模型最终投票更重要。
这就把“关注错题”和“相信好老师”结合在了一起。
9. GBDT:用新树拟合残差
GBDT(Gradient Boosting Decision Tree)可以理解为:用一棵棵决策树不断修正当前模型的错误。
以回归任务为例,假设真实房价是 100 万,第一棵树预测 80 万,那么残差是:
$$
100 - 80 = 20
$$
第二棵树就不再直接预测房价,而是学习这个残差。假设第二棵树预测残差为 15,那么组合模型变成:
$$
80 + 15 = 95
$$
还差 5,后面的树继续修正。
所以 GBDT 的直觉是:
当前预测不够好 -> 看还差多少 -> 用下一棵树补上
更一般地说,GBDT 每一步拟合的是损失函数的负梯度方向。对平方误差来说,负梯度刚好和残差方向一致,所以可以先把它理解为“拟合残差”。
9.1 从残差到负梯度
GBDT 的目标是最小化整体损失:
$$
\sum_{i=1}^{n}L(y_i,F(x_i))
$$
第 $t$ 轮时,当前模型是 $F_{t-1}(x)$。GBDT 会计算每个样本在当前模型下的负梯度:
$$
r_i^{(t)}=-\left[\frac{\partial L(y_i,F(x_i))}{\partial F(x_i)}\right]{F=F{t-1}}
$$
然后训练一棵新的回归树 $h_t(x)$ 去拟合这些 $r_i^{(t)}$:
输入:x_i
目标:r_i^(t)
也就是说,新树学习的不是原始标签 $y_i$,而是当前模型最应该修正的方向。
如果损失函数是平方误差:
$$
L(y,F(x))=\frac{1}{2}(y-F(x))^2
$$
那么对 $F(x)$ 求导:
$$
\frac{\partial L}{\partial F(x)}=F(x)-y
$$
负梯度为:
$$
-\frac{\partial L}{\partial F(x)}=y-F(x)
$$
这正好就是残差。
所以在平方误差下:
拟合负梯度 = 拟合残差
这也是为什么很多入门解释会说 GBDT 每一轮都在学习上一轮的残差。
9.2 一个实际例子:GBDT 如何一步步预测房价
假设我们要预测 3 套房子的价格:
| 房子 | 真实价格 |
|---|---|
| A | 100 万 |
| B | 160 万 |
| C | 200 万 |
GBDT 一开始可以先用一个很简单的初始预测,比如所有房子都预测为平均价格:
$$
F_0(x)=\frac{100+160+200}{3}=153.3
$$
于是第一轮残差为:
| 房子 | 真实价格 | 当前预测 $F_0$ | 残差 $y-F_0$ |
|---|---|---|---|
| A | 100 | 153.3 | -53.3 |
| B | 160 | 153.3 | 6.7 |
| C | 200 | 153.3 | 46.7 |
第 1 棵树学习的不是房价本身,而是这些残差。它要学的是:
什么样的房子应该在 153.3 万基础上减一点?
什么样的房子应该在 153.3 万基础上加一点?
假设第 1 棵树预测残差为:
| 房子 | 第 1 棵树预测残差 |
|---|---|
| A | -40 |
| B | 10 |
| C | 35 |
如果学习率 $\eta=0.5$,那么新的预测是:
$$
F_1(x)=F_0(x)+0.5h_1(x)
$$
对应结果为:
| 房子 | $F_0$ | $0.5h_1(x)$ | 新预测 $F_1$ |
|---|---|---|---|
| A | 153.3 | -20 | 133.3 |
| B | 153.3 | 5 | 158.3 |
| C | 153.3 | 17.5 | 170.8 |
可以看到,A 的预测从 153.3 往 100 靠近,B 已经比较接近,C 也往 200 靠近。
然后 GBDT 会继续计算新的残差:
A:100 - 133.3 = -33.3
B:160 - 158.3 = 1.7
C:200 - 170.8 = 29.2
也可以写成统一公式:
$$
r_i^{(2)}=y_i-F_1(x_i)
$$
例如房子 A:
$$
r_A^{(2)}=100-133.3=-33.3
$$
房子 C:
$$
r_C^{(2)}=200-170.8=29.2
$$
第 2 棵树继续学习这些新的残差。这样一轮一轮下去,模型预测会逐渐逼近真实价格。
所以 GBDT 的实际运作过程可以记成:
先给一个粗略预测
-> 计算还差多少
-> 训练一棵树去补这个差距
-> 更新预测
-> 再计算新的差距
-> 继续补
这比“GBDT 拟合负梯度”更好入口:先把它理解成“不断补差价”,再回头看负梯度公式,就会顺很多。
10. 学习率:每棵树别迈太大步
GBDT 中有一个非常重要的参数:学习率 learning_rate。
如果第 $t$ 棵树是 $h_t(x)$,GBDT 的更新可以写成:
$$
F_t(x)=F_{t-1}(x)+\eta h_t(x)
$$
其中 $\eta$ 就是学习率。
学习率越小,每棵树对最终模型的影响越小,训练通常更稳,但需要更多树。
常见经验是:
learning_rate小一些,n_estimators大一些,模型更细致但训练更慢。learning_rate太大,模型可能学得太猛,更容易过拟合。
这和梯度下降中的学习率很像:步子太大容易冲过头,步子太小又会走得慢。
11. XGBoost、LightGBM 和 CatBoost
在实际比赛和工业项目中,经常会见到 XGBoost、LightGBM、CatBoost。它们都属于梯度提升树家族,但在工程实现和细节上做了很多优化。
| 算法 | 特点 |
|---|---|
| XGBoost | 正则化、缺失值处理、并行优化较完善,稳定强大 |
| LightGBM | 训练速度快,内存占用低,适合大规模数据 |
| CatBoost | 对类别特征处理友好,减少目标泄漏方面设计更细 |
刚开始学的时候不必急着陷入每个库的实现细节。可以先抓住共同主线:
它们都是一棵树接一棵树地修正前面模型的错误。
理解了 GBDT,再学习这些工程增强版本会轻松很多。
12. Bagging 和 Boosting 的区别
Bagging 和 Boosting 是集成学习中最重要的两条路线。
| 对比项 | Bagging | Boosting |
|---|---|---|
| 训练方式 | 多个模型并行训练 | 多个模型串行训练 |
| 模型关系 | 模型之间相对独立 | 后一个模型依赖前面的结果 |
| 主要目标 | 降低方差 | 降低偏差,也可能降低方差 |
| 典型算法 | 随机森林 | AdaBoost、GBDT、XGBoost |
| 对噪声敏感度 | 相对不敏感 | 可能更敏感 |
简单记忆:
Bagging 像开会投票:大家独立判断,然后汇总。
Boosting 像连续改错:前面哪里错了,后面重点补哪里。
13. Python 实战:随机森林分类
下面用鸢尾花数据集训练一个随机森林分类器。
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
# 加载鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
# 创建随机森林模型
model = RandomForestClassifier(
n_estimators=100,
max_depth=3,
random_state=42,
)
# 训练模型
model.fit(X_train, y_train)
# 在测试集上预测
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred, target_names=iris.target_names))
随机森林还可以查看特征重要性:
for name, importance in zip(iris.feature_names, model.feature_importances_):
print(name, importance)
feature_importances_ 可以帮助我们粗略观察哪些特征对模型更重要。不过它不是因果解释,只能作为模型内部参考。
14. Python 实战:梯度提升树分类
sklearn 中可以使用 GradientBoostingClassifier 体验 GBDT 的基本用法。
from sklearn.datasets import load_iris
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
model = GradientBoostingClassifier(
n_estimators=100,
learning_rate=0.05,
max_depth=3,
random_state=42,
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("准确率:", accuracy_score(y_test, y_pred))
这里的几个参数很关键:
n_estimators:树的数量。learning_rate:每棵树的贡献比例。max_depth:每棵树的深度。
如果模型过拟合,可以尝试降低 max_depth、减小 learning_rate,或者配合交叉验证选择参数。
15. 使用交叉验证调参
集成模型的参数之间经常会相互影响。比如 GBDT 中,learning_rate 和 n_estimators 通常要一起看。
可以使用 GridSearchCV 做一个简单搜索:
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, train_test_split
iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
param_grid = {
"n_estimators": [100, 200],
"max_depth": [2, 3, 5],
"min_samples_leaf": [1, 3],
}
grid_search = GridSearchCV(
estimator=RandomForestClassifier(random_state=42),
param_grid=param_grid,
cv=5,
scoring="accuracy",
)
grid_search.fit(X_train, y_train)
print("最佳参数:", grid_search.best_params_)
print("交叉验证最佳分数:", grid_search.best_score_)
print("测试集分数:", grid_search.score(X_test, y_test))
和前面讲交叉验证时一样,调参应该只在训练集上进行,测试集留到最后评估一次,避免数据泄漏。
16. 集成学习的优缺点
优点
- 预测效果通常比单个模型更强。
- Bagging 能明显提升模型稳定性。
- Boosting 能逐步修正错误,表达能力很强。
- 树模型集成通常不需要对特征做标准化。
- 随机森林、GBDT 类模型对非线性关系处理较好。
缺点
- 可解释性通常不如单个简单模型。
- 训练和预测成本更高。
- 参数更多,调参成本更高。
- Boosting 对噪声和异常值可能更敏感。
- 如果数据泄漏或验证方式不正确,强模型也会给出虚高结果。
17. 实践建议
使用集成学习时,可以按下面的顺序尝试:
- 先用逻辑回归、决策树等简单模型做基线。
- 如果单棵树不稳定,尝试随机森林。
- 如果追求更高性能,尝试 GBDT、XGBoost、LightGBM。
- 使用交叉验证评估模型,不要只看一次划分结果。
- 关注特征质量和数据泄漏,强模型不能弥补错误的数据流程。
对于表格数据,随机森林和梯度提升树通常是非常值得优先尝试的模型。它们不一定永远最好,但经常能给出很强的基线。
18. 总结
集成学习的核心不是某一个具体算法,而是一种思想:
把多个模型组合起来,让整体判断比单个模型更可靠。
Bagging 通过并行训练多个差异化模型来降低方差,随机森林就是最典型的代表。
Boosting 通过串行训练模型来不断修正错误,AdaBoost、GBDT、XGBoost、LightGBM 都属于这条路线。
理解决策树之后,再看集成学习会很自然:随机森林是很多棵树一起投票,GBDT 是一棵树接一棵树地补错误。它们都是在单棵树的基础上,让模型变得更稳定、更强大。
