资讯详情

资讯详情

AdaBoost算法原理推导与实战:从公式到代码实现

1. 从“三个臭皮匠”说起为什么需要AdaBoost如果你翻过周志华老师的《机器学习》俗称“西瓜书”翻到集成学习那一章大概率会在AdaBoost的公式推导那里卡住。我当年第一次看的时候满脑子都是“这个权重更新公式怎么来的”“为什么这样加权就能降低误差”“那个归一化因子到底在干什么”。书上的推导是严谨的但节奏很快很多中间步骤被压缩了。这篇文章就是把我自己啃这块硬骨头的过程完整还原出来把每一步的“为什么”讲清楚。AdaBoost的全称是Adaptive Boosting自适应增强。名字里的“自适应”三个字是精髓——它根据每一轮弱分类器的表现动态调整样本权重和分类器权重。表现差的样本下一轮会被“重点关照”表现好的分类器在最终投票时话语权更大。这个思路用一句大白话说就是让后面的人专门去补前面的人做错的题。它解决的核心问题是单个弱分类器比如一个深度为1的决策树桩能力有限但把很多个弱分类器按一定规则组合起来就能得到一个强分类器。这背后的理论支撑是Valiant提出的“强可学习”与“弱可学习”等价性——在概率近似正确PAC学习的框架下如果存在一个多项式级算法能学习到仅比随机猜测略好的弱分类器那么就能把它提升为强分类器。AdaBoost就是构造性地证明了这个等价性。这篇文章适合谁看如果你正在学机器学习被AdaBoost的公式卡住了或者你已经会用sklearn调AdaBoostClassifier但说不清楚它内部到底在干什么又或者你在准备面试被问到“AdaBoost为什么不容易过拟合”时只能背结论——那这篇内容就是写给你的。我会从算法直觉、公式推导、代码复现、调参经验、常见坑这几个维度把AdaBoost彻底拆开。2. 算法整体设计与核心思路拆解2.1 集成学习的三大流派与AdaBoost的定位集成学习大致可以分成三条路线。第一条是Bagging代表是随机森林思路是并行地训练多个基学习器每个学习器看不同的数据子集最后投票或取平均。第二条是Stacking思路是训练一个元学习器来组合基学习器的输出。第三条就是Boosting代表就是AdaBoost和梯度提升树GBDT思路是串行地训练基学习器每个新的学习器都在纠正前面所有学习器的错误。AdaBoost属于Boosting流派中最经典的一个。它的“串行”体现在第t个基学习器的训练依赖于第t-1轮结束后的样本权重分布。这个权重分布不是均匀的而是根据前一轮的分类结果调整过的——分错的样本权重变大分对的样本权重变小。这样一来新的基学习器就会被迫去关注那些“难啃”的样本。为什么选择这种串行纠错的策略因为如果并行训练每个基学习器看到的都是原始数据分布它们犯的错误很可能是相似的。而串行纠错让每个新学习器都针对前序模型的薄弱环节理论上最终组合出来的模型在训练集上的误差可以持续下降。2.2 弱分类器的选择为什么决策树桩是标配AdaBoost对弱分类器的要求很低——只要在任意样本权重分布下分类错误率略低于50%即可。这意味着弱分类器不需要很强甚至不需要很稳定。实践中用得最多的是决策树桩decision stump也就是深度为1的决策树只做一次分裂。为什么树桩这么受欢迎第一它足够简单训练速度快每次只需要找一个特征和一个阈值来最小化加权误差。第二它天然支持样本权重——在计算分裂点的加权基尼指数或加权误差时样本权重直接参与计算。第三它的偏差高但方差低多个树桩组合起来能有效降低偏差这正是Boosting擅长的事情。当然弱分类器不限于树桩。理论上任何支持样本权重的分类器都可以用比如带权重的逻辑回归、带权重的SVM。但实践中树桩的性价比最高尤其是在中小规模数据集上。如果数据维度很高、特征之间关系复杂可以考虑用深度为2到3的决策树作为基学习器但要注意控制深度否则基学习器太强反而会让AdaBoost的增益变小。2.3 样本权重与分类器权重的双重更新机制AdaBoost最核心的设计就是两个权重样本权重和分类器权重。这两个权重在每一轮迭代中都会更新而且更新方式紧密相关。样本权重的更新逻辑是如果某个样本被当前基学习器分错了它的权重乘以一个大于1的因子如果分对了乘以一个小于1的因子。这个因子与当前基学习器的分类器权重有关。分类器权重越大说明这个基学习器越靠谱分错样本的权重就被放大得越厉害。分类器权重的更新逻辑是基学习器的加权误差率越低它在最终投票中的话语权越大。具体来说分类器权重是误差率的一个函数误差率越低权重越大当误差率等于0.5时权重为0意味着这个分类器完全没有话语权当误差率低于0.5时权重为正。这两个权重更新机制相互耦合形成了一个自适应的反馈循环。样本权重决定了下一个基学习器关注什么分类器权重决定了最终组合时每个基学习器的贡献。理解了这个耦合关系就理解了AdaBoost的一半。3. 核心公式推导与细节解析3.1 符号定义与问题设定在开始推导之前先把符号约定清楚。给定训练集 ( D {(x_1, y_1), (x_2, y_2), \ldots, (x_m, y_m)} )其中 ( x_i \in \mathbb{R}^n )( y_i \in {-1, 1} )。注意这里标签用的是1和-1而不是0和1这个选择在推导中会带来很多便利。第t轮迭代中样本权重分布记为 ( D_t (w_{t,1}, w_{t,2}, \ldots, w_{t,m}) )满足 ( \sum_{i1}^{m} w_{t,i} 1 )。初始时 ( D_1 (1/m, 1/m, \ldots, 1/m) )即所有样本等权重。第t个基学习器记为 ( h_t: \mathbb{R}^n \rightarrow {-1, 1} )。它的加权误差率定义为[ \epsilon_t P_{x \sim D_t}(h_t(x) \neq y) \sum_{i1}^{m} w_{t,i} \cdot \mathbb{I}(h_t(x_i) \neq y_i) ]这个误差率是加权意义下的不是简单的错误率。权重大的样本分错了对误差率的贡献就大。3.2 分类器权重的推导从指数损失到最小化AdaBoost的推导可以从指数损失函数的角度来理解。定义整个集成分类器为 ( H(x) \text{sign}\left(\sum_{t1}^{T} \alpha_t h_t(x)\right) )其中 ( \alpha_t ) 是第t个基学习器的权重。考虑指数损失函数[ L(H) \mathbb{E}_{x \sim D, y}\left[\exp(-y H(x))\right] ]在AdaBoost的每一轮我们固定前面的 ( \alpha_1, \ldots, \alpha_{t-1} ) 和 ( h_1, \ldots, h_{t-1} )只优化当前的 ( \alpha_t ) 和 ( h_t )。将 ( H_t(x) H_{t-1}(x) \alpha_t h_t(x) ) 代入指数损失[ L \sum_{i1}^{m} \exp(-y_i H_{t-1}(x_i)) \cdot \exp(-y_i \alpha_t h_t(x_i)) ]记 ( w_{t,i} \exp(-y_i H_{t-1}(x_i)) )忽略归一化常数则[ L \sum_{i1}^{m} w_{t,i} \cdot \exp(-y_i \alpha_t h_t(x_i)) ]对于分对的样本( y_i h_t(x_i) 1 )贡献为 ( w_{t,i} e^{-\alpha_t} )对于分错的样本( y_i h_t(x_i) -1 )贡献为 ( w_{t,i} e^{\alpha_t} )。因此[ L e^{-\alpha_t} \sum_{y_i h_t(x_i)} w_{t,i} e^{\alpha_t} \sum_{y_i \neq h_t(x_i)} w_{t,i} ]令 ( \epsilon_t \sum_{y_i \neq h_t(x_i)} w_{t,i} )归一化后的加权误差率则 ( \sum_{y_i h_t(x_i)} w_{t,i} 1 - \epsilon_t )。于是[ L e^{-\alpha_t}(1 - \epsilon_t) e^{\alpha_t} \epsilon_t ]对 ( \alpha_t ) 求导并令导数为零[ \frac{\partial L}{\partial \alpha_t} -e^{-\alpha_t}(1 - \epsilon_t) e^{\alpha_t} \epsilon_t 0 ]整理得[ e^{\alpha_t} \epsilon_t e^{-\alpha_t}(1 - \epsilon_t) ]两边取自然对数[ \alpha_t \ln \epsilon_t -\alpha_t \ln(1 - \epsilon_t) ][ 2\alpha_t \ln\left(\frac{1 - \epsilon_t}{\epsilon_t}\right) ][ \alpha_t \frac{1}{2} \ln\left(\frac{1 - \epsilon_t}{\epsilon_t}\right) ]这就是分类器权重的更新公式。当 ( \epsilon_t 0.5 ) 时( \alpha_t 0 )当 ( \epsilon_t 0.5 ) 时( \alpha_t 0 )当 ( \epsilon_t 0.5 ) 时( \alpha_t 0 )意味着这个基学习器在最终投票中起反作用。实践中如果某个基学习器的误差率大于0.5通常直接丢弃或重新训练。3.3 样本权重更新的推导从指数损失到权重调整样本权重的更新同样可以从指数损失推导出来。在第t轮结束后新的样本权重分布 ( D_{t1} ) 应该反映当前集成模型 ( H_t ) 对每个样本的“关注程度”。根据指数损失的定义[ w_{t1,i} \frac{\exp(-y_i H_t(x_i))}{Z_t} ]其中 ( Z_t ) 是归一化因子确保 ( \sum_{i1}^{m} w_{t1,i} 1 )。将 ( H_t(x_i) H_{t-1}(x_i) \alpha_t h_t(x_i) ) 代入[ w_{t1,i} \frac{\exp(-y_i H_{t-1}(x_i)) \cdot \exp(-y_i \alpha_t h_t(x_i))}{Z_t} ]注意到 ( w_{t,i} \exp(-y_i H_{t-1}(x_i)) / Z_{t-1} )所以[ w_{t1,i} \frac{w_{t,i} \cdot Z_{t-1} \cdot \exp(-y_i \alpha_t h_t(x_i))}{Z_t} ]忽略常数因子 ( Z_{t-1}/Z_t )得到[ w_{t1,i} \propto w_{t,i} \cdot \exp(-y_i \alpha_t h_t(x_i)) ]当 ( y_i h_t(x_i) )分对时( y_i h_t(x_i) 1 )权重乘以 ( e^{-\alpha_t} )当 ( y_i \neq h_t(x_i) )分错时( y_i h_t(x_i) -1 )权重乘以 ( e^{\alpha_t} )。由于 ( \alpha_t 0 )( e^{\alpha_t} 1 )( e^{-\alpha_t} 1 )。所以分错的样本权重被放大分对的样本权重被缩小。这正好符合直觉下一轮要重点关照那些分错的样本。归一化因子 ( Z_t ) 的计算也很重要。它等于[ Z_t \sum_{i1}^{m} w_{t,i} \cdot \exp(-y_i \alpha_t h_t(x_i)) ]将分对和分错的情况分开[ Z_t (1 - \epsilon_t) e^{-\alpha_t} \epsilon_t e^{\alpha_t} ]代入 ( \alpha_t \frac{1}{2} \ln\left(\frac{1 - \epsilon_t}{\epsilon_t}\right) )可以化简得到[ Z_t 2\sqrt{\epsilon_t(1 - \epsilon_t)} ]这个归一化因子在后续的误差上界分析中会用到。3.4 训练误差上界为什么AdaBoost能持续降低误差AdaBoost有一个非常漂亮的训练误差上界定理。令 ( H_T(x) \text{sign}\left(\sum_{t1}^{T} \alpha_t h_t(x)\right) )则训练集上的误差率满足[ \frac{1}{m} \sum_{i1}^{m} \mathbb{I}(H_T(x_i) \neq y_i) \leq \frac{1}{m} \sum_{i1}^{m} \exp(-y_i H_T(x_i)) \prod_{t1}^{T} Z_t ]这个不等式成立的原因是当 ( H_T(x_i) \neq y_i ) 时( -y_i H_T(x_i) \geq 0 )所以 ( \exp(-y_i H_T(x_i)) \geq 1 )当 ( H_T(x_i) y_i ) 时( \exp(-y_i H_T(x_i)) \geq 0 )。因此指示函数不超过指数函数。而 ( \prod_{t1}^{T} Z_t ) 可以进一步放缩。由于 ( Z_t 2\sqrt{\epsilon_t(1 - \epsilon_t)} )令 ( \gamma_t 0.5 - \epsilon_t )即弱分类器比随机猜测好的程度则[ Z_t 2\sqrt{(0.5 - \gamma_t)(0.5 \gamma_t)} 2\sqrt{0.25 - \gamma_t^2} \sqrt{1 - 4\gamma_t^2} ]利用不等式 ( \sqrt{1 - x} \leq e^{-x/2} )对 ( x \geq 0 )得到[ Z_t \leq \exp(-2\gamma_t^2) ]因此[ \prod_{t1}^{T} Z_t \leq \exp\left(-2\sum_{t1}^{T} \gamma_t^2\right) ]如果每个弱分类器都比随机猜测好一点即 ( \gamma_t \geq \gamma 0 )则[ \text{训练误差} \leq \exp(-2T\gamma^2) ]这个上界说明随着迭代次数T的增加训练误差以指数速度下降。这就是AdaBoost能够把弱分类器提升为强分类器的理论保证。注意这个上界是训练误差的上界不是泛化误差的上界。AdaBoost的泛化误差行为更复杂后面会专门讨论。4. 完整实操流程与代码复现4.1 从零实现AdaBoost不调库的版本理解算法最好的方式是自己写一遍。下面是一个基于决策树桩的AdaBoost完整实现只依赖numpy不调用sklearn的集成模块。import numpy as np class DecisionStump: 深度为1的决策树桩支持样本权重 def __init__(self): self.feature_idx None self.threshold None self.polarity 1 # 1表示大于阈值为1-1表示大于阈值为-1 def fit(self, X, y, weights): m, n X.shape best_err float(inf) for j in range(n): feature_values X[:, j] thresholds np.unique(feature_values) for thresh in thresholds: for polarity in [1, -1]: pred np.ones(m) pred[polarity * feature_values polarity * thresh] -1 err np.sum(weights[pred ! y]) if err best_err: best_err err self.feature_idx j self.threshold thresh self.polarity polarity return self def predict(self, X): m X.shape[0] pred np.ones(m) pred[self.polarity * X[:, self.feature_idx] self.polarity * self.threshold] -1 return pred class AdaBoostScratch: 从零实现的AdaBoost def __init__(self, n_estimators50): self.n_estimators n_estimators self.stumps [] self.alphas [] def fit(self, X, y): m X.shape[0] weights np.ones(m) / m self.stumps [] self.alphas [] for t in range(self.n_estimators): stump DecisionStump() stump.fit(X, y, weights) pred stump.predict(X) err np.sum(weights[pred ! y]) err np.clip(err, 1e-10, 1 - 1e-10) alpha 0.5 * np.log((1 - err) / err) weights weights * np.exp(-alpha * y * pred) weights weights / np.sum(weights) self.stumps.append(stump) self.alphas.append(alpha) if err 0: break return self def predict(self, X): m X.shape[0] agg np.zeros(m) for stump, alpha in zip(self.stumps, self.alphas): agg alpha * stump.predict(X) return np.sign(agg)这段代码的核心逻辑和前面的推导完全对应。DecisionStump在训练时遍历所有特征和所有可能的阈值选择加权误差最小的分裂方式。AdaBoostScratch在每一轮计算加权误差率、分类器权重然后更新样本权重并归一化。有一个细节值得注意err np.clip(err, 1e-10, 1 - 1e-10)这一行是为了防止误差率为0或1时对数运算出现无穷大。实际中如果误差率为0说明当前基学习器完美分类可以直接跳出循环。4.2 在西瓜数据集上验证周志华的西瓜书里有一个经典的西瓜数据集包含17个样本2个特征密度和含糖率2个类别好瓜和坏瓜。我用这个数据集来验证上面的实现。# 西瓜数据集3.0alpha简化版 X np.array([ [0.697, 0.460], [0.774, 0.376], [0.634, 0.264], [0.608, 0.318], [0.556, 0.215], [0.403, 0.237], [0.481, 0.149], [0.437, 0.211], [0.666, 0.091], [0.243, 0.267], [0.245, 0.057], [0.343, 0.099], [0.639, 0.161], [0.657, 0.198], [0.360, 0.370], [0.593, 0.042], [0.719, 0.103] ]) y np.array([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]) model AdaBoostScratch(n_estimators10) model.fit(X, y) pred model.predict(X) accuracy np.mean(pred y) print(f训练准确率: {accuracy:.4f}) print(f基学习器数量: {len(model.stumps)}) print(f分类器权重: {[round(a, 3) for a in model.alphas]})运行结果通常会在几轮迭代后达到100%的训练准确率。你可以观察每一轮的加权误差率和分类器权重会发现误差率逐轮下降分类器权重逐轮上升对于表现好的基学习器。4.3 用sklearn对比验证自己实现完之后用sklearn的AdaBoostClassifier做对比可以验证实现的正确性。from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier sk_model AdaBoostClassifier( estimatorDecisionTreeClassifier(max_depth1), n_estimators10, algorithmSAMME, random_state42 ) sk_model.fit(X, y) sk_pred sk_model.predict(X) print(fsklearn训练准确率: {np.mean(sk_pred y):.4f}) print(fsklearn分类器权重: {[round(a, 3) for a in sk_model.estimator_weights_[:10]]})注意sklearn在1.2版本之后base_estimator参数改名为estimatoralgorithm参数推荐用SAMME离散AdaBoost。如果你用的是更早的版本参数名可能不同。对比两者的分类器权重应该非常接近差异主要来自决策树桩分裂点的选择策略sklearn用的是基尼指数我上面的实现用的是加权误差。4.4 关键参数的计算过程实录以西瓜数据集第一轮迭代为例初始权重均为1/17≈0.0588。决策树桩遍历所有特征和阈值找到加权误差最小的分裂点。假设最优分裂点是“含糖率 ≤ 0.126”此时有3个样本被分错样本9、16、17加权误差率 ( \epsilon_1 3/17 \approx 0.1765 )。分类器权重[ \alpha_1 \frac{1}{2} \ln\left(\frac{1 - 0.1765}{0.1765}\right) \frac{1}{2} \ln(4.667) \approx 0.770 ]样本权重更新分错的3个样本权重乘以 ( e^{0.770} \approx 2.16 )分对的14个样本权重乘以 ( e^{-0.770} \approx 0.463 )。然后归一化使得所有权重之和为1。第二轮迭代时那3个分错的样本权重明显变大决策树桩会优先考虑把它们分对。这就是“自适应”的直观体现。5. 常见问题与排查技巧实录5.1 AdaBoost的过拟合问题为什么它不容易过拟合这是一个经典问题。按照直觉迭代次数越多模型越复杂应该越容易过拟合。但AdaBoost在实践中表现出一种“慢过拟合”甚至“不过拟合”的特性。周志华的书里也提到了这一点。原因可以从间隔理论margin theory来解释。AdaBoost的最终分类器不仅关心分类是否正确还关心分类的“置信度”。即使训练误差已经降到0继续迭代仍然可以增大样本的间隔margin从而提高泛化能力。间隔定义为 ( y_i \sum_{t} \alpha_t h_t(x_i) / \sum_{t} \alpha_t )间隔越大模型对噪声越鲁棒。不过要注意这个“不容易过拟合”是有条件的。如果数据噪声很大AdaBoost会不断给噪声样本增加权重最终导致模型被噪声带偏。所以实践中如果发现验证集误差在训练集误差降到0之后开始上升就应该早停。5.2 基学习器误差率超过0.5怎么办理论上如果某个基学习器的加权误差率 ( \epsilon_t 0.5 )它的分类器权重 ( \alpha_t ) 会变成负数在最终投票中起反作用。实践中遇到这种情况通常有三种处理方式第一种是直接丢弃这个基学习器重新训练一个。第二种是把这个基学习器的预测结果取反这样误差率就变成 ( 1 - \epsilon_t 0.5 )。第三种是检查数据或特征是否有问题因为一个正常的弱分类器不应该在加权数据上表现比随机猜测还差。我个人的经验是如果用的是决策树桩误差率超过0.5通常意味着数据中存在标签噪声或者特征与标签的关系非常弱。这时候应该先做数据清洗而不是强行继续训练。5.3 样本权重极端化的处理随着迭代进行分错样本的权重会不断增大分对样本的权重会不断减小。极端情况下少数几个样本的权重可能占据主导地位导致后续的基学习器只关注这几个样本忽略整体分布。这个问题在噪声数据上尤其明显。一个被错误标注的样本会被反复分错权重越来越大最终模型完全被它带偏。处理方法包括限制样本权重的最大值比如不超过0.1或者在权重更新时加入学习率参数类似于梯度提升中的shrinkage。sklearn的AdaBoostClassifier有一个learning_rate参数默认是1.0。调小这个参数比如0.1可以减缓权重更新的速度让模型更平滑但需要增加n_estimators来补偿。5.4 常见问题速查表问题现象可能原因排查思路解决方案训练准确率不上升基学习器太弱或太强检查基学习器的深度和误差率调整基学习器复杂度树桩深度设为1验证集误差早升过拟合或噪声干扰观察训练误差和验证误差的曲线早停减小learning_rate清洗噪声某个基学习器权重为负加权误差率0.5打印每轮的误差率丢弃该基学习器或取反预测样本权重极端化噪声样本或异常值检查权重最大的几个样本限制权重上限或移除异常样本迭代后期性能下降基学习器同质化检查基学习器的多样性增加基学习器随机性或换用其他弱分类器5.5 实操心得调参的顺序和优先级根据我自己的经验AdaBoost的调参优先级是这样的第一先确定基学习器的类型和复杂度。树桩是默认选择如果效果不好再考虑深度为2或3的决策树。基学习器太强会导致Boosting的增益变小太弱会导致需要很多轮才能收敛。第二调整n_estimators。这个参数决定了迭代次数。可以从50开始观察验证集误差曲线找到误差最低点对应的迭代次数。如果验证集误差在很早就开始上升说明过拟合了需要减小n_estimators或增大learning_rate。第三调整learning_rate。这个参数控制每轮权重更新的步长。较小的学习率需要更多的迭代次数但通常能得到更好的泛化性能。常用的组合是learning_rate0.1配合n_estimators500或者learning_rate0.5配合n_estimators100。第四考虑样本权重初始化和更新方式的变体。标准AdaBoost对所有样本等权重初始化但在类别不平衡的场景下可以按类别频率初始化权重。另外有些变体如AdaBoost.M1、AdaBoost.M2、SAMME在多分类问题上的处理方式不同需要根据具体任务选择。提示sklearn的AdaBoostClassifier在多分类时默认使用SAMME算法二分类时SAMME和SAMME.R都可以用。SAMME.R使用概率估计来更新权重通常收敛更快但对基学习器的要求更高需要支持predict_proba。6. 从理论到实践AdaBoost的适用场景与局限性6.1 适合用什么数据、解决什么问题AdaBoost在中小规模数据集上表现很好尤其是特征维度不高、样本量在几百到几万之间的场景。它在文本分类、图像识别、生物信息学等领域都有成功应用。经典的Viola-Jones人脸检测算法就是用AdaBoost结合Haar特征实现的在早期计算资源有限的条件下达到了实时检测的效果。它的优势在于不需要复杂的特征工程基学习器可以很简单训练速度快理论保证清晰。对于二分类问题AdaBoost的框架非常成熟。对于多分类问题SAMME和SAMME.R提供了扩展方案。但它也有明显的局限。第一对噪声和异常值敏感因为分错的样本会被不断加权。第二串行训练的特性使得它难以并行化在大规模数据上训练速度不如随机森林。第三基学习器的选择比较受限需要支持样本权重。6.2 与随机森林、GBDT的对比随机森林是Bagging的代表并行训练多棵决策树每棵树看不同的数据子集和特征子集最后投票。它的优势是抗过拟合能力强训练可以并行对噪声鲁棒。缺点是模型解释性差预测速度相对慢。GBDT是Boosting的另一位代表和AdaBoost的思路类似但每一轮拟合的是前序模型的负梯度残差而不是调整样本权重。GBDT在回归和分类任务上都很强尤其是XGBoost、LightGBM这些工程优化版本在大规模数据上表现优异。AdaBoost和GBDT的核心区别在于AdaBoost通过调整样本权重来纠错GBDT通过拟合残差来纠错。AdaBoost的损失函数是指数损失GBDT可以用任意可微损失函数。实践中GBDT的调参空间更大通常能取得更好的效果但AdaBoost的理论更简洁理解起来更容易。6.3 一个容易忽略的细节基学习器的多样性AdaBoost的最终性能不仅取决于每个基学习器的准确率还取决于它们之间的多样性。如果所有基学习器都犯同样的错误那么组合起来也无法纠正这些错误。样本权重调整机制本身就是在鼓励多样性——每个新基学习器都关注前序模型分错的样本从而在决策边界上形成互补。但如果基学习器太强比如深度很大的决策树它们可能都会完美拟合训练数据导致多样性不足。我试过在AdaBoost中使用深度为5的决策树作为基学习器结果发现最终模型的性能反而不如用树桩。原因就是深度大的树太强了每棵树都几乎完美分类样本权重调整失去了意义基学习器之间高度相关。所以“弱”是AdaBoost对基学习器的核心要求不是随便说说的。6.4 关于AdaBoost泛化误差的一个补充前面推导了训练误差的上界但泛化误差的上界更复杂。Schapire等人给出了基于间隔的泛化误差上界[ P(\text{测试误差}) \leq \hat{P}(\text{间隔} \leq \theta) O\left(\sqrt{\frac{d}{m\theta^2}}\right) ]其中 ( \hat{P}(\text{间隔} \leq \theta) ) 是训练集中间隔小于 ( \theta ) 的样本比例d是基学习器的复杂度m是样本数。这个上界说明即使训练误差降到0继续增大间隔仍然可以降低泛化误差的上界。这解释了为什么AdaBoost在训练误差为0后继续迭代测试误差可能仍然下降。但这个上界也说明当间隔分布无法继续改善时继续迭代会导致过拟合。所以实践中早停仍然是必要的。7. 写在最后我踩过的几个坑第一个坑是直接用sklearn的默认参数跑AdaBoost结果发现效果不如随机森林就以为AdaBoost不行。后来才发现是基学习器用了默认的深度为1的决策树但n_estimators只有50learning_rate是1.0参数组合不对。把n_estimators加到200、learning_rate降到0.1之后效果明显提升。第二个坑是在一个类别极度不平衡的数据集上直接用AdaBoost少数类样本被反复分错权重越来越大最终模型完全偏向少数类多数类的性能崩了。后来在初始化样本权重时按类别频率做了调整才解决了这个问题。第三个坑是忽略了基学习器的多样性。有一段时间我尝试用深度为3的决策树作为基学习器觉得“强一点应该更好”结果发现基学习器之间高度相关Boosting的增益很小。换回树桩之后虽然单个基学习器弱了但组合起来的效果反而更好。AdaBoost的公式推导看起来复杂但核心思想其实很朴素让后面的人专门补前面的人做错的题。理解了这一点再看那些公式就不会觉得是在看天书了。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →