前言
AdaBoost
算法是基于Boosting
思想的一种集成学习算法,是Boosting
家族中最著名的算法之一。本文将介绍AdaBoost
算法的原理,从二分类问题到多分类问题,再到回归问题,并且介绍其sklearn
代码实现。
AdaBoost算法流程
从Boosting开始
AdaBoost
算法是基于Boosting
思想的,所以我们先来回顾一下Boosting
思想的框架,如下图

接下来,我们要在这个框架上填上具体内容,而上述框架缺失的有弱学习器,更新权重,结合策略,需要注意的是AdaBoost
算法与Boosting
一样,并不是一种具体的算法,而是一种思想,也就是说,我们这里并不会将所有缺失的内容填满,而是留下弱学习器不填。可以理解为,AdaBoost
算法是Boosting
的一个子集,其确定了更新权重的方法与结合策略,但是没有确定弱学习器。
二分类问题
与一些机器学习算法一样,AdaBoost
算法也是从二分类问题开始发展的。由于更新权重的策略并不是一个简单的问题,因此我们这里先给出整个AdaBoost
算法的流程,后续再追根溯源。
假设我们的训练样本为
T={(x,y1),(x2,y2),…,(xm,ym)}
其中yi∈{−1,1},也就是二分类变量。
在AdaBoost
算法中,每个样本是有权重的,并且权重在训练过程中逐轮更新,因此我们定义第k轮的样本权重为
D(k)=(wk1,wk2,…,wkm)i=1,2...m
权重之和为1,并且,初始权重为m1,也就是
D(1)=(w11,w12,…,w1m)=(m1,m1,…m1)
现在我们临时插入结合策略的方案,AdaBoost
采用加权平均法,记第k轮训练出的弱学习器为Gk(x),则最终的强学习器为
f(x)=sign(k=1∑KαkGk(x))
注:sign(x)为符号函数,也可以写为sgn(x),其定义为
sign(x)=⎩⎪⎪⎨⎪⎪⎧10−1x>0x=0x<0
可以看出,如果出现x=0,则会出现错误,虽然一般不会出现该情况,但是保险起见,我们可以修改定义为
sign(x)={1−1x≥0x<0
而AdaBoost
算法的思路就是通过每一次训练的弱学习器的误差,确定该弱学习器的权重αk,并且更新下一次训练所用的样本权重D(k+1),这也很好理解,如果一个弱学习器的准确率高,则权重自然要高,反之亦然,而一个样本被本轮弱学习器误判,则这个样本在下一轮训练中应该得到重视,因此该样本的权重会提高,反之亦然。
最后,我们来看AdaBoost
是如何具体实现学习器权重的计算与样本权重的更新的,首先是误差的定义,分类问题的误差就是判断的正确与否,一般的误差率计算公式为
m1i=1∑mI(Gk(xi)=yi)
但是这里我们需要将样本权重也考虑进去,也就是第k个弱学习器的误差率为
ek=i=1∑mwkiI(Gk(xi)=yi)
这才能起到权重高的样本被更多关注的目的。
注:I(x)为示函数,其定义为
I(Gk(xi)=yi)={10Gk(xi)=yiGk(xi)=yi
接着,我们可以计算第k个弱学习器的权重为
αk=21lnek1−ek
而样本权重的更新公式为
wk+1,i=ZKwkie−αkyiGk(xi)
其中Zk为规范化因子,其值为
Zk=i=1∑mwkie−αkyiGk(xi)
其为作用为保证样本权重之和为1。
注:这里的弱学习器权重与样本权重的更新给出的很突兀,后文会给出其详细推导的
现在我们来总结一下AdaBoost
算法处理二分类问题的步骤
输入:训练数据集T,弱学习器,训练次数
输出:强学习器
(1) 初始化训练集权重D(1)=(w11,w12,…,w1m)=(m1,m1,…m1)
(2) 对于第k轮训练,使用带权重D(k)的训练集进行训练,得到弱学习器Gk(x)
(3) 计算Gk(x)的误差率ek=∑i=1mwkiI(Gk(xi)=yi)
(4) 计算Gk(x)的权重αk=21lnek1−ek
(5) 更新权重为wk+1,i=ZKwkie−αkyiGk(xi),其中Zk=∑i=1mwkie−αkyiGk(xi)
(6) 当完成全部训练后,得到强学习器f(x)=sign(∑k=1KαkGk(x))
AdaBoost算法的解释
向前分布算法
现在我们来推导学习器权重与样本权重更新公式。首先,我们要简单介绍一下向前分布算法,对于加法模型
f(x)=m=1∑Mβmb(x;γm)
其中,b(x;γm)为基函数,γm为基函数的参数,βm为基函数的系数。
对于该加法模型,给定损失函数L(y,f(x)),求解以下优化问题
βm,γmmini=1∑NL(yi,m=1∑Mβmb(xi;γm))
显然,这是一个复杂的问题。但是注意到,我们可以对其做如下分解
f(x)=fm−1(x)+βMb(x;γM)
同理,fm−1(x)也可以做该分解。对此,我们可以想到,从β1b(x;γ1)开始,从前向后逐步学习每一组参数,也就是每一步只需要求解优化问题
β,γmini=1∑NL(yi,βb(xi;γ))
这就是向前分步算法。
AdaBoost算法的推导
可以注意到,AdaBoost
算法所训练出的模型也是一个加法模型
f(x)=k=1∑KαkGk(x)
定义其损失函数为指数损失函数,即
L(y,f(x))=e−yf(x)
注:损失函数就是用于衡量预测值f(x)与真实值y之间误差的函数,例如回归问题中最常用的L(y,f(x))=(y−f(x))2,而指数损失函数为分类问题中最常用的损失函数,可以注意到,当分类正确时,其值为e−1,当分类错误时,其值为e1,分类错误时的损失函数值大。
在第k轮训练中会得到的学习器为
fk(x)=fk−1(x)+αkGk(x)
我们需要求解αk与Gk(x),其优化问题为
argminL=argαk,Gkmini=1∑Ne−yi(fk−1(xi)+αkGk(xi))=argαk,Gkmini=1∑Ne−yifk−1(xi)e−yiαkGk(xi)
记wki=e−yifk−1(xi),上述优化问题可写为
argαk,Gkmini=1∑Nwkie−yiαkGk(xi)
可以注意到,wki其实就是样本权重,其计算公式与本次训练需要求解的αk与Gk(x)无关,而只与上一次训练得到的学习器,也就是fk−1有关。通过简单的证明可以得到其与上一小节所给的样本权重更新公式是相同的,证明如下
wki=e−yifk−1(xi)=e−yi(fk−2(xi)+αk−1Gk−1(xi))=e−yifk−2(xi)e−yiαk−1Gk−1(xi)=wk−1,ie−yiαk−1Gk−1(xi)
只需要再除以规范化因子,就是上述我们给出的样本权重更新公式了。现在我们已经推导出样本权重的计算公式了,接着往下看。
注:上述权重更新公式也可以写为
wk,i={Zmwmie−αm,Zmwmieαm,Gm(xi)=yiGm(xi)=yi
并且,由于
−yiGk(xi)={−11yi=G(xi)yi=G(xi)
因此,可以得出−yiGk(xi)=2I(yi=G(xi))−1,则样本权重更新公式可改写为
wki=wk−1,ie−yiαk−1Gk−1(xi)=wk−1,ieαk−1(2I(yi=G(xi))−1)=wk−1,ie2αk−1I(yi=G(xi))e−αk−1
由于e−αk−1是常数,因此可以忽略(这在后面很多推导中都会用到),故wki=wk−1,ie2αk−1I(yi=G(xi)),这是AdaBoost.M1
算法所使用的样本权重更新公式。
求解上述优化问题可以分为两步,首先是求解Gk,无论αk取何值,为了使得目标函数最小,Gk的目标都应该是误差率最小,也就是说
Gk∗(x)=argGkmini=1∑NwkiI(yi=G(xi))
其中,Gk∗(x)为Gk(x)的最优解。
接着,将Gk∗(x)代入原优化问题,则该问题变为一个单变量优化问题
argαkmini=1∑Nwkie−yiαkGk∗(xi)
对其目标函数做变式
i=1∑Nwkie−yiαkGk∗(xi)=yi=Gk∗(xi)∑wkie−α+yi=Gk∗(xi)∑wkieα=yi=Gk∗(xi)∑wkieα+i=1∑Nwkie−α−yi=Gk∗(xi)∑wkie−α=(eα−e−α)i=1∑NwkiI(yi=G(xi))+e−αi=1∑Nwki
注:∑yi=Gk∗(xi)跟∑i=1NI(yi=G(xi))是一样的,就是写法不同而已。
记上述目标函数为L,接着我们用正常的单变量优化问题的求解方法,对其求导并令其为0,将其对α求导可得
∂α∂L=(eα+e−α)i=1∑NwkiI(yi=G(xi))−e−αi=1∑Nwki
令其为0,可得
(eα+e−α)i=1∑NwkiI(yi=G(xi))−e−αi=1∑Nwki=0⇒(eα+e−α)i=1∑NwkiI(yi=G(xi))=e−αi=1∑Nwki⇒eαi=1∑NwkiI(yi=G(xi))=e−αi=1∑NwkiI(yi=G(xi))⇒e2α=∑i=1NwkiI(yi=G(xi))∑i=1NwkiI(yi=G(xi))⇒2α=ln(∑i=1NwkiI(yi=G(xi))∑i=1NwkiI(yi=G(xi)))
显然,∑i=1NwkiI(yi=G(xi))为误差率ek,因此,学习器权重αk为
αk=21ln(ek1−ek)
至此,我们完成了AdaBoost
算法的完整推导。
多分类问题与回归问题
多分类问题
现在我们来将AdaBoost
算法推广至多分类问题,这里我们介绍两个AdaBoost
的扩展算法,分别为SAMME
算法与SAMME.R
算法。
SAMME算法
多分类变量
首先是对于多分类问题,输出y要如何修改,SAMME
算法的处理方法是,将输出改为一个1×R的向量,其中R为该问题的类别数。我们记
yi=(yi1,yi2,…,yiR)
当样本属于第k类时,yik=1,其他列均为−R−11,也就是说yi的列和为0,例如样本属于第一类,则
yi=(1,−R−11,…,−R−11)
同理,最终得到的强学习器的输出也应该为一个1×R的向量,这依旧可以写为加法模型
f(x)=k=1∑KαkGk(x)
其最终结果为整个向量的最大值对应的列数,也就是
F(x)=rmax(f(r)(x))
权重更新公式推导
接着,我们来进行SAMME
算法的推导,这与上述推导基本相同,首先是损失函数定义为
L(y,f(x))=e−R1Yf(x)
其优化问题可写为
argminL=argαk,Gkmini=1∑Ne−R1yi(fk−1(xi)+αkGk(xi))=argαk,Gkmini=1∑Nwkie−R1yiαkGk(xi)
其中wki=e−R1yifk−1(xi),故这里我们可以推导出样本权重的更新公式为
wki=e−R1yifk−1(xi)=e−R1yi(fk−2(xi)+αk−1Gk−1(xi))=e−R1yifk−2(xi)e−R1yiαk−1Gk−1(xi)=wk−1,ie−R1yiαk−1Gk−1(xi)
而后该优化问题分两步解,同理最优Gk为
Gk∗(x)=argGkmini=1∑NwkiI(yi=G(xi))
代入原优化问题,并对其目标函数进行变式
i=1∑Nwkie−R1yiαkGk∗(xi)=yi=Gk∗(xi)∑wkie−R−11α+yi=Gk∗(xi)∑wkie(R−1)21α=i=1∑Nwkie−R−11α−yi=Gk∗(xi)∑wkie−R−11α+yi=Gk∗(xi)∑wkie(R−1)21α=e−R−11αi=1∑Nwki+(e(R−1)2α−e−R−1α)i=1∑NwkiI(yi=G(xi))
注:这里补充一下yiGk(xi)的值,由于两者均为1×R向量,严格来讲应该写为yiGkT(xi),两者相乘为一个数。当判断正确时,其值为
1+(R−1)(−R−11)2=R−1R
当判断错误是,其值为
−R−12+(R−2)(−R−11)2=−(R−1)2R
因此,yiGk(xi)的值为
yiGk(xi)={R−1R−(R−1)2Ryi=G(xi)yi=G(xi)
记其为L,对α求导可得
∂α∂L=−R−11e−R−11αi=1∑Nwki+((R−1)21e(R−1)2α+R−11e−R−1α)i=1∑NwkiI(yi=G(xi))
令其为0可得
−R−11e−R−11αi=1∑Nwki+((R−1)21e(R−1)2α+R−11e−R−1α)i=1∑NwkiI(yi=G(xi))=0⇒((R−1)21e(R−1)2α+R−11e−R−1α)i=1∑NwkiI(yi=G(xi))=R−11e−R−11αi=1∑Nwki⇒(R−11e(R−1)2Rα+1)∑i=1Nwki∑i=1NwkiI(yi=G(xi))=1⇒R−11e(R−1)2Rα=ek1−ek⇒(R−1)2Rα=ln(ek1−ek)+ln(R−1)⇒α=R(R−1)2(ln(ek1−ek)+ln(R−1))
故解得弱学习器权重为
αk=R(R−1)2[lnek1−ek+ln(R−1)]
权重更新公式的优化
可以注意到,上述推导出的弱学习器权重公式与样本权重更新公式均较复杂,为了降低计算量,我们可以对上述公式进行精简优化。首先是弱学习器权重公式,可以注意到R(R−1)2为常数,因此可以忽略,记为
αk∗=[lnek1−ek+ln(R−1)]
且αk=R(R−1)2αk∗。注意到样本权重更新公式中也有αk,因此需要将其更新为
wk+1,i=wkie−R2(R−1)2yiαk∗Gk(xi)
上文我们提到过AdaBoost.M1
算法将样本权重更新公式改写为示函数的形式,这里我们也可以做类似改动,由于
yiGk(xi)={R−1R−(R−1)2Ryi=G(xi)yi=G(xi)
可以得到
e−R2(R−1)2yiαk∗Gk(xi)={eR1−Rαk∗eR1αk∗yi=G(xi)yi=G(xi)
注意到eR1αk∗=eR1−R+Rαk∗=e(R1−R+1)αk∗=eR1−Rαk∗eαk∗,因此可以写为
e−R2(R−1)2yiαk∗Gk(xi)=eR1−Rαk∗eαk∗I(yi=G(xi))
也就是当yi=G(xi)时,后面的eαk∗I(yi=G(xi))=e0=1,故e−R2(R−1)2yiαk∗Gk(xi)=eR1−Rαk∗,当yi=G(xi)时,eαk∗I(yi=G(xi))=eαk∗,故e−R2(R−1)2yiαk∗Gk(xi)=eR1αk∗,其实就是改个写法,能理解吧。
再注意到,eR1−Rαk∗对于每一个样本i都是一样的,跟规范化因子Zk一样,可以视作一个常数,因此可以忽略,故最终的样本权重更新公式为
wk+1,i=wkieαk∗I(yi=G(xi))
标准化后为
wk+1,i=Zkwkieαk∗I(yi=G(xi))
其中
Zk=i=1∑mwkieαk∗I(yi=G(xi))
至此我们就推导出了SAMME
算法的样本权重更新公式及弱学习器权重公式,其余部分与一般的AdaBoost
算法相同。总结一下就是
输入:训练数据集T,弱学习器,训练次数
输出:强学习器
(1) 初始化训练集权重D(1)=(w11,w12,…,w1m)=(m1,m1,…m1)
(2) 对于第k轮训练,使用带权重D(k)的训练集进行训练,得到弱学习器Gk(x)
(3) 计算Gk(x)的误差率ek=∑i=1mwkiI(Gk(xi)=yi)
(4) 计算Gk(x)的权重αk=(ln(ek1−ek)+ln(R−1))
(5) 更新权重为wk+1,i=Zkwkieαk∗I(yi=G(xi)),其中Zk=∑i=1mwkieαk∗I(yi=G(xi))
(6) 当完成全部训练后,得到强学习器F(x)=maxr(f(r)(x)),其中f(x)=∑k=1KαkGk(x)
SAMME.R算法
接着我们来介绍SAMME.R
算法,这是一种SAMME
算法的改进,其具有更快的收敛速度。SAMME.R
算法将αkGk(x)统一为hk(x),也就是说
fk(x)=fk−1(x)+hk(x)
并且,SAMME.R
算法引入了加权概率估计,用于代替误差率,记为p(x)。就是说,对于每一轮训练出的弱学习器Gk(x),有对应的pk(x),其输出为x被分到每一类的概率。对于一个样本xi,其标签为yi,是一个1×R的向量,R为类别数,这里的yi形式与SAMME
算法相同,而pk(xi),也为一个1×R的向量,其第r列记为pk(r)(xi),代表xi被分到第r类的概率。
注:pk(x)的计算也是带权重的
hk(x)与样本权重更新公式均与pk(x)有关,具体为
hk(r)(x)=(R−1)[lnpk(r)(x)−R1j=1∑Rlnpk(j)(x)]
这里的hk(r)(x)指hk(x)的第r列。
wk+1,i=wkie−RR−1yilnpk(xi)
故SAMME.R
算法的流程为
输入:训练数据集T,弱学习器,训练次数
输出:强学习器
(1) 初始化训练集权重D(1)=(w11,w12,…,w1m)=(m1,m1,…m1)
(2) 对于第k轮训练,使用带权重D(k)的训练集进行训练,得到弱学习器Gk(x)
(3) 计算Gk(x)的加权概率估计pk(x)
(4) 计算$h_k^{ ( r ) }(x) = (R - 1) \left[ \ln p_k^{ ( r ) }(x) - \frac1R \sum_{j = 1}^{R} \ln p_k^{(j)}(x) \right] $
(5) 更新权重为wk+1,i=Zkwkie−RR−1yilnpk(xi),其中Zk=∑i=1mwkie−RR−1yilnpk(xi)
(6) 当完成全部训练后,得到强学习器F(x)=maxr(f(r)(x)),其中f(x)=∑k=1Khk(x)
回归问题
最后是如何使用AdaBoost
处理回归问题,这里我们介绍AdaBoost.R2
算法的流程
输入:训练数据集T,弱学习器,训练次数
输出:强学习器
(1) 初始化训练集权重D(1)=(w11,w12,…,w1m)=(m1,m1,…m1)
(2) 对于第k轮训练,使用带权重D(k)的训练集进行训练,得到弱学习器Gk(x)
(3) 计算每个样本的误差eki=Ek∣yi−Gk(xi)∣,其中Ek=max∣yi−Gk(xi)∣,也可以使用不同的误差计算方法,如eki=Ek2(yi−Gk(xi))2或eki=1−e−Ek∣yi−Gk(xi)∣
(3) 计算Gk(x)的误差率ek=∑i=1meki
(4) 计算Gk(x)的权重αk=ek1−ek
(5) 更新权重为wk+1,i=Zkwkiαk1−ekt,其中Zk=∑i=1mwkiαk1−ekt
(6) 当完成全部训练后,得到强学习器f(x)=Gk∗(x),其中,Gk∗(x)是所有ln(αk1)的中位数值乘以对应序号k∗所对应的学习器
这里需要额外解释一下最后一步,也就是结合策略的部分,这与分类算法是有较大区别的。这里我们所使用的权重其实是ln(αk1),我们选取权重的中位数,这可以避免欠拟合与过拟合,假设其中位数为ln(αh1),对应的弱学习器为Gh(x),则Gk∗(x)=ln(αh1)Gh(x)。
sklearn代码实现
最后我们来介绍一下sklearn
代码实现,运行环境为jupyter notebook
,需要用到的库为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns
from sklearn.ensemble import AdaBoostClassifier from sklearn.ensemble import AdaBoostRegressor from sklearn.tree import DecisionTreeClassifier from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import load_wine from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from scipy import stats
import warnings warnings.filterwarnings("ignore")
|
使用红酒数据集与波士顿房价数据集作为示例数据
1 2 3
| wine = load_wine() boston = load_boston()
|
分类
对于分类问题,使用AdaBoostClassifier
函数,其使用的就是我们上文所提到的SAMME
与SAEEM.R
算法,其参数列表如下
参数 |
说明 |
base_estimator |
弱学习器,需要支持样本权重,常用CART决策树与MLP。默认为DecisionTreeClassifier(max_depth=1) 。 |
n_estimators |
训练次数,也就是弱学习器数量,在任何集成学习算法中都是重要的参数。 |
learning_rate |
学习率,默认为1,较大的学习率可以完成模型的快速收敛,较小的学习率可以使得模型的学习更加的精细。learning_rate 和n_estimators 之间存在权衡关系,一般较小的学习率要配合较大的训练次数。 |
algorithm |
分类问题独有的参数,选择使用的算法,可填**{‘SAMME’, ‘SAMME.R’},默认为’SAMME.R’**。 |
random_state |
随机数种子 |
其中,我们需要补充一下学习率,学习率就是在原有模型的基础上增加一个参数v,模型变为
f(x)=k=1∑KvαkGk(x)
很自然的可以得到,模型的更新公式变为
fk(x)=fk−1(x)+vαkGk(x)
其默认为1,就是我们原本的模型。当其大于1时,会加快学习速度,例如模型原本需要50次训练才能收敛,但是调大v后可能只需要30次,适合用于粗调,验证模型的效果(当然,其实一般不会调大的,因为默认的1已经是一个很大的学习率了)。而当其小于1时,会减慢学习速度,但是由于每一个弱学习器的贡献变小了,这也会使得模型变得跟精细,适合用于精调。
下面我们来看看AdaBoost
的demo,首先划分数据
1 2 3 4 5 6 7
| data = pd.DataFrame(wine.data) data.columns = wine.feature_names target = pd.DataFrame(wine.target) target.columns = ['class']
Xtrain, Xtest, Ytrain, Ytest = train_test_split(data, target, test_size=0.2)
|
然后进行建模
1 2 3 4 5 6 7 8
| clf = AdaBoostClassifier(base_estimator=DecisionTreeClassifier(max_depth=2) , n_estimators=100 , learning_rate=0.5 , algorithm='SAMME.R' , random_state=1008) clf.fit(Xtrain, Ytrain) clf.score(Xtest, Ytest)
|
当然,AdaBoost
也继承了一些基础模型的属性,例如
1 2 3
| clf.feature_importances_ clf.classes_ clf.n_classes_
|
回归
对于回归问题,使用AdaBoostRegressor
函数,其使用的是我们上文讲过的AdaBoost.R2
算法,其参数列表为,其大多数参数同分类问题
参数 |
说明 |
base_estimator |
弱学习器 |
n_estimators |
训练次数 |
learning_rate |
学习率 |
loss |
损失函数,可填**{‘linear’, ‘square’, exponential},默认为’linear’**。分别对应我们上文提到的三种误差计算方式。 |
random_state |
随机数种子 |
下面我们跑一个demo
1 2 3 4 5 6 7
| data = pd.DataFrame(boston.data) data.columns = boston.feature_names target = pd.DataFrame(boston.target) target.columns = ['MEDV']
Xtrain, Xtest, Ytrain, Ytest = train_test_split(data, target, test_size=0.2)
|
1 2 3 4 5 6 7
| clf = AdaBoostRegressor(base_estimator=DecisionTreeRegressor(max_depth=4) , n_estimators=100 , learning_rate=0.5 , random_state=1008) clf.fit(Xtrain, Ytrain) clf.score(Xtest, Ytest)
|
总结
本文介绍了AdaBoost
算法的原理,从最初的二分类问题开始,到解决多分类问题的SAEEM
与SAEEM.R
算法,再到解决回归问题的AdaBoost.R2
算法,并介绍其sklearn
代码实现。