一、AdaBoost概述

AdaBoost(Adaptive Boosting,自适应提升)是一种迭代的集成学习算法,通过组合多个弱分类器来构建强分类器。其核心思想是关注错误样本,通过不断调整样本权重,让后续的弱分类器更专注于那些之前被分错的样本,逐步改进模型的性能。它已被广泛应用于人脸表情识别、图像检索等领域。

二、AdaBoost基本思想

  1. 样本权重调整:在训练过程中,提高被弱分类器错分样本的权值,降低被正确分类样本的权值。这样一来,那些没有得到正确分类的数据,由于其权值加大而受到后一轮的弱分类器的更大关注,从而实现分类问题被一系列弱分类器“分而治之”。

  2. 弱分类器组合:采取加权多数表决的方法,误差率小的分类器的权值大,使其在表决过程中起较大作用。

三、AdaBoost算法流程

(一)初始化阶段

给定训练集{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)},其中y_i\in{-1,+1}。初始化样本权重:

w_i^{(1)}=\frac{1}{N}(i = 1,2,\cdots,N)

即所有样本等权重。

(二)迭代训练(for t = 1 to T)

  • 训练弱分类器:使用当前权重分布w^{(t)}训练弱分类器h_t(x),计算加权错误率:
    \epsilon_t=\sum_{i = 1}^N w_i^{(t)}\cdot I(h_t(x_i)\neq y_i)
    其中I为指示函数,当h_t(x_i)\neq y_i时,I(h_t(x_i)\neq y_i)=1,否则为I(h_t(x_i)=y_i)=0

  • 计算分类器权重:

    • 确定该弱分类器的权重:\alpha_t=\frac{1}{2}\ln(\frac{1 - \epsilon_t}{\epsilon_t})

    • \epsilon_t<0.5时,\alpha_t>0,且错误率越低,权重越大。

  • 更新样本权重:增加错分样本权重,减少正确分类样本权重,更新公式为:w_i^{(t + 1)}=\frac{w_i^{(t)}\cdot e^{-\alpha_t y_i h_t(x_i)}}{Z_t}

    • 其中Z_t为归一化因子,用于确保\sum_{i = 1}^N w_i^{(t + 1)} = 1,计算公式:

    • 对于错分样本:y_i h_t(x_i)=-1,权重增大

    • 对于正确样本:y_i h_t(x_i)=1,权重减小

(三)最终强分类器

最终的强分类器为

H(x)=\text{sign}(\sum_{t = 1}^T \alpha_t h_t(x))

即根据所有弱分类器的加权和进行符号判断来确定样本的类别。

(四)算法分类器停止迭代的标志

1. 达到预设的最大迭代次数(最常用)

这是工程实现中最普遍的停止方式,对应参数为n_estimators(如 sklearn 的AdaBoostClassifier中默认值为 50)。

  • 逻辑:预先设定弱分类器的总数量T,当迭代轮次t达到T时,停止训练新的弱分类器,权重调整自然结束;

  • 示例:若设sT=100,则算法会训练 100 个弱分类器,计算 100 个\alpha_t(t=1到 100),之后不再调整权重;

  • 原因:简单易实现,且能通过交叉验证(如 GridSearchCV)优化T,平衡 “泛化能力” 与 “过拟合风险”(T过大会导致过拟合,过小则强分类器性能不足)。

2. 训练误差达到预设阈值(理论可行,实际少用)

若预设 “训练误差阈值\text{err}_{max}”,当强分类器的训练误差(对所有训练样本的分类错误率)降至\text{err}_{max}以下时,停止迭代。

  • 逻辑:强分类器的训练误差随迭代次数增加而下降(理论上呈指数级下降),若误差已满足需求(如\text{err}_{max}=0.01),则无需继续训练新弱分类器,权重调整停止;

  • 局限性:实际数据中,训练误差可能在早期快速下降后进入 “平台期”,难以精确降至预设阈值;且过度追求低训练误差易导致过拟合,因此该方式较少单独使用。

3. 弱分类器错误率无法降低(特殊停止条件)

若某一轮迭代中,所有候选弱分类器的最小错误率\epsilon_t \geq 0.5(即弱分类器性能差于随机猜测),则停止迭代。

  • 逻辑:根据\alpha_t的计算公式,若\epsilon_t \geq 0.5,则\frac{1 - \epsilon_t}{\epsilon_t} \leq 1\ln(\cdot) \leq 0,导致\alpha_t \leq 0 负的分类器权重会让该弱分类器在 “强分类器投票” 中起反作用,反而降低整体性能;

  • 实际意义:此时数据可能存在 “线性不可分” 或 “噪声过多” 的问题,继续迭代无法提升性能,因此停止权重调整。

