在这节我们通过一个具体的小例子,来理解 AdaBoostClassifier 是如何在多分类问题中做出预测的,重点是弄清楚:
- 每个弱学习器是怎么打分的
- 最终得分是怎么组合出来的
- 为什么权重大的弱学习器影响更大
1. 预测
1.1 获得模型
假设我们有如下样本:
| 样本编号 | 特征 X | 真实类别 |
|---|---|---|
| 1 | 1.0 | 0 |
| 2 | 1.5 | 0 |
| 3 | 2.0 | 0 |
| 4 | 3.0 | 1 |
| 5 | 3.5 | 1 |
| 6 | 4.0 | 1 |
| 7 | 5.0 | 2 |
| 8 | 5.5 | 2 |
| 9 | 6.0 | 2 |
| 10 | 3.8 | 1 |
我们让 AdaBoost 去学习这个三分类任务(类别 0、1、2)。弱学习器使用深度为 1 的决策树桩,训练 3 个弱学习器,学习率为 0.3。

1.2 样本预测
假设来了一个新样本 [2.5],我们需要预测它的类别。AdaBoost 的预测是基于加权投票的思想:
- 每个弱学习器在预测时,对自己认为正确的类别加分,对其他类别减分
- 最终将所有弱学习器的打分加权求和,分数最高的类别就是预测结果
为什么要对其他类别减分?
在 AdaBoost 中,每个弱学习器为每个类别打分,表示它对该类别的信心。正分数表示当前弱学习器对该类的信任程度,负分数表示弱学习器该类别不信任程度。弱学习器的权重决定了分值,权重越大,打得分越高,反之,打得越低。
为什么打的分数要和模型权重相关联呢?这种设计有其合理性,如果第一个弱学习器的权重大,它对某个类别的否定(即给负分)就会带来更大的惩罚。即使后续的弱学习器试图纠正(通过给出正分),由于它们的权重较小,影响力不足,无法完全改变第一个大权重学习器的判断。
具体来说:对于一个新样本 \(x\),第 \(t\) 个弱学习器的输出记作 \(h_{t}(x)\),它的权重为 \(α_{t}\)。那么该学习器的打分向量为:

我们根据该公式,可以计算出 [[2.5]] 在每个弱学习器对新样本的打分向量:
- 第一个弱学习器的分数向量:
[-0.23106676 0.46213351 -0.23106676] - 第二个弱学习器的分数向量:
[ 0.52950045 -0.26475023 -0.26475023] - 第三个弱学习器的分数向量:
[ 0.47849894 -0.23924947 -0.23924947]
回过头来,我们再看公式中,预测错误时,为什么打的负分是 \( -\alpha_{t} / (K – 1) \) ?
我们可以这么理解,AdaBoost 想要表达得是,弱学习器有多肯定 0 类别,就有多否定非 0 的类别,分数就是表示这种强度的,假设肯定 0 类别的分数是 0.8 时,那么,否定其他类别的分数也是 -0.8,但是其他类别共有 K-1 个类别,所以其他的类别分别承担 -0.4 的否定分数。
接下来,将对应类别的分数加起来,得到 AdaBoost 对各个类别的打分:[ 0.77693263 -0.04186618 -0.73506645],我们可以看到,0 类别分数最大, 所以,[2.5] 最终归类为 0 类别。
from sklearn.ensemble import AdaBoostClassifier
import numpy as np
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
def demo():
# 1. 数据准备
X = [[1.0], [1.5], [2.0], [3.0], [3.5], [4.0], [5.0], [5.5], [6.0], [3.8]]
y = [0, 0, 0, 1, 1, 1, 2, 2, 2, 1]
# 2. 模型训练
# 弱学习器默认使用高度为 1 的决策树
adaboost = AdaBoostClassifier(n_estimators=3, learning_rate=0.3, random_state=42)
adaboost.fit(X, y)
# 弱决策树可视化
fig, axes = plt.subplots(3, 1, figsize=(6, 10))
for i, (estimator, weight, error, ax) in enumerate(zip(adaboost.estimators_, adaboost.estimator_weights_, adaboost.estimator_errors_, axes)):
plot_tree(estimator, fontsize=10, filled=True, impurity=False, proportion=False, ax=ax)
ax.set_title(f"弱学习器 {i + 1} 权重 {round(weight, 3)} 错误率 {round(error, 3)}")
plt.show()
print('弱学习器错误率:', adaboost.estimator_errors_)
# 弱学习器权重 = 学习率 * 弱学习器原本权重
print('弱学习器的权重:', adaboost.estimator_weights_)
# 3. 新样本预测
new_x = [[2.5]]
n_classes = len(adaboost.classes_)
# 3.1 内部计算
y_score = adaboost.decision_function(new_x)
y_label = adaboost.predict(new_x)
print('内部计算:', '各个类别预测分数:', y_score.squeeze(), '最终预测标签', y_label.squeeze())
# 3.2 手动计算
# 累计每个类别预测得分
y_score = np.zeros(n_classes, dtype=np.float64)
for estimator, weight in zip(adaboost.estimators_, adaboost.estimator_weights_):
# 弱分类器预测结果(例如 [1])
stage_label = estimator.predict(new_x)
# 投票规则:
# 对预测到的类别 +weight,
# 对未预测的类别 -weight / (K - 1)
stage_score = np.where(adaboost.classes_ == stage_label[0], weight, -weight / (n_classes - 1))
print(f"预测类别={stage_label[0]}, 权重={weight:.3f}, 投票分数={stage_score}")
y_score += stage_score
print('最终打分:', y_score)
# 归一化(我们在前面的例子中,并没有做这一步,这一步不会影响最终预测结果)
y_score = y_score / adaboost.estimator_weights_.sum()
y_label = adaboost.classes_[np.argmax(y_score)]
print('手动计算:', '各个类别预测分数:', y_score, '最终预测标签', y_label)
if __name__ == '__main__':
demo()
弱学习器错误率: [0.3 0.2550506 0.28867062] 弱学习器的权重: [0.46213351 0.52950045 0.47849894] 内部计算: 各个类别预测分数: [ 0.52847782 -0.02847782 -0.5 ] 最终预测标签 0 预测类别=1, 权重=0.462, 投票分数=[-0.23106676 0.46213351 -0.23106676] 预测类别=0, 权重=0.530, 投票分数=[ 0.52950045 -0.26475023 -0.26475023] 预测类别=0, 权重=0.478, 投票分数=[ 0.47849894 -0.23924947 -0.23924947] 手动计算: 各个类别预测分数: [ 0.52847782 -0.02847782 -0.5 ] 最终预测标签 0
2. 训练
AdaBoost 核心思想是关注错误样本、动态调整权重。每轮训练后,提高被当前弱学习器误分类样本的权重,让后续弱学习器更聚焦于这些难分样本。同时,根据弱学习器的分类精度,为其分配不同权重(精度越高,权重越大),最终通过加权投票得到强学习器。
接下来,以具体案例为核心,拆解完整训练过程,帮助大家理解自适应权重调整的本质。我们采用10 个样本的 3 分类任务,数据如下表所示。特征仅 1 个,类别分为 0、1、2,任务是训练多个弱决策树,实现对样本的分类。
| 样本编号 | 特征 X | 真实类别 |
|---|---|---|
| 1 | 1.0 | 0 |
| 2 | 1.5 | 0 |
| 3 | 2.0 | 0 |
| 4 | 3.0 | 1 |
| 5 | 3.5 | 1 |
| 6 | 4.0 | 1 |
| 7 | 5.0 | 2 |
| 8 | 5.5 | 2 |
| 9 | 6.0 | 2 |
| 10 | 3.8 | 1 |
AdaBoost(SAMME)的训练过程可以总结为以下步骤:
- 初始化:样本权重 = 1/N
- 训练弱决策树(树桩)
- 计算弱学习器错误率
- 计算弱学习器的权重
- 更新下轮样本的权重
- 循环 2-5 步,直到达到停止的条件
2.1 模型起步(初始预测)
训练开始前,因为我们对样本难分程度无先验认知,所以样本的权重均等。样本总数为 10,故每个样本的初始权重为 0.1。
| 样本编号 | 特征 X | 真实标签 | 初始权重 |
|---|---|---|---|
| 1 | 1.0 | 0 | 0.1 |
| 2 | 1.5 | 0 | 0.1 |
| 3 | 2.0 | 0 | 0.1 |
| 4 | 3.0 | 1 | 0.1 |
| 5 | 3.5 | 1 | 0.1 |
| 6 | 4.0 | 1 | 0.1 |
| 7 | 5.0 | 2 | 0.1 |
| 8 | 5.5 | 2 | 0.1 |
| 9 | 6.0 | 2 | 0.1 |
| 10 | 3.8 | 1 | 0.1 |
2.2 首次纠错(更新权重)
我们基于这些带权重的样本训练第一个弱决策树,核心是找一个切分点。在 scikit-learn 1.7.2 中决策树的构建默认使用 gini 分裂增益计算方法。我们以 4.5 为例,将训练集分为两部分:
| 样本编号 | 特征 X | 真实标签 | 初始权重 |
|---|---|---|---|
| 1 | 1.0 | 0 | 0.1 |
| 2 | 1.5 | 0 | 0.1 |
| 3 | 2.0 | 0 | 0.1 |
| 4 | 3.0 | 1 | 0.1 |
| 5 | 3.5 | 1 | 0.1 |
| 6 | 4.0 | 1 | 0.1 |
| 7 | 5.0 | 2 | 0.1 |
| 8 | 5.5 | 2 | 0.1 |
| 9 | 6.0 | 2 | 0.1 |
| 10 | 3.8 | 1 | 0.1 |
接下来计算该分裂的分类增益:

基尼不纯度计算方法:https://mengbaoliang.cn/archives/84678/
我们按照相同的方法,计算所有候选切分点的加权基尼不纯度,选择值最小的点进行此次分裂。

接下来,基于下面的公式计算模型的错误率。错误样本共 3 个,其权重和为 0.1+0.1+0.1=0.3,所以该模型的错误率为:0.3。

| 样本编号 | 特征 X | 真实标签 | 初始权重 | 预测标签 |
|---|---|---|---|---|
| 1 | 1.0 | 0 | 0.1 | 1 – × |
| 2 | 1.5 | 0 | 0.1 | 1 – × |
| 3 | 2.0 | 0 | 0.1 | 1 – × |
| 4 | 3.0 | 1 | 0.1 | 1 – √ |
| 5 | 3.5 | 1 | 0.1 | 1 – √ |
| 6 | 4.0 | 1 | 0.1 | 1 – √ |
| 7 | 5.0 | 2 | 0.1 | 2- √ |
| 8 | 5.5 | 2 | 0.1 | 2- √ |
| 9 | 6.0 | 2 | 0.1 | 2- √ |
| 10 | 3.8 | 1 | 0.1 | 1- √ |
模型的权重根据下面公式计算即可(\( \eta=0.3、K=3、err_{t}=0.3 \)):


最终计算得到第一个弱学习器的权重为:0.4621。注意:学习率直接计算到模型权重上了。
接着,更新样本的权重。如果之前对该样本预测错误,则提高样本权重,否则权重不变。然后重新归一化,得到第二轮迭代需要的样本权重。

计算举例:
- 1号样本预测错误,则其权重为:\( 0.1 \times \exp(0.4621) = 0.15874 \)
- 4号样本预测正确,则其权重为:\( 0.1 \times \exp(0) = 0.1 \)
以此类推,计算所有样本新的权重,然后再归一化,如下图所示:
| 样本编号 | 特征 X | 真实类别 | 预测类别 | 更新权重分数 | 归一化权重 |
|---|---|---|---|---|---|
| 1 | 1.0 | 0 | 1 – × | 0.15875 | 0.13496 |
| 2 | 1.5 | 0 | 1 – × | 0.15875 | 0.13496 |
| 3 | 2.0 | 0 | 1 – × | 0.15875 | 0.13496 |
| 4 | 3.0 | 1 | 1 – √ | 0.1 | 0.08502 |
| 5 | 3.5 | 1 | 1 – √ | 0.1 | 0.08502 |
| 6 | 4.0 | 1 | 1 – √ | 0.1 | 0.08502 |
| 7 | 5.0 | 2 | 2- √ | 0.1 | 0.08502 |
| 8 | 5.5 | 2 | 2- √ | 0.1 | 0.08502 |
| 9 | 6.0 | 2 | 2- √ | 0.1 | 0.08502 |
| 10 | 3.8 | 1 | 1 – √ | 0.1 | 0.08502 |
我们会发现:样本权重的更新依赖模型权重,而学习率已经作用到模型权重中,相当于间接影响了样本权重的计算。较大的学习率,模型的权重较大,误分类样本权重会较大幅度调整,反之,样本的权重调整幅度较小,模型的训练过程也相对保守一些。
2.3 持续优化(多轮迭代)
基于新的样本权重权重训练第二个弱学习器:

计算当前弱学习器的错误率,错误的样本为 7、8、9,其权重和为:\( 0.08502 + 0.08502 + 0.08502 = 0.25506 \)
| 样本编号 | 特征 X | 真实标签 | 归一化权重 | 预测标签 |
|---|---|---|---|---|
| 1 | 1.0 | 0 | 0.13496 | 0 – √ |
| 2 | 1.5 | 0 | 0.13496 | 0 – √ |
| 3 | 2.0 | 0 | 0.13496 | 0 – √ |
| 4 | 3.0 | 1 | 0.08502 | 1 – √ |
| 5 | 3.5 | 1 | 0.08502 | 1 – √ |
| 6 | 4.0 | 1 | 0.08502 | 1 – √ |
| 7 | 5.0 | 2 | 0.08502 | 1 – × |
| 8 | 5.5 | 2 | 0.08502 | 1 – × |
| 9 | 6.0 | 2 | 0.08502 | 1 – × |
| 10 | 3.8 | 1 | 0.08502 | 1 – √ |
接着计算当前弱学习器的权重(0.5295):

最后,更新样本权重(省略计算过程),循环迭代构建多个弱学习器。
2.4 停止条件
当满足以下条件时,停止训练:
- 当构建的弱学习器数量达到指定的数量,停止训练
- 当构建的弱学习器错误率为 0 时,已经能够正确区分所有样本,停止训练
- 当弱学习器的错误率高于随机猜测的错误率 \(1-1/K\) 时,模型的权重是负值,那么样本的权重在更新时,分类错误样本权重会降低,分类正确的样本权重会上升,这使得继续训练会产生更大的错误,所以停止训练。

冀公网安备13050302001966号