多类AdaBoost决策树#

本示例展示了如何通过提升(boosting)来提高多标签分类问题的预测准确性。它重现了Zhu等人论文中图1所示的类似实验 [1]

AdaBoost(自适应提升)的核心原则是在重复重采样的数据版本上拟合一系列弱学习器(例如决策树)。每个样本都带有一个权重,该权重在每个训练步骤后进行调整,以便将更高的权重分配给错误分类的样本。带替换的重采样过程会考虑分配给每个样本的权重。权重较高的样本在新数据集中被多次选中的机会更大,而权重较低的样本被选中的可能性较小。这确保了算法的后续迭代会关注那些难以分类的样本。

参考文献

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

创建数据集#

分类数据集的构建方式是:取一个十维标准正态分布(\(x\)\(R^{10}\) 中),并定义三个由嵌套的同心十维球体分隔的类别,使得每个类别中的样本数量大致相等(\(\chi^2\) 分布的分位数)。

from sklearn.datasets import make_gaussian_quantiles

X, y = make_gaussian_quantiles(
    n_samples=2_000, n_features=10, n_classes=3, random_state=1
)

我们将数据集分成两部分:70%的样本用于训练,剩余30%用于测试。

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, train_size=0.7, random_state=42
)

训练 AdaBoostClassifier#

我们训练 AdaBoostClassifier。该估计器利用提升(boosting)来提高分类准确性。提升是一种旨在训练弱学习器(即 estimator)的方法,这些学习器从其前身的错误中学习。

在这里,我们将弱学习器定义为 DecisionTreeClassifier 并将最大叶子数设置为8。在实际应用中,应调整此参数。我们将其设置为一个较低的值,以限制示例的运行时间。

AdaBoostClassifier 中内置的 SAMME 算法会利用当前弱学习器的正确或不正确预测来更新用于训练后续弱学习器的样本权重。此外,弱学习器本身的权重是根据其在分类训练示例时的准确性计算的。弱学习器的权重决定了其对最终集成预测的影响。

from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier

weak_learner = DecisionTreeClassifier(max_leaf_nodes=8)
n_estimators = 300

adaboost_clf = AdaBoostClassifier(
    estimator=weak_learner,
    n_estimators=n_estimators,
    random_state=42,
).fit(X_train, y_train)

分析#

AdaBoostClassifier 的收敛性#

为了展示提升在提高准确性方面的有效性,我们评估了提升树的误分类误差,并与两个基准分数进行比较。第一个基准分数是来自单个弱学习器(即 DecisionTreeClassifier)的 misclassification_error,它作为一个参考点。第二个基准分数来自 DummyClassifier,它预测数据集中最常见的类别。

from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score

dummy_clf = DummyClassifier()


def misclassification_error(y_true, y_pred):
    return 1 - accuracy_score(y_true, y_pred)


weak_learners_misclassification_error = misclassification_error(
    y_test, weak_learner.fit(X_train, y_train).predict(X_test)
)

dummy_classifiers_misclassification_error = misclassification_error(
    y_test, dummy_clf.fit(X_train, y_train).predict(X_test)
)

print(
    "DecisionTreeClassifier's misclassification_error: "
    f"{weak_learners_misclassification_error:.3f}"
)
print(
    "DummyClassifier's misclassification_error: "
    f"{dummy_classifiers_misclassification_error:.3f}"
)
DecisionTreeClassifier's misclassification_error: 0.475
DummyClassifier's misclassification_error: 0.692

在训练 DecisionTreeClassifier 模型后,所达到的误差超出了通过猜测最频繁类别标签所能获得的预期值,正如 DummyClassifier 所做的那样。

现在,我们计算加性模型(DecisionTreeClassifier)在测试集上每个提升迭代的 misclassification_error,即 1 - accuracy,以评估其性能。

我们使用 staged_predict,它执行与拟合估计器数量(即对应于 n_estimators)相同的迭代次数。在迭代 n 时,AdaBoost的预测只使用前 n 个弱学习器。我们将这些预测与真实预测 y_test 进行比较,从而得出是否将新的弱学习器添加到链中的好处(或没有好处)。

我们绘制了不同阶段的误分类误差

import matplotlib.pyplot as plt
import pandas as pd

boosting_errors = pd.DataFrame(
    {
        "Number of trees": range(1, n_estimators + 1),
        "AdaBoost": [
            misclassification_error(y_test, y_pred)
            for y_pred in adaboost_clf.staged_predict(X_test)
        ],
    }
).set_index("Number of trees")
ax = boosting_errors.plot()
ax.set_ylabel("Misclassification error on test set")
ax.set_title("Convergence of AdaBoost algorithm")

plt.plot(
    [boosting_errors.index.min(), boosting_errors.index.max()],
    [weak_learners_misclassification_error, weak_learners_misclassification_error],
    color="tab:orange",
    linestyle="dashed",
)
plt.plot(
    [boosting_errors.index.min(), boosting_errors.index.max()],
    [
        dummy_classifiers_misclassification_error,
        dummy_classifiers_misclassification_error,
    ],
    color="c",
    linestyle="dotted",
)
plt.legend(["AdaBoost", "DecisionTreeClassifier", "DummyClassifier"], loc=1)
plt.show()
Convergence of AdaBoost algorithm

该图显示了每次提升迭代后测试集上的误分类误差。我们看到,提升树的误差在50次迭代后收敛到0.3左右,这表明与单个树相比,准确性显著更高,如图中虚线所示。

由于 SAMME 算法使用弱学习器的离散输出训练提升模型,因此误分类误差会抖动。

AdaBoostClassifier 的收敛性主要受学习率(即 learning_rate)、所使用的弱学习器数量(n_estimators)以及弱学习器的表达能力(例如 max_leaf_nodes)的影响。

弱学习器的误差和权重#

如前所述,AdaBoost是一个前向分步加性模型。我们现在关注弱学习器的权重与其统计性能之间的关系。

我们使用拟合的 AdaBoostClassifier 的属性 estimator_errors_estimator_weights_ 来研究这种联系。

weak_learners_info = pd.DataFrame(
    {
        "Number of trees": range(1, n_estimators + 1),
        "Errors": adaboost_clf.estimator_errors_,
        "Weights": adaboost_clf.estimator_weights_,
    }
).set_index("Number of trees")

axs = weak_learners_info.plot(
    subplots=True, layout=(1, 2), figsize=(10, 4), legend=False, color="tab:blue"
)
axs[0, 0].set_ylabel("Train error")
axs[0, 0].set_title("Weak learner's training error")
axs[0, 1].set_ylabel("Weight")
axs[0, 1].set_title("Weak learner's weight")
fig = axs[0, 0].get_figure()
fig.suptitle("Weak learner's errors and weights for the AdaBoostClassifier")
fig.tight_layout()
Weak learner's errors and weights for the AdaBoostClassifier, Weak learner's training error, Weak learner's weight

左图显示了在每次提升迭代中,每个弱学习器在重新加权的训练集上的加权误差。右图显示了与每个弱学习器相关的权重,这些权重随后用于最终加性模型的预测。

我们看到弱学习器的误差与权重呈反比。这意味着我们的加性模型将通过增加其对最终决策的影响,更信任那些(在训练集上)犯错较小的弱学习器。事实上,这正是AdaBoost中每次迭代后更新基本估计器权重的方式。

数学细节#

在阶段 \(m\) 训练的弱学习器所关联的权重与其误分类误差成反比,关系如下:

\[\alpha^{(m)} = \log \frac{1 - err^{(m)}}{err^{(m)}} + \log (K - 1),\]

其中 \(\alpha^{(m)}\)\(err^{(m)}\) 分别是第 \(m\) 个弱学习器的权重和误差,而 \(K\) 是我们分类问题中的类别数量。

另一个有趣的观察是,模型中靠前的弱学习器比提升链中靠后的弱学习器犯的错误更少。

这种观察背后的直觉是:由于样本重新加权,后期的分类器被迫尝试对更困难或噪声样本进行分类,并忽略已经分类良好的样本。因此,训练集上的总体误差会增加。这就是为什么弱学习器的权重旨在平衡表现较差的弱学习器。

脚本总运行时间: (0 分 4.388 秒)

相关示例

带AdaBoost的决策树回归

带AdaBoost的决策树回归

在鸢尾花数据集上绘制树集成的决策边界

在鸢尾花数据集上绘制树集成的决策边界

梯度提升中的早停

梯度提升中的早停

梯度提升回归

梯度提升回归

由Sphinx-Gallery生成