四、弱分类器的选择

在AdaBoost算法的第(t=n)轮(任意迭代轮次)中,弱分类器的选择逻辑与(t=1)轮核心原则一致,但需结合当前轮次的样本权重分布进行调整。具体来说:

(一)t=n轮选择弱分类器的核心原则

在第(t=n)轮,选择弱分类器的标准是:在当前样本权重分布(w_i^{(n)})下,找到“加权错误率最低”的弱分类器。

  • 这里的“加权错误率”不再是简单的“错误样本数/总样本数”,而是错误分类样本的权重之和: s\varepsilon_n = \sum_{i=1}^N w_i^{(n)} \cdot I(h_n(x_i) \neq y_i) 其中I(\cdot)是指示函数(分类错误时为1,正确时为0),w_i^{(n)}是第(n)轮的样本权重。

(二)与t=1轮的区别与联系

轮次

样本权重特点

错误率计算方式

选择逻辑

(t=1)

所有样本权重相等w_i=1/N

错误率 = 错误样本数 / 总样本数

找普通错误率最低的弱分类器

(t=n)((n>1))

样本权重不等(错分样本权重更高)

加权错误率 = 错分样本权重之和

找加权错误率最低的弱分类器

(三)关键逻辑总结

  1. 权重引导学习:第(t=n)轮的样本权重是前(n-1)轮迭代的结果,错分样本的权重被放大,迫使算法优先关注这些“难分样本”。

  2. 动态最优选择:每一轮的最优弱分类器可能不同,因为样本权重分布在变化(例如(t=1)轮关注整体分类,(t=2)轮可能更关注某个错分样本)。

  3. 核心目标不变:始终选择“当前权重分布下加权错误率最低”的弱分类器,确保每一轮迭代都能最大程度降低分类误差。

这种机制使AdaBoost能“自适应”地聚焦于难分样本,逐步提升强分类器的性能。

四、AdaBoost的关键特性

  1. 自适应原理:通过调整样本分布,使后续分类器聚焦于难样本,例如在图像识别中难以分类的特定角度物体。

  2. 权重机制:样本权重反映了样本的分类难度,分类器权重反映了分类器的分类能力,误差率小的分类器权重更大。

  3. 误差上界:训练误差满足\text{erro}\leq\prod_{t = 1}^T Z_t,随着迭代进行,误差指数下降。

五、优缺点分析

维度

优点

缺点

模型特性

简单易实现,参数少

对异常值敏感,依赖弱分类器质量

泛化能力

误差指数下降,不易过拟合(合理迭代)

迭代过多易过拟合,难分样本与异常值混淆

训练效率

无复杂运算,速度快,资源消耗低

弱分类器需顺序迭代,并行化受限

适用场景

二分类、小样本、低维度数据

多分类、回归、高噪声数据表现差

  • 适用场景优先选择 AdaBoost:

    • 数据量小(样本数 < 10 万)、维度低(特征数 < 1000),且标注质量高(无明显异常值);

    • 追求 “快节奏实验”(需快速验证想法,无需复杂调参);

    • 硬件资源有限(无 GPU,需 CPU 高效训练)。

  • 不建议选择 AdaBoost:

    • 数据含大量噪声或标注错误(如医疗数据中的标注偏差);

    • 高维度、大样本数据(如深度学习中的图像像素特征);

    • 多分类 / 回归任务(优先选 XGBoost、LightGBM);

    • 非监督学习任务(如聚类、异常检测)。

六、Python代码案例

import numpy as np

# 定义样本数据和标签
# 蓝色样本(blue)标签设为1,红色样本(red)标签设为-1
blue = np.array([[-27, 38], [5, 34], [32, 3], [42, 10]])
red = np.array([[22, 38], [-5, -33], [-22, -25], [-37, -31]])

# 合并所有样本和标签
X = np.vstack((blue, red))  # 8个样本,每个样本2个特征(u, v)
y = np.hstack((np.ones(4), -np.ones(4)))  # 标签:前4个为1(blue),后4个为-1(red)
n_samples = X.shape[0]  # 总样本数:8

# 初始化样本权重 (t=1)
weights = np.ones(n_samples) / n_samples  # 初始权重均为1/8
print("===== 初始状态 (t=1 前) =====")
print(f"所有样本初始权重: {weights.round(4)}")
print(f"权重总和: {sum(weights):.4f}")  # 验证权重和为1
print()

