提升方法|机器学习方法(李航)

分类问题的提升方法的思想是,学习多个分类器,各个分类器中训练样本的权重是不同的,最后将这些分类器进行线性组合,形成一个最终的分类器,以提高分类的性能。

这种思路类似于将多个专家的判断进行适当的综合所得出的判断,要比其中任何一个专家单独的判断好。

在提升方法中有两个基础概念:强可学习,弱可学习。

简单来说,一个概念如果存在一个算法能够学习它,且正确率很高,那么就称这个概念是强可学习的。对应的,一个概念如果存在一个算法能够学习它,学习的正确率仅比随机猜测略好,那么就称这个概念是弱可学习的。

然而在之后算法的发展中,强可学习与弱可学习被证明是等价的,那么问题就是,如果已经发现了一个“弱学习算法”,如何将其提升(boost)为“强学习算法”,便成为开发提升方法时所要解决的问题。

Adaboost算法

仅考虑分类问题,Adaboost算法的基本思路就是通过反复学习,得到一系列弱分类器(又称为基本分类器),然后线性组合这些弱分类器,构成一个强分类器。

很多提升算法所使用的方法,就是对分类器进行迭代,每一次迭代都赋予训练数据集中的实例新的权值,使下一次学习得到的模型能够进化,最后,对得到的所有模型进行线性组合,得到最终的强分类器。

于是,这一问题又转变为以下两个问题

  • 每一次迭代按照什么样的规则赋予实例权值
  • 如何组合多个弱分类器来得到强分类器

在Adaboost算法中,每一轮都要提高被前一轮弱分类器错误分类样本的权值,而降低那些被正确分类样本的权值。这样一来,那些没有得到正确分类的数据,由于其权值的加大而受到后一轮的弱分类器的更大关注。

至于第2个问题,即弱分类器的组合, Adaboost采取加权多数表决的方法。具体地说,加大分类误差率小的弱分类器的权值,使其在表决中起较大的作用;减小分类误差率大的弱分类器的权值,使其在表决中起较小的作用。这样分类错误的可能性就被降低了。

Adaboost算法理论

在Adaboost算法中,输入的是训练集和迭代次数M,输出的是最终分类器 G ( x ) G(x) G(x),即提升后的强学习算法。

之前提到我们需要对训练数据分配权值,在初始化中,我们使各个实例权值相等,这样可以保证在原始数据集上学习得到基本分类器 G 1 ( x ) G_1(x) G1(x)
D 1 = ( w 11 , ⋯   , w 1 i , ⋯   , w 1 N ) , w 1 i = 1 N , i = 1 , 2 , ⋯   , N D_1 = (w_{11},\cdots,w_{1i},\cdots,w_{1N}),w_{1i}=\frac1N,i=1,2,\cdots,N D1=(w11,,w1i,,w1N),w1i=N1,i=1,2,,N
其中下标1表示是初始权值,D代表权值向量,N代表样本容量,在经过第一轮的学习后将权值更新,下标将+1。学习轮数用m表示。

对于 m = 1 , 2 , … , M m = 1, 2, …, M m=1,2,,M,我们使用权值具有Dm的训练数据集进行学习,得到对应的基本分类器 G m ( x ) G_m(x) Gm(x)
G m ( x ) : x → { − 1 , + 1 } G_m(x): \quad x \rightarrow\{-1,+1\} Gm(x):x{1,+1}
为了更新数据集实例的权值,需要对每一轮 m = 1 , 2 , … , M m = 1, 2, …, M m=1,2,,M计算分类器 G m ( x ) G_m(x) Gm(x)在训练数据集上的分类误差率:
e m = ∑ i = 1 N P ( G m ( x i ) ≠ y i ) = ∑ i = 1 N w m i I ( G m ( x i ) ≠ y i ) e_m=\sum_{i=1}^NP(G_m(x_i) \ne y_i)=\sum_{i=1}^Nw_{mi}I(G_m(x_i)\ne y_i) em=i=1NP(Gm(xi)=yi)=i=1NwmiI(Gm(xi)=yi)
其中I是指示函数,根据上式可以看到, e m e_m em为被误分类的实例的权值之和。

在强分类器中,需要对各个弱分类器添加权重,于是需要计算弱分类器 G m ( x ) G_m(x) Gm(x)的权重系数,用来表示该分类器在最终分类器中的重要程度:
a m = 1 2 log ⁡ 1 − e m e m a_m=\frac12 \log\frac{1-e_m}{e_m} am=21logem1em
其中对数为自然对数。可以看到,当 e m ≤ 0.5 e_m \leq 0.5 em0.5时,$ a_m \geq 0$,且 a m a_m am随着 e m e_m em的减少而增大,这样就能让分类误差越小的基本分类器在强分类器中越重要。

为了进行下一轮的学习,需要对训练集实例的权重进行更新,其中被上一轮学习错误分类的实例的权重将会增加,而被正确分类的实例的权重将会下降,因此下一轮的学习中,分类器将更多地考虑之前被分类错误的实例。

新的训练集权重为:
D m + 1 = ( w m + 1 , 1 , ⋯   , w m + 1 , i , ⋯   , w m + 1 , N ) D_{m+1}=(w_{m+1,1}, \cdots, w_{m+1,i}, \cdots, w_{m+1,N}) Dm+1=(wm+11,,wm+1i,,wm+1N)
其中每一个实例的权重为:
w m + 1 , i = w m i Z m e x p ( − a m y i G m ( x i ) ) , i = 1 , 2 , ⋯   , N w_{m+1,i}=\frac{w_{mi}}{Z_m}exp(-a_my_iG_m(x_i)), i=1,2,\cdots,N wm+1,i=Zmwmiexp(amyiGm(xi)),i=1,2,,N
Z m Z_m Zm是规范化因子:
Z m = ∑ i = 1 N w m i e x p ( − a m y i G m ( x i ) ) Z_m=\sum_{i=1}^Nw_{mi}exp(-a_my_iG_m(x_i)) Zm=i=1Nwmiexp(amyiGm(xi))
Z m Z_m Zm的存在可以让所有实例权值的和为1。

可以看到在样本权重更新的过程中,如果分类错误,在 − a m y i G m ( x i ) -a_my_iG_m(x_i) amyiGm(xi) 中, y i G m ( x i ) y_iG_m(x_i) yiGm(xi)为-1,则原权重将增加,如果分类正确, y i G m ( x i ) y_iG_m(x_i) yiGm(xi)为+1,则原权重将减少。

在经过M轮的迭代后,我们将得到M个基本分类器 G m ( x ) , m = 1 , 2 , … , M G_m(x),m=1,2,…,M Gm(x)m=1,2,,M,现在,为了得到最终的强分类器,我们需要对这些弱分类器进行线性组合,即给予它们在强分类器中的权重:
f ( x ) = ∑ m = 1 M a m G m ( x ) f(x)=\sum_{m=1}^Ma_mG_m(x) f(x)=m=1MamGm(x)

于是最终分类器为:
G ( x ) = s i g n ( f ( x ) ) = s i g n ( ∑ m = 1 M a m G m ( x ) ) G(x)=sign(f(x))=sign\left( \sum_{m=1}^Ma_mG_m(x) \right) G(x)=sign(f(x))=sign(m=1MamGm(x))

前向分步算法

Adaboost算法还有另一个解释,即可以认为 Adaboost算法是,模型为加法模型、损失函数为指数函数、学习算法为前向分步算法时的二类分类学习方法。

首先来看加法模型。

