阅读本文需要的背景知识点:集成学习、拉格朗日乘数法、一丢丢编程知识
一、引言
前面一节我们学习了随机森林算法(Random Forest Algorithm),讲到了其中一种集成学习的方法——Bagging 算法,这一节我们来学习另一种集成学习的方法——提升算法1 (Boosting Algorithm),同时介绍其中比较常见的算法——自适应增强算法2(Adaptive Boosting Algorithm / AdaBoost Algorithm)
二、模型介绍
Boosting 算法
Boosting 算法也是一种集成学习,与 Bagging 算法不同的是,每次训练更关注训练出的估计器中分类错误或者回归误差大的样本,即每次训练都是根据上次训练的结果调整不同的样本权重,直到最后的输出小于预设的阈值。
图2-1
图 2-1 展示了提示算法的具体流程,其与 Bagging 算法的区别在于:其一,Bagging 算法的每个估计器相对独立且权重都相同,而 Boosting 算法的每个估计器都依赖于上一个估计器同时权重也不同。其二,一般情况下 Bagging 算法可以减小方差、而 Boosting 算法则是减小偏差。
Boosting 算法中比较有代表性的算法就是自适应增强算法(Adaptive Boosting Algorithm / AdaBoost Algorithm)
AdaBoost 算法
AdaBoost 算法是由 Yoav Freund 和 Robert E. Schapire 在 1995 年提出的,同时还提出了 AdaBoost.M1、AdaBoost.M2 算法用于多分类问题,AdaBoost.R 算法用于回归问题。后面陆续又有人提出了上述算法的变体 AdaBoost-SAMME、AdaBoost-SAMME.R、AdaBoost.R2 算法。
AdaBoost 算法的基本步骤与 Boosting 算法一样,是 Boosting 算法的具体实现,其定义了每次循环如何更新样本权重以及最后如何将每个估计器结合起来。
由于笔者能力所限,本文只会介绍基础的 AdaBoost 算法和现在 scikit-learn 中所实现的 AdaBoost-SAMME、AdaBoost-SAMME.R、AdaBoost.R2算法,其他的算法暂无法一一介绍,感兴趣的读者可以参考文末对应算法的论文原文。
三、算法步骤
下面先给出每个算法的执行步骤,后面再一一说明这些算法步骤中公式的来源。
二分类
假设训练集 T = { X_i, y_i },i = 1,...,N,y_i 可取-1,+1,h(x) 为估计器,估计器的数量为 K。
AdaBoost 算法步骤如下:
初始化样本权重向量 ω_1
ω1,i=N1i=1,...,N
遍历估计器的数量 K 次:
在样本权重 ω_k 下训练估计器 h(x)
计算第k次的误差率 e_k
ek=i=1∑Nωk,iI(yi=hk(Xi))
如果误差率 e_k 大于 0.5
中断循环
计算第k次的估计器权重 α_k
αk=21lnek1−ek
计算第 k+1 次的权重向量 ω_{k+1}
ωk+1,i=∑j=0N(ωk,je−yjαkhk(Xj))ωk,ie−yiαkhk(Xi)
结束循环
最后的结合策略,采用加权后的结果取 sign 函数,得到最终的强估计器:
H(x)=sign(i=1∑Kαihi(x))
多分类
假设训练集 T = { X_i, y_i },i = 1,...,N,y 的取值有 M 种可能,h(x) 为估计器,估计器的数量为 K。
AdaBoost-SUMME 算法步骤如下:
初始化样本权重向量 ω_1
ω1,i=N1i=1,...,N
遍历估计器的数量 K 次:
在样本权重 ω_k 下训练估计器 h(x)
计算第k次的误差率 e_k
ek=i=1∑Nωk,iI(yi=hk(Xi))
计算第 k 次的估计器权重 α_k
αk=lnek1−ek+ln(M−1)
计算第 k+1 次的权重向量 ω_{k+1}
ωk+1,iˉ=ωk,ieαkI(yi=hk(Xi))
对权重向量 ω_{k+1} 进行归一化
ωk+1,i=∑j=1Nωk+1,iˉωk+1,iˉ
结束循环
最后的结合策略,采用正确分类的结果加权后取值最大的分类,得到最终的强估计器:
H(x)=margmax(i=1∑KαiI(hi(x)=m))
AdaBoost-SUMME.R 算法步骤如下:
初始化样本权重向量 ω_1
ω1,i=N1i=1,...,N
遍历估计器的数量 K 次:
在样本权重 ω_k 下计算加权类概率估计向量 P_k
pkm(x)=P(y=m∣x)
计算第 k+1 次的权重向量 ω_{k+1}
y^={1−M−11y=my=mm=1,…,M
ωk+1,iˉ=ωk,ie−MM−1yi^Tlnpk(x)
对权重向量 ω_{k+1} 进行归一化
ωk+1,i=∑j=1Nωk+1,iˉωk+1,iˉ
结束循环
最后的结合策略,采用概率估计计算结果取值最大的分类,得到最终的强估计器:
hk(x)H(x)=(M−1)(lnpkm(x)−M1i=1∑Mlnpki(x))=margmax(i=1∑Khi(x))
回归
假设训练集 T = { X_i, y_i },i = 1,...,N,h(x) 为估计器,估计器的数量为 K
AdaBoost.R2 算法步骤如下:
初始化样本权重向量 ω_1
ω1,i=N1i=1,...,N
遍历估计器的数量 K 次:
在样本权重 ω_k 下训练估计器 h(x)
计算最大误差 E_k
Ek=max∣yi−hk(Xi)∣
计算第 k 次的误差率 e_k
ek,iek,iek,iek=Ek∣yi−hk(Xi)∣=Ek2(yi−hk(Xi))2=1−e−Ek∣yi−hk(Xi)∣=i=1∑Nωk,iek,i线性误差平方误差指数误差
如果误差率 e_k 大于 0.5
中断循环
计算第 k 次的估计器权重 α_k
αk=1−ekek
计算第 k+1 次的权重向量 ω_{k+1}
ωk+1,iˉ=ωk,iαk1−ek,i
对权重向量 ω_{k+1} 进行归一化
ωk+1,i=∑j=1Nωk+1,iˉωk+1,iˉ
结束循环
最后的结合策略,采用估计器权重的中位数对应的估计器的结果,得到最终的强估计器:
H(x)=inf⎩⎨⎧y∈A:hi(x)≤y∑ln(αi1)≥21i=1∑Kln(αi1)⎭⎬⎫
四、原理证明
AdaBoost 算法推导
同算法步骤中的前提条件一样,假设训练集 T = { X_i, y_i },i = 1,...,N,y_i 可取-1,+1,h(x) 为估计器,估计器的数量为 K。
AdaBoost 算法的一种解释是加法模型,通过多个估计器 h(x) 加权以后得到最后的强估计器 H(x),如下所示:
(1)第 k-1 轮的强估计器表达式
(2)第 k 轮的强估计器表达式
(3)第 k 轮的强估计器可以由第 k-1 轮的强估计器和第 k 轮的加权估计器来表示
Hk−1(x)Hk(x)Hk(x)=i=1∑k−1αihi(x)=i=1∑kαihi(x)=Hk−1(x)+αkhk(x)(1)(2)(3)
式4-1
接下来我们来定义最后强估计器的代价函数,AdaBoost 算法选用的是指数函数,相比于0/1 函数具有更好的数学性质。
(1)指数代价函数
(2)带入式 4-1中的(3)式
(3)我们的目标就是找到最优的估计器权重 α 和估计器 h(x)
(4)定义一个新的变量 ω,包含前一轮的强估计器等与 α 、h(x)无关的值
(5)替换 ω
Cost(H(x))Cost(α,h(x))αk,hk(x)ωk,iˉαk,hk(x)=i=1∑Ne−yiH(Xi)=i=1∑Ne−yi(Hk−1(Xi)+αh(Xi))=α,h(x)argmini=1∑Ne−yi(Hk−1(Xi)+αh(Xi))=e−yiHk−1(Xi)=α,h(x)argmini=1∑Nωk,iˉe−yiαh(Xi)(1)(2)(3)(4)(5)
式4-2
我们先来看下估计器 h(x),在每次训练估计器后,估计器已经确定下来了,所以我们现在只需要关心每个估计器的权重 α 即可。
(1)找到最优的估计器权重 α 使得代价函数的取值最小
(2)代价函数 Cost(α)
(3)由于标签值可取正负 1,根据预测值与标签值是否相同拆为两项
(4)增加第二、三两项,不影响最后的结果
(5)将(4)式中前两项和后两项分别合并得到
αkCost(α)=αargmini=1∑Nωk,iˉe−yiαhk(Xi)=i=1∑Nωk,iˉe−yiαhk(Xi)=yi=hk(Xi)∑Nωk,iˉe−α+yi=hk(Xi)∑Nωk,iˉeα=yi=hk(Xi)∑Nωk,iˉe−α+yi=hk(Xi)∑Nωk,iˉe−α−yi=hk(Xi)∑Nωk,iˉe−α+yi=hk(Xi)∑Nωk,iˉeα=e−αi=1∑Nωk,iˉ+(eα−e−α)i=1∑Nωk,iˉI(yi=hk(Xi))(1)(2)(3)(4)(5)
式4-3
(1)对代价函数求导数并令其为零
(2)定义错误率 e_k 的表达式
(3)将错误率 e_k 带入(2)式
(4)两边同时乘以 exp(α)
(5)移项后整理得
(6)求得最后的估计器权重 α 的表达式
∂α∂Cost(α)ek00e2αα=−e−αi=1∑Nωk,iˉ+(eα+e−α)i=1∑Nωk,iˉI(yi=hk(Xi))=0=∑i=1Nωk,iˉ∑i=1Nωk,iˉI(yi=hk(Xi))=−e−α+(eα+e−α)ek=−1+(e2α+1)ek=ek1−ek=21lnek1−ek(1)(2)(3)(4)(5)(6)
式4-4
(1)错误率 e_k 的定义
(2)定义 ω_k
(3)得到错误率 e_k 的表达式
ekωk,iek=∑i=1Nωk,iˉ∑i=1Nωk,iˉI(yi=hk(Xi))=∑i=1Nωk,iˉωk,iˉ=i=1∑Nωk,iI(yi=hk(Xi))(1)(2)(3)
式4-5
接下来是 ω 的更新方法:
(1) ω_{k+1} 的定义
(2)带入式 4-1中的(3)式
(3)替换为 ω_k
ωk+1,iˉ=e−yiHk(Xi)=e−yi(Hk−1(Xi)+αkhk(Xi))=ωk,iˉe−yiαkhk(Xi)(1)(2)(3)
式4-6
(1)式 4-6中的(3)
(2)两边同时除以归一化参数
(3)分子按照式 4-5中(2)式的定义替换,分母用式 4-7中(1)式替换
(4)分母再按照式 4-5中(2)式的定义替换
(5)由于 ω 的和为一个常数 C
(6)分子分母的常数 C 可以消除,得到 ω 的更新方表达式
ωk+1,iˉωk+1,i=ωk,iˉe−yiαkhk(Xi)=∑j=0Nωk+1,jˉωk,iˉe−yiαkhk(Xi)=∑j=0N(ωk,jˉe−yjαkhk(Xj))ωk,i∑j=0N(ωk,jˉ)e−yiαkhk(Xi)=∑j=0N(ωk,j(∑l=0Nωk,lˉ)e−yjαkhk(Xj))ωk,i∑j=0N(ωk,jˉ)e−yiαkhk(Xi)=∑j=0N(ωk,jCe−yjαkhk(Xj))ωk,iCe−yiαkhk(Xi)=∑j=0N(ωk,je−yjαkhk(Xj))ωk,ie−yiαkhk(Xi)(1)(2)(3)(4)(5)(6)
式4-7
综合式 4-1~式 4-7 可以得到 AdaBoost 算法的表达式:
ekαkωk+1,iH(x)=i=1∑Nωk,iI(yi=hk(Xi))=21lnek1−ek=∑j=0N(ωk,je−yjαkhk(Xj))ωk,ie−yiαkhk(Xi)=sign(i=1∑Kαihi(x))(1)(2)(3)(4)
式4-8
AdaBoost-SAMME 算法推导
同算法步骤中的前提条件一样,假设训练集 T = { X_i, y_i },i = 1,...,N,y 的取值有 M 种可能,h(x) 为估计器,估计器的数量为 K。
为了适应多分类问题,AdaBoost-SAMME 算法将原本为数值的标签 y 转化成一个向量的形式,如式 4-9 所示:
y^={1−M−11y=my=mm=1,…,M
式4-9
下面用一个例子来说明式 4-9 的含义,假设标签 y 可取 1,2,3,标签集 y = { 2,1,2,3 },这时根据式 4-9 可以得到对应的转换后的标签集如式 4-10 所示:
y∈{1,2,3}y={2,1,2,3}y^i={1−21yi=myi=mm=1,2,3y^=⎣⎡−211−21−211−211−21−21−21−211⎦⎤
式4-10
同样将算法解释为加法模型,通过多个估计器 h(x) 加权以后得到最后的强估计器 H(x),代价函数使用指数函数
(1)代价函数,这里比原始算法多了一个 1 / M,是为了后面计算方便,同时 H(X_i) 也是一个向量
(2)带入式 4-1 中的(3)式
(3)同样定义一个 ω,包含前一轮的强估计器等与 α 无关的值
(4)带入 ω 得到代价函数的表达式
(5)目标为找到最优的估计器权重 α 使得代价函数的取值最小
Cost(H(x))Cost(α)ωk,iˉCost(α)αk=i=1∑Ne−M1y^iH(Xi)=i=1∑Ne−M1y^i(Hk−1(Xi)+αhk(Xi))=e−M1y^iHk−1(Xi)=i=1∑Nωk,iˉe−M1y^iαhk(Xi)=αargmini=1∑Nωk,iˉe−M1y^iαhk(Xi)(1)(2)(3)(4)(5)
式4-11
我们先来看下代价函数中指数的部分,即预测值与标签值的点积,下面分两种情况讨论:
当预测值与标签值相同的时候,向量中 1 的位置一致,-1 / (M - 1) 一共有 M - 1 个,得到如下的点积结果:
1+(M−1)(−M−11)(−M−11)=M−1M
式4-12
当预测值与标签值不相同的时候,向量中 1 的位置不一致,-1 / (M - 1) 一共有 M - 2 个,得到如下的点积结果:
(−M−11)+(−M−11)+(M−2)(−M−11)(−M−11)=−(M−1)2M
式4-13
综合上面两种情况,得到如下的结果:
y^ihk(Xi)=⎩⎨⎧M−1M−(M−1)2My^i=hk(Xi)y^i=hk(Xi)
式4-14
(1)代价函数 Cost(α)
(2)分两种情况带入式 4-14
(3)增加第二、三两项,不影响最后的结果
(4)将(3)式中前两项和后两项分别合并得到
Cost(α)=i=1∑Nωk,iˉe−M1y^iαhk(Xi)=y^i=hk(Xi)∑Nωk,iˉe−M−1α+y^i=hk(Xi)∑Nωk,iˉe(M−1)2α=y^i=hk(Xi)∑Nωk,iˉe−M−1α+y^i=hk(Xi)∑Nωk,iˉe−M−1α−y^i=hk(Xi)∑Nωk,iˉe−M−1α+y^i=hk(Xi)∑Nωk,iˉe(M−1)2α=e−M−1αi=1∑Nωk,iˉ+(e(M−1)2α−e−M−1α)i=1∑Nωk,iˉI(y^i=hk(Xi))(1)(2)(3)(4)
式4-15
(1)对代价函数求导数并令其为零
(2)定义错误率 e_k 的表达式
(3)将错误率 e_k 带入(2)式
(4)两边同时乘以 exp(α / (M - 1))
(5)移项后整理得
(6)求得最后的估计器权重 α 的表达式
∂α∂Cost(α)eke−M−1α1ek1−ekα=(−M−11)e−M−1αi=1∑Nωk,iˉ+(((M−1)21)e(M−1)2α+((M−1)1)e−M−1α)i=1∑Nωk,iˉI(yi=hk(Xi))=0=∑i=1Nωk,iˉ∑i=1Nωk,iˉI(yi=hk(Xi))=((M−11)e(M−1)2α+e−M−1α)ek=((M−11)e(M−1)2α+M−1α+1)ek=(M−11)e(M−1)2Mα=M(M−1)2(ln(ek1−ek)+ln(M−1))(1)(2)(3)(4)(5)(6)
式4-16
式 4-16 中估计器权重 α 的表达式前面的常数在进过归一化后对结果没有影响,后面的样本权重更新的公式一样也是简化后的结果。更多详细的算法说明请参考原始论文——Multi-class AdaBoost7
AdaBoost-SAMME.R 算法推导
AdaBoost-SAMME.R 算法是 AdaBoost-SAMME 算法的变体,该算法是使用加权概率估计来更新加法模型,如式 4-17 所示:
Hk(x)=Hk−1(x)+hk(x)
式4-17
代价函数使用的依然是指数函数,不同的是已经没有了估计器权重或者说每一个估计器的权重都为 1,且改成了期望的形式,其中 h(x) 返回的是 M 维的向量,同时为保证求出的 h(x) 唯一,加上了向量的各个元素之和为 0 的限制条件。
hk(x)=h(x)argmaxE(e−M1y^i(Hk−1(x)+h(x))∣x)s.t.hk1(x)+hk2(x)+⋯+hkM(x)=0
式4-18
代价函数可以拆分成对每一类分别求期望后再相加:
Cost(h(x))=E(e−M1y^i(Hk−1(x)+h(x))∣x)=E(e−M1y^iHk−1(x)e−M1y^ih(x)∣x)=E(e−M1y^iHk−1(x)e−M1y^ih(x)I(y=1)∣x)+⋯+E(e−M1y^iHk−1(x)e−M1y^ih(x)I(y=M)∣x)(1)(2)(3)
式4-19
先来看看当 y = 1 时,y * h(x) 的结果:
(1)当 y = 1 时,转换后 y 的向量形式
(2)计算点积的结果
(3)合并最后的项
(4)根据限制条件替换
(5)得到化简后的结果
y^y^ih(x)=[1,−M−11,⋯,−M−11]=h1(x)+(−M−11)h2(x)+⋯+(−M−11)hM(x)=h1(x)−M−1h2(x)+⋯+hM(x)=h1(x)−M−1−h1(x)=M−1Mh1(x)(1)(2)(3)(4)(5)
式4-20
(1)带入式 4-20
(2)提出与期望无关的项
(3)另后面的期望为 P(y = 1 | x)
(4)同理可以得每一类的期望结果
E(e−M1y^iHk−1(x)e−M1y^ih(x)I(y=1)∣x)P(y=1∣x)E(e−M1y^iHk−1(x)e−M1y^ih(x)I(y=m)∣x)=E(e−M1y^iHk−1(x)e−M−1h1(x)I(y=1)∣x)=e−M−1h1(x)E(e−M1y^iHk−1(x)I(y=1)∣x)=E(e−M1y^iHk−1(x)I(y=1)∣x)=e−M−1hm(x)P(y=m∣x)(1)(2)(3)(4)
式4-21
将上面的结果带入代价函数得:
Cost(h(x))=e−M−1h1(x)P(y=1∣x)+⋯+e−M−1hM(x)P(y=M∣x)=m=1∑Me−M−1hm(x)P(y=m∣x)(1)(2)
式4-22
这时可以使用拉格朗日乘数法来求解上述问题,其拉格朗日函数 L 如下:
L(h(x),λ)=m=1∑Me−M−1hm(x)P(y=m∣x)−λm=1∑Mhm(x)
式4-23
拉格朗日函数分别对 h(x) 的各个分量求导数:
∂h1(x)∂L(h(x),λ)∂h2(x)∂L(h(x),λ)∂hM(x)∂L(h(x),λ)=−M−11e−M−1h1(x)P(y=1∣x)−λ=0=−M−11e−M−1h2(x)P(y=2∣x)−λ=0⋯=−M−11e−M−1hM(x)P(y=M∣x)−λ=0
式4-24
两两联立式 4-24 ,分别求出各个分量的结果,下面以第一个为例:
(1)联立导数中的第 1,2 式子
(2)消掉相同的常数项再两边同时取对数
(3)移项化简后得
−M−11e−M−1h1(x)P(y=1∣x)−M−1h1(x)+lnP(y=1∣x)h1(x)−h2(x)=−M−11e−M−1h2(x)P(y=2∣x)=−M−1h2(x)+lnP(y=2∣x)=(M−1)(lnP(y=1∣x)−lnP(y=2∣x))(1)(2)(3)
式4-25
(1)~(3)同理可得
(4)将(1)~(3)式累加起来根据限制条件化简
(5)将最后一项补充完整
(6)得到第一个分量的结果
h1(x)−h2(x)h1(x)−h3(x)h1(x)−hM(x)(M−1)h1(x)−(−h1(x))Mh1(x)h1(x)=(M−1)(lnP(y=1∣x)−lnP(y=2∣x))=(M−1)(lnP(y=1∣x)−lnP(y=3∣x))⋯=(M−1)(lnP(y=1∣x)−lnP(y=M∣x))=(M−1)((M−1)lnP(y=1∣x)−m=1∑lnP(y=m∣x)))=(M−1)(MlnP(y=1∣x)−m=1∑MlnP(y=m∣x))=(M−1)(lnP(y=1∣x)−M1m=1∑MlnP(y=m∣x))(1)(2)(3)(4)(5)(6)
式4-26
同理可得 h(x) 各个分量的结果
hm(x)=(M−1)(lnP(y=m∣x)−M1m′=1∑MlnP(y=m′∣x))
式4-27
样本权重的更新如下,将 h(x) 带入更新方法中,可以看到更新方法只保留了前面一项,因为后面一项为每一类的 p(x) 求和,可以认为是一个常数,归一化以后不影响最后的结果。
ωk,iˉωk+1,iˉ=e−M1y^iHk−1(Xi)=ωk,iˉe−M1y^ihk(Xi)=ωk,iˉe−MM−1y^ilnpk(Xi)(1)(2)(3)
式4-28
这样就得到了算法步骤中的样本权重的更新公式,更多详细的算法说明也请参考原始论文——Multi-class AdaBoost7
五、代码实现
使用 Python 实现 AdaBoost 算法
import numpy as np
from sklearn.tree import DecisionTreeClassifier
class adaboostc():
"""
AdaBoost 分类算法
"""
def __init__(self, n_estimators = 100):
self.n_estimators = n_estimators
def fit(self, X, y):
"""
AdaBoost 分类算法拟合
"""
sample_weights = np.ones(X.shape[0]) / X.shape[0]
estimators = []
weights = []
for i in range(self.n_estimators):
estimator = DecisionTreeClassifier(max_depth = 1)
estimator.fit(X, y, sample_weight=sample_weights)
y_predict = estimator.predict(X)
e = np.sum(sample_weights[y_predict != y])
if e >= 0.5:
self.n_estimators = i
break
weight = 0.5 * np.log((1 - e) / e)
temp_weights = np.multiply(sample_weights, np.exp(- weight * np.multiply(y, y_predict)))
sample_weights = temp_weights / np.sum(temp_weights)
weights.append(weight)
estimators.append(estimator)
self.weights = weights
self.estimators = estimators
def predict(self, X):
"""
AdaBoost 分类算法预测
"""
y = np.zeros(X.shape[0])
for i in range(self.n_estimators):
estimator = self.estimators[i]
weight = self.weights[i]
predicts = estimator.predict(X)
y += weight * predicts
return np.sign(y)
使用 Python 实现 AdaBoost-SAMME 算法
import numpy as np
from sklearn.tree import DecisionTreeClassifier
class adaboostmc():
"""
AdaBoost 多分类SAMME算法
"""
def __init__(self, n_estimators = 100):
self.n_estimators = n_estimators
def fit(self, X, y):
"""
AdaBoost 多分类SAMME算法拟合
"""
self.classes = np.unique(y)
self.n_classes = len(self.classes)
sample_weights = np.ones(X.shape[0]) / X.shape[0]
estimators = []
weights = []
for i in range(self.n_estimators):
estimator = DecisionTreeClassifier(max_depth = 1)
estimator.fit(X, y, sample_weight=sample_weights)
y_predict = estimator.predict(X)
incorrect = y_predict != y
e = np.sum(sample_weights[incorrect])
weight = np.log((1 - e) / e) + np.log(self.n_classes - 1)
temp_weights = np.multiply(sample_weights, np.exp(weight * incorrect))
sample_weights = temp_weights / np.sum(temp_weights)
weights.append(weight)
estimators.append(estimator)
self.weights = weights
self.estimators = estimators
def predict(self, X):
"""
AdaBoost 多分类SAMME算法预测
"""
results = np.zeros((X.shape[0], self.n_classes))
for i in range(self.n_estimators):
estimator = self.estimators[i]
weight = self.weights[i]
predicts = estimator.predict(X)
for j in range(self.n_classes):
results[predicts == self.classes[j], j] += weight
return self.classes.take(np.argmax(results, axis=1), axis=0)
使用 Python 实现 AdaBoost-SAMME.R 算法
import numpy as np
from sklearn.tree import DecisionTreeClassifier
class adaboostmcr():
"""
AdaBoost 多分类SAMME.R算法
"""
def __init__(self, n_estimators = 100):
self.n_estimators = n_estimators
def fit(self, X, y):
"""
AdaBoost 多分类SAMME.R算法拟合
"""
self.classes = np.unique(y)
self.n_classes = len(self.classes)
sample_weights = np.ones(X.shape[0]) / X.shape[0]
estimators = []
y_codes = np.array([-1. / (self.n_classes - 1), 1.])
y_coding = y_codes.take(self.classes == y[:, np.newaxis])
for i in range(self.n_estimators):
estimator = DecisionTreeClassifier(max_depth = 1)
estimator.fit(X, y, sample_weight=sample_weights)
y_predict_proba = estimator.predict_proba(X)
np.clip(y_predict_proba, np.finfo(y_predict_proba.dtype).eps, None, out=y_predict_proba)
temp_weights = sample_weights * np.exp(- ((self.n_classes - 1) / self.n_classes) * np.sum(np.multiply(y_coding, np.log(y_predict_proba)), axis=1))
sample_weights = temp_weights / np.sum(temp_weights)
estimators.append(estimator)
self.estimators = estimators
def predict(self, X):
"""
AdaBoost 多分类SAMME.R算法预测
"""
results = np.zeros((X.shape[0], self.n_classes))
for i in range(self.n_estimators):
estimator = self.estimators[i]
y_predict_proba = estimator.predict_proba(X)
np.clip(y_predict_proba, np.finfo(y_predict_proba.dtype).eps, None, out=y_predict_proba)
y_predict_proba_log = np.log(y_predict_proba)
h = (self.n_classes - 1) * (y_predict_proba_log - (1 / self.n_classes) * np.sum(y_predict_proba_log, axis=1)[:, np.newaxis])
results += h
return self.classes.take(np.argmax(results, axis=1), axis=0)
使用 Python 实现 AdaBoost.R2 算法
import numpy as np
from sklearn.tree import DecisionTreeRegressor
class adaboostr():
"""
AdaBoost 回归算法
"""
def __init__(self, n_estimators = 100):
self.n_estimators = n_estimators
def fit(self, X, y):
"""
AdaBoost 回归算法拟合
"""
sample_weights = np.ones(X.shape[0]) / X.shape[0]
estimators = []
weights = []
for i in range(self.n_estimators):
estimator = DecisionTreeRegressor(max_depth = 3)
estimator.fit(X, y, sample_weight=sample_weights)
y_predict = estimator.predict(X)
errors = np.abs(y_predict - y)
errors = errors / np.max(errors)
e = np.sum(np.multiply(errors, sample_weights))
if e >= 0.5:
self.n_estimators = i
break
weight = e / (1 - e)
temp_weights = np.multiply(sample_weights, np.power(weight, 1 - errors))
sample_weights = temp_weights / np.sum(temp_weights)
weights.append(weight)
estimators.append(estimator)
self.weights = np.array(weights)
self.estimators = np.array(estimators)
def predict(self, X):
"""
AdaBoost 回归算法预测
"""
weights = np.log(1 / self.weights)
predictions = np.array([self.estimators[i].predict(X) for i in range(self.n_estimators)]).T
sorted_idx = np.argsort(predictions, axis=1)
weight_cdf = np.cumsum(weights[sorted_idx], axis=1, dtype=np.float64)
median_or_above = weight_cdf >= 0.5 * weight_cdf[:, -1][:, np.newaxis]
median_idx = median_or_above.argmax(axis=1)
median_estimators = sorted_idx[np.arange(X.shape[0]), median_idx]
return predictions[np.arange(X.shape[0]), median_estimators]
六、第三方库实现
scikit-learn3 实现自适应增强分类
from sklearn.ensemble import AdaBoostClassifier
clf = AdaBoostClassifier(n_estimators = 50, random_state = 0, algorithm = "SAMME")
clf = AdaBoostClassifier(n_estimators = 50, random_state = 0, algorithm = "SAMME.R")
clf = clf.fit(X, y)
scikit-learn4 实现自适应增强回归
from sklearn.ensemble import AdaBoostRegressor
clf = AdaBoostRegressor(n_estimators = 50, random_state = 0)
clf = clf.fit(X, y)
七、示例演示
图 7-1 展示了使用自适应增强算法进行二分类的结果,红色表示标签值为 -1 的样本点,蓝色代表标签值为 1 的样本点。浅红色的区域为预测值为 -1 的部分,浅蓝色的区域则为预测值为 1 的部分
图7-1
图 7-2 、图 7-3 分别展示了使用 SAMME 和 SAMME.R 算法进行多分类的结果,红色表示标签值为 0 的样本点,蓝色代表标签值为 1 的样本点,绿色代表标签值为 2 的样本点。浅红色的区域为预测值为 0 的部分,浅蓝色的区域则为预测值为 1 的部分,浅绿色的区域则为预测值为 1 的部分
图7-2
图7-3
图 7-4 展示了使用自适应增强算法进行回归的结果
图7-4
八、思维导图
图8-1
九、参考文献
- en.wikipedia.org/wiki/Boosti…
- en.wikipedia.org/wiki/AdaBoo…
- scikit-learn.org/stable/modu…
- scikit-learn.org/stable/modu…
- www.face-rec.org/algorithms/…
- citeseer.ist.psu.edu/viewdoc/dow…
- hastie.su.domains/Papers/samm…
完整演示请点击这里
注:本文力求准确并通俗易懂,但由于笔者也是初学者,水平有限,如文中存在错误或遗漏之处,恳请读者通过留言的方式批评指正
本文首发于——AI导图,欢迎关注