# 定义函数:找到最优的平行轴弱分类器
def find_best_weak_classifier(X, y, weights):
    best_error = float('inf')
    best_h = None
    best_feature = None
    best_threshold = None
    
    # 尝试所有可能的平行轴分类器(u轴和v轴)
    # 1. 沿u轴分割 (特征0)
    for i in range(X.shape[0]):
        threshold = X[i, 0]  # 以每个样本的u值为阈值
        # 预测:u > threshold 为1,否则为-1
        h = np.where(X[:, 0] > threshold, 1, -1)
        error = np.sum(weights * (h != y))  # 加权错误率
        
        if error < best_error:
            best_error = error
            best_h = h
            best_feature = 0  # 0表示u轴
            best_threshold = threshold
    
    # 2. 沿v轴分割 (特征1)
    for i in range(X.shape[0]):
        threshold = X[i, 1]  # 以每个样本的v值为阈值
        # 预测:v > threshold 为1,否则为-1
        h = np.where(X[:, 1] > threshold, 1, -1)
        error = np.sum(weights * (h != y))  # 加权错误率
        
        if error < best_error:
            best_error = error
            best_h = h
            best_feature = 1  # 1表示v轴
            best_threshold = threshold
    
    return best_h, best_error, best_feature, best_threshold

# 执行2次迭代
for t in range(1, 3):  # t=1和t=2
    print(f"===== 第{t}次迭代 =====")
    
    # 1. 找到当前最优弱分类器
    h, error, feature, threshold = find_best_weak_classifier(X, y, weights)
    feature_name = 'u' if feature == 0 else 'v'
    
    # 2. 计算分类器权重alpha
    alpha = 0.5 * np.log((1 - error) / error) if error != 0 else 100  # 避免除零
    
    # 3. 计算归一化因子Z
    Z = np.sum(weights * np.exp(-alpha * y * h))
    
    # 4. 输出当前迭代信息
    print(f"最优弱分类器: 沿{feature_name}轴,阈值={threshold}")
    print(f"弱分类器预测结果: {h}")
    print(f"加权错误率(ε{t}): {error:.4f}")
    print(f"分类器权重(α{t}): {alpha:.4f}")
    print(f"归一化因子(Z{t}): {Z:.4f}")
    print(f"第{t}次迭代后样本权重: {weights.round(4)}")
    
    # 5. 更新样本权重(为下一次迭代做准备)
    if t < 2:  # 第2次迭代后不需要更新
        weights = (weights * np.exp(-alpha * y * h)) / Z
    
    print()

# 输出最终的强分类器信息
print("===== 最终强分类器 =====")
print("强分类器 = sign(α1*h1 + α2*h2)")

七、sklearn中的AdaBoost

在scikit - learn中,提供了AdaBoostClassifierAdaBoostRegressor分别用于分类和回归任务。

from sklearn.ensemble import AdaBoostClassifier#分类(离散型
from sklearn.ensemble import AdaBoostRegressor#回归(连续型
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report, accuracy_score

# 1. 准备数据
# 蓝色样本(blue)标签设为1,红色样本(red)标签设为0
blue = np.array([[-27, 38], [5, 34], [32, 3], [42, 10]])
red = np.array([[22, 38], [-5, -33], [-22, -25], [-37, -31]])

# 合并样本和标签
X = np.vstack((blue, red))  # 特征矩阵:8个样本,2个特征(u, v)
y = np.hstack((np.ones(4), np.zeros(4)))  # 标签:蓝色为1,红色为0
sample_names = [f"blue{i+1}" for i in range(4)] + [f"red{i+1}" for i in range(4)]

# 2. 创建并训练AdaBoost模型
# 基础弱分类器使用深度为1的决策树(决策树桩)
base_estimator = DecisionTreeClassifier(max_depth=1)

# 创建AdaBoost分类器,迭代次数设为2
adaboost = AdaBoostClassifier(
    estimator=base_estimator,  # 使用estimator参数
    n_estimators=2,  # 迭代次数
    learning_rate=1.0,
    algorithm='SAMME'  # 适合离散型弱分类器
)

# 3. 手动跟踪样本权重变化
print("===== 样本权重变化 =====")
# 初始权重
sample_weights = np.ones(len(X)) / len(X)
print(f"初始样本权重 (t=0):")
for name, weight in zip(sample_names, sample_weights):
    print(f"  {name}: {weight:.4f}")
print(f"  权重总和: {np.sum(sample_weights):.4f}\n")

# 训练并跟踪权重
adaboost.fit(X, y, sample_weight=sample_weights)

# 计算第一次迭代后的权重
estimator1 = adaboost.estimators_[0]
alpha1 = adaboost.estimator_weights_[0]
y_pred1 = estimator1.predict(X)
error1 = np.sum(sample_weights * (y_pred1 != y)) / np.sum(sample_weights)

# 更新权重
sample_weights *= np.exp(-alpha1 * (2 * (y_pred1 == y) - 1))  # 转换为+1/-1的形式计算
sample_weights /= np.sum(sample_weights)  # 归一化

print(f"第一次迭代后样本权重 (t=1):")
for name, weight in zip(sample_names, sample_weights):
    print(f"  {name}: {weight:.4f}")
print(f"  权重总和: {np.sum(sample_weights):.4f}\n")

# 计算第二次迭代后的权重
estimator2 = adaboost.estimators_[1]
alpha2 = adaboost.estimator_weights_[1]
y_pred2 = estimator2.predict(X)
error2 = np.sum(sample_weights * (y_pred2 != y)) / np.sum(sample_weights)

# 更新权重
sample_weights *= np.exp(-alpha2 * (2 * (y_pred2 == y) - 1))
sample_weights /= np.sum(sample_weights)  # 归一化

print(f"第二次迭代后样本权重 (t=2):")
for name, weight in zip(sample_names, sample_weights):
    print(f"  {name}: {weight:.4f}")
print(f"  权重总和: {np.sum(sample_weights):.4f}\n")

# 4. 模型预测与评估
y_pred = adaboost.predict(X)

# 输出评估结果
print("===== 模型评估 =====")
print(f"准确率: {accuracy_score(y, y_pred):.4f}")
print("\n分类报告:")
print(classification_report(y, y_pred, target_names=['红色', '蓝色']))

# 5. 输出弱分类器信息
print("\n===== 弱分类器信息 =====")
for i, estimator in enumerate(adaboost.estimators_):
    # 弱分类器权重
    print(f"\n弱分类器 {i+1} 权重: {adaboost.estimator_weights_[i]:.4f}")
    # 弱分类器的决策阈值(针对决策树桩)
    tree = estimator.tree_
    feature = tree.feature[0]  # 分裂特征
    threshold = tree.threshold[0]  # 分裂阈值
    feature_name = 'u' if feature == 0 else 'v'
    print(f"弱分类器 {i+1} 决策规则: {feature_name} <= {threshold:.4f}")
    # 弱分类器错误率
    error = [error1, error2][i]
    print(f"弱分类器 {i+1} 加权错误率: {error:.4f}")

# 6. 可视化决策边界
def plot_decision_boundary(X, y, model, title):
    h = 0.5  # 网格步长
    x_min, x_max = X[:, 0].min() - 10, X[:, 0].max() + 10
    y_min, y_max = X[:, 1].min() - 10, X[:, 1].max() + 10
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    plt.figure(figsize=(8, 6))
    plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Paired)
    plt.scatter(X[y==1, 0], X[y==1, 1], c='blue', label='蓝色样本', edgecolors='k')
    plt.scatter(X[y==0, 0], X[y==0, 1], c='red', label='红色样本', edgecolors='k')
    plt.xlabel('u')
    plt.ylabel('v')
    plt.title(title)
    plt.legend()
    plt.show()

# 绘制决策边界
plot_decision_boundary(X, y, adaboost, 'AdaBoost分类器决策边界 (2次迭代)')
    

(一)AdaBoostClassifier参数

  1. base_estimator:弱分类学习器,理论上可以选择任何支持样本权重的分类器,默认是CART分类树DecisionTreeClassifier。如果算法选择SAMME.R,则弱分类学习器必须支持概率预测,即除了predict方法外,还需要有predict_proba方法。

  2. n_estimators:弱学习器的最大迭代次数,默认为(50)。一般来说,n_estimators太小容易欠拟合,太大容易过拟合。

  3. learning_rate:每个弱学习器的权重缩减系数,取值范围为(0)到(1),默认为(1.0)。较小的learning_rate意味着需要更多的弱学习器迭代次数,通常需要和n_estimators一起调参。

  4. algorithm:可选参数,默认为SAMME.R。scikit - learn实现了两种Adaboost分类算法,SAMMESAMME.R,两者的主要区别是弱学习器权重的度量,SAMME使用对样本集分类效果作为弱学习器权重,而SAMME.R使用对样本集分类的预测概率大小来作为弱学习器权重,SAMME.R迭代一般比SAMME快。

  5. random_state:整数型,可选参数,默认为None,用于控制随机数生成,以确保结果的可重复性。

(二)AdaBoostRegressor参数

AdaBoostRegressor(base_estimator=None, n_estimators=50, learning_rate=1.0, loss='linear', random_state=None)

AdaBoostRegressor的参数与AdaBoostClassifier类似,其中loss参数是AdaBoostRegressor特有的,有线性'linear'、平方'square'和指数'exponential'三种选择,默认是线性,一般使用线性就足够了,除非怀疑该参数导致拟合程度不好。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