考虑一个加法模型的一般形式:
f ( x ) = ∑ m = 1 M β m b ( x ; γ m ) f(x)=\sum_{m=1}^M\beta_mb(x;\gamma_m) f(x)=m=1Mβmb(x;γm)
其中, b ( x ; γ m ) b(x;\gamma_m) b(x;γm)为基函数, γ m \gamma_m γm为基函数的参数, β m \beta_m βm为基函数的系数。显然,对应到Adabosst,它的模型 f ( x ) = ∑ m = 1 M a m G m ( x ) f(x)=\sum_{m=1}^Ma_mG_m(x) f(x)=m=1MamGm(x) 是一个加法模型。

加法模型的损失函数为:
∑ i = 1 N L ( y i , ∑ m = 1 M β m b ( x ; γ m ) ) \sum_{i=1}^NL\left( y_i,\sum_{m=1}^M\beta_mb(x;\gamma_m) \right) i=1NL(yi,m=1Mβmb(x;γm))
即使用加法模型进行预测,并与真实值比较,根据损失函数L得到该样本的损失,并将所有样本的损失相加,即得到加法模型的损失函数。

最小化这种形式的损失函数是一个十分复杂的问题,于是提出前向分步算法解决这一问题。前向分步算法的思路是:既然对整个加法模型求最小化损失函数的解比较困难,那么我们可以从前向后,每一步只学习一个基函数和它的系数,这样就可以简化这一问题:
min ⁡ β , γ ∑ i = 1 N L ( y i , β b ( x i ; γ ) ) \min_{\beta,\gamma}\sum_{i=1}^NL(y_i,\beta b(x_i;\gamma)) β,γmini=1NL(yi,βb(xi;γ))
从形式上可以看到,上式将下标m去掉了,此时仅使用一个基分类器进行预测,得到的损失函数即该基分类器的损失函数,此时通过最小化这一损失函数,可以优化该基分类器。

具体到Adaboost,就是从前往后对每一轮迭代得到的弱分类器进行最小化损失函数的计算,得到在该轮中最优的弱分类器,再根据这个最优的弱分类器对训练集实例的权值进行迭代。

前向分步算法的基本步骤为:

首先初始化 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0

接着,对于每一轮迭代 m = 1 , 2 , ⋯   , M m = 1, 2, \cdots, M m=1,2,,M极小化损失函数:
( β m , γ m ) = arg ⁡ min ⁡ β , γ ∑ i = 1 N L ( y i , f m − 1 ( x i ) + β b ( x i ; γ ) ) (\beta_m,\gamma_m)=\arg\min_{\beta,\gamma}\sum_{i=1}^NL(y_i,f_{m-1}(x_i)+\beta b(x_i;\gamma)) (βm,γm)=argβ,γmini=1NL(yi,fm1(xi)+βb(xi;γ))
通过对损失函数的极小化,我们能够得到每一轮的最优参数 ( β m , γ m ) (\beta_m,\gamma_m) (βm,γm)。得到最优参数后即得到在该轮内的最优弱分类器:
f m ( x ) = f m − 1 ( x ) + β m b ( x ; γ m ) f_m(x)=f_{m-1}(x)+\beta_mb(x;\gamma_m) fm(x)=fm1(x)+βmb(x;γm)
经过M轮迭代后,得到M个弱分类器,并且每个分类器的损失函数已经最小化,得到了最优参数,再将各个分类器进行线性组合得到加法模型,即强分类器:
f ( x ) = f m ( x ) = ∑ m = 1 M β m b ( x ; γ m ) f(x)=f_{m}(x)=\sum_{m=1}^M\beta_mb(x;\gamma_m) f(x)=fm(x)=m=1Mβmb(x;γm)
现在具体到Adaboost,可以说,当前向分步算法的损失函数为指数损失函数时,就与Adaboost算法等价了。下面对这一论述进行数学证明。

首先写出前向分步算法的指数损失函数的形式:
L ( y , f ( x ) ) = e x p [ − y f ( x ) ] L(y,f(x))=exp[-yf(x)] L(y,f(x))=exp[yf(x)]
在Adaboost算法中,对于第m轮迭代得到的 a m a_m am G m ( x ) G_m(x) Gm(x) f m ( x ) f_m(x) fm(x)有下列关系式:
f m ( x ) = f m − 1 ( x ) + a m G m ( x ) f_m(x)=f_{m-1}(x)+a_mG_m(x) fm(x)=fm1(x)+amGm(x)
其中:
f m ( x ) = f m − 2 ( x ) + a m − 1 G m − 1 ( x ) = a 1 G 1 ( x ) + ⋯ + a m − 1 G m − 1 ( x ) f_m(x)=f_{m-2}(x)+a_{m-1}G_{m-1}(x)=a_1G_1(x)+\cdots+a_{m-1}G_{m-1}(x) fm(x)=fm2(x)+am1Gm1(x)=a1G1(x)++am1Gm1(x)
现在我们利用前提条件,即损失函数使用指数损失函数,下式成立:
( a m , G m ( x ) ) = arg ⁡ min ⁡ a , G ∑ i = 1 N exp ⁡ [ − y i ( f m ( x i ) ) ] = arg ⁡ min ⁡ a , G ∑ i = 1 N exp ⁡ [ − y ( f m − 1 ) + a G ( x i ) ] (a_m,G_m(x))=\arg \min_{a,G}\sum_{i=1}^N\exp[-y_i(f_m(x_i))]=\arg\min_{a,G}\sum_{i=1}^N\exp[-y(f_{m-1})+aG(x_i)] (am,Gm(x))=arga,Gmini=1Nexp[yi(fm(xi))]=arga,Gmini=1Nexp[y(fm1)+aG(xi)]
上式最左是指数损失函数最小时弱分类器Gm(x)和弱分类器的系数αm,最右的α和G(x)是还没有最小化损失函数时的分类器和系数,此时它们不是最优的。

我们令 w → m i = e x p [ − y i f m − 1 ( x i ) ] \overrightarrow{w}_{mi}=exp[-y_if_{m-1}(x_i)] w mi=exp[yifm1(xi)],于是上式可以转变为:
KaTeX parse error: Can't use function '$' in math mode at position 41: …,G}\sum_{i=1}^N$̲\overrightarrow…
因为 w → m i \overrightarrow{w}_{mi} w mi 不依赖α也不依赖分类器G,因此与最小化无关,但是它依赖于之前所有得到的弱分类器的线性组合 f m − 1 ( x ) f_{m-1}(x) fm1(x),因此它的值会随着每一轮迭代而变化。

现在分别对 a m a_m am G m ( x ) G_m(x) Gm(x)进行讨论,对于 G m ( x ) G_m(x) Gm(x)而言,我们看到当分类正确,指数为负,当分类错误,指数为正,即当分类错误的实例越少,则损失函数越小,于是可以用指数函数I表示,因此m轮最优基本分类器 G m ∗ ( x ) G_m^*(x) Gm(x)可以表示为:
G m ∗ ( x ) = arg ⁡ min ⁡ G ∑ i = 1 N w → m i I ( y i ≠ G ( x i ) ) G_m^*(x)=\arg \min_G\sum_{i=1}^N\overrightarrow{w}_{mi}I(y_i \ne G(x_i)) Gm(x)=argGmini=1Nw miI(yi=G(xi))
接下来再看 a m a_m am
∑ i = 1 N w → m i e x p [ − y i a G ( x i ) ] = ∑ y i = G m ( x i ) w → m i e − a + ∑ y i ≠ G m ( x i ) w → m i e a = ∑ y i = G m ( x i ) w → m i e − a + ∑ y i ≠ G m ( x i ) w → m i e a − ∑ y i ≠ G m ( x i ) w → m i e − a + ∑ y i ≠ G m ( x i ) w → m i e a = ∑ y i ≠ G m ( x i ) w → m i e a − ∑ y i ≠ G m ( x i ) w → m i e − a + ∑ y i = G m ( x i ) w → m i e − a + ∑ y i ≠ G m ( x i ) w → m i e a = ( e a − e − a ) ∑ y i ≠ G m ( x i ) w → m i + e − a ∑ i w → m i = ( e a − e − a ) ∑ i = 1 w → m i I ( y ≠ G ( x i ) ) + e − a ∑ i = 1 N w → m i \begin{aligned} & \sum_{i=1}^N\overrightarrow{w}_{mi}exp[-y_iaG(x_i)] \\ =&\sum_{y_i=G_m(x_i)}\overrightarrow{w}_{mi}e^{-a}+\sum_{y_i\ne G_m(x_i)}\overrightarrow{w}_{mi}e^{a} \\ =&\sum_{y_i=G_m(x_i)}\overrightarrow{w}_{mi}e^{-a}+ \sum_{y_i\ne G_m(x_i)}\overrightarrow{w}_{mi}e^{a}- \sum_{y_i \ne G_m(x_i)}\overrightarrow{w}_{mi}e^{-a}+ \sum_{y_i\ne G_m(x_i)}\overrightarrow{w}_{mi}e^{a} \\ =& \sum_{y_i\ne G_m(x_i)}\overrightarrow{w}_{mi}e^{a}- \sum_{y_i \ne G_m(x_i)}\overrightarrow{w}_{mi}e^{-a}+ \sum_{y_i=G_m(x_i)}\overrightarrow{w}_{mi}e^{-a}+ \sum_{y_i\ne G_m(x_i)}\overrightarrow{w}_{mi}e^{a} \\ =& (e^a-e^{-a})\sum_{y_i\ne G_m(x_i)}\overrightarrow{w}_{mi}+e^{-a}\sum_{i}\overrightarrow{w}_{mi} \\ =&(e^a-e^{-a})\sum_{i=1}\overrightarrow{w}_{mi}I(y \ne G(x_i))+e^{-a}\sum_{i=1}^N\overrightarrow{w}_{mi} \end{aligned} =====i=1Nw miexp[yiaG(xi)]yi=Gm(xi)w miea+yi=Gm(xi)w mieayi=Gm(xi)w miea+yi=Gm(xi)w mieayi=Gm(xi)w miea+yi=Gm(xi)w mieayi=Gm(xi)w mieayi=Gm(xi)w miea+yi=Gm(xi)w miea+yi=Gm(xi)w miea(eaea)yi=Gm(xi)w mi+eaiw mi(eaea)i=1w miI(y=G(xi))+eai=1Nw mi
根据之前Adaboost的推导,可知 ∑ i = 1 w → m i I ( y ≠ G ( x i ) ) = e m \sum_{i=1}\overrightarrow{w}_{mi}I(y \ne G(x_i))=e_m i=1w miI(y=G(xi))=em ,且 ∑ i = 1 N w → m i = 1 \sum_{i=1}^N\overrightarrow{w}_{mi}=1 i=1Nw mi=1 ,因此上式可继续转变为:
( e a − e − a ) e m + e − a (e^a-e^{-a})e_m+e^{-a} (eaea)em+ea
现在对其进行关于 a a a的求导并令导数为0:
∂ [ ( e a − e − a ) e m + e − a ] ∂ a = e a e m + e − a e m − e − a = 0 e a e m = e e − a ( 1 − e m ) → e a e − a = 1 − e m e m → e 2 a = 1 − e m e m → 2 a = log ⁡ 1 − e m e m → a = 1 2 log ⁡ 1 − e m e m \frac{\partial[(e^a-e^{-a})e_m+e^{-a}]}{\partial a}=e^ae_m+e^{-a}e_m-e^{-a}=0 \\ e^ae_m=e^{e^-a}(1-e_m) \rightarrow \\ \frac{e^a}{e^{-a}}=\frac{1-e_m}{e_m} \rightarrow \\ e^{2a}=\frac{1-e_m}{e_m} \rightarrow \\ 2a=\log\frac{1-e_m}{e_m} \rightarrow \\ a= \frac12 \log\frac{1-e_m}{e_m} a[(eaea)em+ea]=eaem+eaemea=0eaem=eea(1em)eaea=em1eme2a=em1em2a=logem1ema=21logem1em
于是得到最优 a m ∗ a_m^* am,这里得到的最优 a m ∗ a_m^* am与之前Adaboost推导中得到的基本分类器的系数等式一致。

这样就在前向分步算法中得到的最优 G m G_m Gm G m G_m Gm的系数 a a a,和在Adaboost算法中得到的一致。最后再来看一下前向分步算法中每一轮样本权值的更新:

因为:
w → m i = exp ⁡ [ − y i f m − 1 ( x i ) ] \overrightarrow{w}_{mi} = \exp[-y_if_{m-1}(x_i)] w mi=exp[yifm1(xi)]
所以:
w → m + 1 , i = exp ⁡ [ − y i f m ( x i ) ] = exp ⁡ [ − y i f m − 1 ( x i ) + a m G m ( x ) ] = exp ⁡ [ − y i f m − 1 ( x i ) + y i a m G m ( x ) ] = exp ⁡ [ − y i a m G m ( x ) ] \begin{aligned} \overrightarrow{w}_{m+1,i}&=\exp[-y_if_{m}(x_i)] \\ &=\exp[-y_if_{m-1}(x_i)+a_mG_m(x)] \\ &=\exp[-y_if_{m-1}(x_i)+y_ia_mG_m(x)] \\ &=\exp[-y_ia_mG_m(x)] \\ \end{aligned} w m+1,i=exp[yifm(xi)]=exp[yifm1(xi)+amGm(x)]=exp[yifm1(xi)+yiamGm(x)]=exp[yiamGm(x)]
这与Adaboost算法中只相差规范化因子,而规范化因子并不影响权值的分布,因此两者等价。

前向分步算法与Adaboost算法的关系

我们之前说到在前向分步算法中,如果损失函数取指数损失函数,则与Adaboost算法等价,也就是说,Adaboost算法是前向分步算法的一个特殊情况。

提升树

提升树是以分类树或回归树为基本分类器的提升方法。

提升树模型可以表示为决策树的加法模型:
f M ( x ) = ∑ m = 1 M T ( x ; Θ m ) f_M(x)=\sum_{m=1}^MT(x;\Theta_m) fM(x)=m=1MT(x;Θm)
其中 T ( x ; Θ m ) T(x;\Theta_m) T(x;Θm) 表示决策树, Θ m \Theta_m Θm为决策树参数, M M M为树的个数。

提升树算法

采用前向分布算法,其初始提升树为: f 0 ( x ) = 0 f_0(x)=0 f0(x)=0 ,第 m m m步的模型是:
f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) f_m(x)=f_{m-1}(x)+T(x;\Theta_m) fm(x)=fm1(x)+T(x;Θm)
其中 f m − 1 ( x ) f_{m-1}(x) fm1(x) m − 1 m-1 m1步时的强分类器, T ( x ; Θ m ) T(x;\Theta_m) T(x;Θm)是第 m m m步得到的新弱分类器,两者的线性组合 f m ( x ) f_m(x) fm(x)即为新的强分类器。

可以通过经验风险极小化确定下一课决策树的参数:
Θ ^ m = arg ⁡ min ⁡ Θ m ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x i ; Θ m ) ) \hat{\Theta}_m=\arg \min_{\Theta_m}\sum_{i=1}^NL(y_i,f_{m-1}(x_i)+T(x_i;\Theta_m)) Θ^m=argΘmmini=1NL(yi,fm1(xi)+T(xi;Θm))
下面讨论针对不同问题的提升树学习算法,其主要区别在于使用的损失函数不同。包括用平方误差损失函数的回归问题,用指数损失函数的分类问题,以及用一般损失函数的一般决策问题。

对于二分类问题,提升树算法只需将Adaboost算法中的基本分类器限制为二分类树即可。

在提升树算法中,基本分类器可表示为:
T ( x ; Θ m ) = ∑ j = 1 J c j I ( x ∈ R j ) T(x;\Theta_m) = \sum_{j=1}^Jc_jI(x \in R_j) T(x;Θm)=j=1JcjI(xRj)
其中 R j R_j Rj是输入空间中的一个被划分出来的区域, c j c_j cj代表这个区域的输出值,J代表区域的数量,在树中即是叶节点的数量。参数 Θ = { ( R 1 , c 1 ) , ( R 2 , c 2 ) , … , ( R J , c J ) } \Theta=\{ (R_1,c_1), (R_2,c_2), \dots, (R_J,c_J) \} Θ={(R1,c1),(R2,c2),,(RJ,cJ)},表示树的区域划分和各区域上的常数。

总结一下,回归问题提升树的前向分步算法为:
f 0 ( x ) = 0 f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) , m = 1 , 2 , ⋯   , M f M ( x ) = ∑ m = 1 M T ( x ; Θ m ) \begin{aligned} &f_0(x)=0 \\ &f_m(x)=f_{m-1}(x)+T(x;\Theta_m),m=1,2,\cdots,M \\ &f_M(x)=\sum_{m=1}^MT(x;\Theta_m) \end{aligned} f0(x)=0fm(x)=fm1(x)+T(x;Θm),m=1,2,,MfM(x)=m=1MT(x;Θm)
在每一步中,都需要最优化当前模型:
Θ ^ m = arg ⁡ min ⁡ Θ m ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x i ; Θ m ) ) \hat{\Theta}_m=\arg \min_{\Theta_m}\sum_{i=1}^NL(y_i,f_{m-1}(x_i)+T(x_i;\Theta_m)) Θ^m=argΘmmini=1NL(yi,fm1(xi)+T(xi;Θm))
当上式中的损失函数采用平方误差损失函数时:
L ( y , f ( x ) ) = ( y − f ( x ) ) 2 L(y,f(x))=(y-f(x))^2 L(y,f(x))=(yf(x))2
损失变为:
L ( y , f m − 1 ( x ) + T ( x i ; Θ m ) ) ) = [ y − f m − 1 ( x ) − T ( x i ; Θ m ) ) ] 2 = [ r − T ( x i ; Θ m ) ) ] 2 L(y,f_{m-1}(x)+T(x_i;\Theta_m)))=[y-f_{m-1}(x)-T(x_i;\Theta_m))]^2=[r-T(x_i;\Theta_m))]^2 L(y,fm1(x)+T(xi;Θm)))=[yfm1(x)T(xi;Θm))]2=[rT(xi;Θm))]2
其中 r = y − f m − 1 ( x ) r=y-f_{m-1}(x) r=yfm1(x)是当前模型拟合数据的残差。对回归问题的提升树算法来说,只需简单地拟合当前模型的残差。

上句话的意思是,我们可以将上一步得到的训练集实例的残差作为训练集实例的新标记,并用新的标记训练下一颗树。

梯度提升

在提升树算法中,当损失函数是平方损失和指数损失函数时,每一步优化是很简单的。但对一般损失函数而言,则没有那么容易,为了解决这个问题,提出了梯度提升算法。这是一种利用梯度下降法的方法,即在求每一个树的最小化损失函数时,采用梯度下降法得到最优参数。
− [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) -\left[\frac{\partial L(y,f(x_i))}{\partial f(x_i)}\right]_{f(x)=f_{m-1}(x)} [f(xi)L(y,f(xi))]f(x)=fm1(x)

梯度提升的关键在于,我们使用损失函数的负梯度在当前模型的值作为训练下一个基本分类器的标记,拟合一个回归树。

代码

import numpy as np
import pandas as pd
from sklearn import datasets
from sklearn.model_selection import train_test_split
import math
from tqdm import tqdm

# 加载鸢尾花数据集
iris = datasets.load_iris()
df = pd.DataFrame(iris.data)

# 加载鸢尾花数据集
x = iris.data
y = iris.target

# 加载鸢是三分类,将它转为二分类
# y = np.array([1 if i == 1 else 0 for i in y])
# 划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=100)

class Model:
    def __init__(self, train_data, train_label, num):
        self.train_data = train_data  # 训练集数据
        self.train_label = train_label  # 训练集标记
        self.num = num  # 多少个基本分类器
        # m为样本容量,n为特征数量
        m, n = np.shape(train_data)
        self.m = m  # m为样本容量
        self.n = n  # n为特征数量
        self.w = np.full(m, fill_value=1 / m)
        self.tree_arr = []
        self.alpha_arr = []

    def estimate(self, feature, divide, rule):
        if rule == 'H1L-1':
            H = 1
            L = -1
        else:
            H = -1
            L = 1

        error = 0
        predict_arr = []

        num = len(feature)

        # 遍历每个样本,判断样本的该特征值是否大于divide
        # 并预测相应的类
        for i in range(num):
            if feature[i] > divide:
                predict = H
            else:
                predict = L

            predict_arr.append(predict)

            # 如果预测值与标记不符
            # 则error + 该分类错误样本所对应的权值
            if predict != self.train_label[i]:
                error += self.w[i]

        return predict_arr, error

    def create_single_boosting_tree(self):
        # 错误的样本数
        error = 1
        tree_dict = {}
        for i in range(self.n):
            feature = self.train_data[:, i]

            for divide in [-0.5, 0.5, 1.5]:
                for rule in ['H1L-1', 'H-1L1']:
                    predict_arr, e = self.estimate(feature, divide, rule)

                    if e < error:
                        error = e
                    tree_dict['feature'] = i
                    tree_dict['divide'] = divide
                    tree_dict['rule'] = rule
                    tree_dict['error'] = e
                    tree_dict['PredictArr'] = predict_arr
        return tree_dict

    def create_boosting_tree(self):
        for N in trange(self.num):
            tree_dict = self.create_single_boosting_tree()
            self.tree_arr.append(tree_dict)
            
            e = tree_dict["error"]
            alpha = np.log((1 - e) / e) / 2
            self.alpha_arr.append(alpha)
            
            gxi=tree_dict["PredictArr"]
    
            exp = np.exp(-1*alpha*self.train_label*gxi)
            
            Z = np.dot(self.w, exp)
            
            self.w = self.w * exp / Z
            
    
    def fit(self):
        self.create_boosting_tree()

    def predict(self, feature, divide, rule, x):
        # 用于之后预测在该情况下每个样本属于哪个类
        if rule == 'H1L-1':
            H = 1
            L = -1
        else:
            H = -1
            L = 1
    
        # 根据特征值的大小返回相应预测值
        if x[feature] > divide:
            return H
        else:
            return L
    def test(self, test_data, test_label):
        error = 0
        for i in trange(len(test_label)):
            xi = test_data[i]
            yi = test_label[i]
            
            result = 0
            
            for j in range(self.num):
                tree = self.tree_arr[j]

                feature = tree['feature']
                divide = tree['divide']
                rule = tree['rule']

                weak_result = self.predict(feature, divide, rule, xi)

                alpha = self.alpha_arr[j]
                result += alpha * weak_result

            final_result = np.sign(result)

            # 如果分类错误,errorCnt + 1
            if final_result != yi:
                error += 1
        
        acc = 1- error / len(test_label)

        return acc


adt = Model(x_train, y_train, 1000)
adt.fit()

acc = adt.test(x_test, y_test)
print("acc", acc)
  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值