gbdt如何用于gbdt二分类代码

    在中,我们对Boosting家族的Adaboost算法做了总结,本文就对Boosting家族中另一个重要的算法梯度提升树(Gradient Boosting Decison Tree, 以下简称GBDT)做一个总结。GBDT有很多简称,有GBT(Gradient Boosting Tree),&GTB(Gradient Tree Boosting&),&GBRT(Gradient Boosting Regression Tree), MART(Multiple Additive Regression Tree),其实都是指的同一种算法,本文统一简称GBDT。GBDT在BAT大厂中也有广泛的应用,假如要选择3个最重要的机器学习算法的话,个人认为GBDT应该占一席之地。
1. GBDT概述
    GBDT也是集成学习Boosting家族的成员,但是却和传统的Adaboost有很大的不同。回顾下Adaboost,我们是利用前一轮迭代弱学习器的误差率来更新训练集的权重,这样一轮轮的迭代下去。GBDT也是迭代,使用了前向分布算法,但是弱学习器限定了只能使用CART回归树模型,同时迭代思路和Adaboost也有所不同。
    在GBDT的迭代中,假设我们前一轮迭代得到的强学习器是$f_{t-1}(x)$, 损失函数是$L(y,&f_{t-1}(x))$, 我们本轮迭代的目标是找到一个CART回归树模型的弱学习器$h_t(x)$,让本轮的损失损失$L(y,&f_{t}(x) =L(y,&f_{t-1}(x)+ h_t(x))$最小。也就是说,本轮迭代找到决策树,要让样本的损失尽量变得更小。
    GBDT的思想可以用一个通俗的例子解释,假如有个人30岁,我们首先用20岁去拟合,发现损失有10岁,这时我们用6岁去拟合剩下的损失,发现差距还有4岁,第三轮我们用3岁拟合剩下的差距,差距就只有一岁了。如果我们的迭代轮数还没有完,可以继续迭代下面,每一轮迭代,拟合的岁数误差都会减小。
    从上面的例子看这个思想还是蛮简单的,但是有个问题是这个损失的拟合不好度量,损失函数各种各样,怎么找到一种通用的拟合方法呢?
2. GBDT的负梯度拟合
    在上一节中,我们介绍了GBDT的基本思路,但是没有解决损失函数拟合方法的问题。针对这个问题,大牛Freidman提出了用损失函数的负梯度来拟合本轮损失的近似值,进而拟合一个CART回归树。第t轮的第i个样本的损失函数的负梯度表示为$$r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial&f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)}$$
    利用$(x_i,r_{ti})\;\; (i=1,2,..m)$,我们可以拟合一颗CART回归树,得到了第t颗回归树,其对应的叶节点区域$R_{tj}, j =1,2,..., J$。其中J为叶子节点的个数。
    针对每一个叶子节点里的样本,我们求出使损失函数最小,也就是拟合叶子节点最好的的输出值$c_{tj}$如下:$$c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c)$$
    这样我们就得到了本轮的决策树拟合函数如下:$$h_t(x) = \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
    从而本轮最终得到的强学习器的表达式如下:$$f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})&$$
    通过损失函数的负梯度来拟合,我们找到了一种通用的拟合损失误差的办法,这样无轮是分类问题还是回归问题,我们通过其损失函数的负梯度的拟合,就可以用GBDT来解决我们的分类回归问题。区别仅仅在于损失函数不同导致的负梯度不同而已。
&3.&GBDT回归算法
    好了,有了上面的思路,下面我们总结下GBDT的回归算法。为什么没有加上分类算法一起?那是因为分类算法的输出是不连续的类别值,需要一些处理才能使用负梯度,我们在下一节讲。
    输入是训练集样本$T=\{(x_,y_1),(x_2,y_2), ...(x_m,y_m)\}$, 最大迭代次数T, 损失函数L。
    输出是强学习器f(x)
    1) 初始化弱学习器$$f_0(x) = \underbrace{arg\; min}_{c}\sum\limits_{i=1}^{m}L(y_i, c)$$
    2) 对迭代轮数t=1,2,...T有:
      a)对样本i=1,2,...m,计算负梯度$$r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial&f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)}$$
      b)利用$(x_i,r_{ti})\;\; (i=1,2,..m)$, 拟合一颗CART回归树,得到第t颗回归树,其对应的叶子节点区域为$R_{tj}, j =1,2,..., J$。其中J为回归树t的叶子节点的个数。
      c) 对叶子区域j =1,2,..J,计算最佳拟合值$$c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c)$$
      d) 更新强学习器$$f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})&$$
    3) 得到强学习器f(x)的表达式$$f(x) = f_T(x) =f_0(x) + \sum\limits_{t=1}^{T}\sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
4. GBDT分类算法
    这里我们再看看GBDT分类算法,GBDT的分类算法从思想上和GBDT的回归算法没有区别,但是由于样本输出不是连续的值,而是离散的类别,导致我们无法直接从输出类别去拟合类别输出的误差。
    为了解决这个问题,主要有两个方法,一个是用指数损失函数,此时GBDT退化为Adaboost算法。另一种方法是用类似于逻辑回归的对数似然损失函数的方法。也就是说,我们用的是类别的预测概率值和真实概率值的差来拟合损失。本文仅讨论用对数似然损失函数的GBDT分类。而对于对数似然损失函数,我们又有二元分类和多元分类的区别。
4.1 二元GBDT分类算法
    对于二元GBDT,如果用类似于逻辑回归的对数似然损失函数,则损失函数为:$$L(y, f(x)) = log(1+ exp(-yf(x)))$$
    其中$y \in\{-1, +1\}$。则此时的负梯度误差为$$r_{ti} = -\bigg[\frac{\partial L(y, f(x_i)))}{\partial&f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)} = y_i/(1+exp(y_if(x_i)))$$
    对于生成的决策树,我们各个叶子节点的最佳残差拟合值为$$c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} log(1+exp(-y_i(f_{t-1}(x_i) +c)))$$
    由于上式比较难优化,我们一般使用近似值代替$$c_{tj} =&\sum\limits_{x_i \in R_{tj}}r_{ti}\bigg /&&\sum\limits_{x_i \in R_{tj}}|r_{ti}|(1-|r_{ti}|)$$
    除了负梯度计算和叶子节点的最佳残差拟合的线性搜索,二元GBDT分类和GBDT回归算法过程相同。
4.2 多元GBDT分类算法
    多元GBDT要比二元GBDT复杂一些,对应的是多元逻辑回归和二元逻辑回归的复杂度差别。假设类别数为K,则此时我们的对数似然损失函数为:$$L(y, f(x)) = - &\sum\limits_{k=1}^{K}y_klog\;p_k(x)&$$
    其中如果样本输出类别为k,则$y_k=1$。第k类的概率$p_k(x)&$的表达式为:$$p_k(x) = exp(f_k(x)) \bigg / \sum\limits_{l=1}^{K}&exp(f_l(x))&$$
    集合上两式,我们可以计算出第$t$轮的第$i$个样本对应类别$l$的负梯度误差为$$r_{til} =&-\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial&f(x_i)}\bigg]_{f_k(x) = f_{l, t-1}\;\; (x)} = y_{il} - p_{l, t-1}(x_i)$$
    观察上式可以看出,其实这里的误差就是样本$i$对应类别$l$的真实概率和$t-1$轮预测概率的差值。
    对于生成的决策树,我们各个叶子节点的最佳残差拟合值为$$c_{tjl} = \underbrace{arg\; min}_{c_{jl}}\sum\limits_{i=0}^{m}\sum\limits_{k=1}^{K}&L(y_k, f_{t-1, l}(x) + \sum\limits_{j=0}^{J}c_{jl} I(x_i \in R_{tj}))$$
    由于上式比较难优化,我们一般使用近似值代替$$c_{tjl} = &\frac{K-1}{K} \; \frac{\sum\limits_{x_i \in R_{tjl}}r_{til}}{\sum\limits_{x_i \in R_{til}}|r_{til}|(1-|r_{til}|)}$$
    除了负梯度计算和叶子节点的最佳残差拟合的线性搜索,多元GBDT分类和二元GBDT分类以及GBDT回归算法过程相同。
5. GBDT常用损失函数
    这里我们再对常用的GBDT损失函数做一个总结。
    对于分类算法,其损失函数一般有对数损失函数和指数损失函数两种:
    a) 如果是指数损失函数,则损失函数表达式为$$L(y, f(x)) = exp(-yf(x))$$
    其负梯度计算和叶子节点的最佳残差拟合参见Adaboost原理篇。
    b)&如果是对数损失函数,分为二元分类和多元分类两种,参见4.1节和4.2节。
    对于回归算法,常用损失函数有如下4种:
    a)均方差,这个是最常见的回归损失函数了$$L(y, f(x)) =(y-f(x))^2$$
    b)绝对损失,这个损失函数也很常见$$L(y, f(x)) =|y-f(x)|$$
      对应负梯度误差为:$$sign(y_i-f(x_i))$$
    c)Huber损失,它是均方差和绝对损失的折衷产物,对于远离中心的异常点,采用绝对损失,而中心附近的点采用均方差。这个界限一般用分位数点度量。损失函数如下:
$$L(y, f(x))=\begin{cases}\frac{1}{2}(y-f(x))^2& {|y-f(x)| \leq \delta}\\\delta(|y-f(x)| - \frac{\delta}{2})& {|y-f(x)| & \delta}\end{cases}$$
    对应的负梯度误差为:
$$r(y_i, f(x_i))=\begin{cases}y_i-f(x_i)& {|y_i-f(x_i)| \leq \delta}\\\delta sign(y_i-f(x_i))& {|y_i-f(x_i)| & \delta}\end{cases}$$
    d) 分位数损失。它对应的是分位数回归的损失函数,表达式为$$L(y, f(x)) =\sum\limits_{y \geq f(x)}\theta|y - f(x)| + \sum\limits_{y & f(x)}(1-\theta)|y - f(x)|&$$
      其中$\theta$为分位数,需要我们在回归前指定。对应的负梯度误差为:
$$r(y_i, f(x_i))=\begin{cases}\theta& { y_i \geq f(x_i)}\\\theta - 1 & {y_i & f(x_i) }\end{cases}$$
    对于Huber损失和分位数损失,主要用于健壮回归,也就是减少异常点对损失函数的影响。
6. GBDT的正则化
    和Adaboost一样,我们也需要对GBDT进行正则化,防止过拟合。GBDT的正则化主要有三种方式。
    第一种是和Adaboost类似的正则化项,即步长(learning rate)。定义为$\nu$,对于前面的弱学习器的迭代$$f_{k}(x) = f_{k-1}(x) + h_k(x) $$
    如果我们加上了正则化项,则有$$f_{k}(x) = f_{k-1}(x) + \nu h_k(x) $$
    $\nu$的取值范围为$0 & \nu \leq 1 $。对于同样的训练集学习效果,较小的$\nu$意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。
    第二种正则化的方式是通过子采样比例(subsample)。取值为(0,1]。注意这里的子采样和随机森林不一样,随机森林使用的是放回抽样,而这里是不放回抽样。如果取值为1,则全部样本都使用,等于没有使用子采样。如果取值小于1,则只有一部分样本会去做GBDT的决策树拟合。选择小于1的比例可以减少方差,即防止过拟合,但是会增加样本拟合的偏差,因此取值不能太低。推荐在[0.5, 0.8]之间。
    使用了子采样的GBDT有时也称作随机梯度提升树(Stochastic Gradient Boosting Tree, SGBT)。由于使用了子采样,程序可以通过采样分发到不同的任务去做boosting的迭代过程,最后形成新树,从而减少弱学习器难以并行学习的弱点。
    第三种是对于弱学习器即CART回归树进行正则化剪枝。在决策树原理篇里我们已经讲过,这里就不重复了。
7. GBDT小结 
    GBDT终于讲完了,GDBT本身并不复杂,不过要吃透的话需要对集成学习的原理,决策树原理和各种损失函树有一定的了解。由于GBDT的卓越性能,只要是研究机器学习都应该掌握这个算法,包括背后的原理和应用调参方法。目前GBDT的算法比较好的库是xgboost。当然scikit-learn也可以。
    最后总结下GBDT的优缺点。
    GBDT主要的优点有:
    1) 可以灵活处理各种类型的数据,包括连续值和离散值。
    2) 在相对少的调参时间情况下,预测的准备率也可以比较高。这个是相对SVM来说的。
    3)使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如 Huber损失函数和Quantile损失函数。
    GBDT的主要缺点有:
    1)由于弱学习器之间存在依赖关系,难以并行训练数据。不过可以通过自采样的SGBT来达到部分并行。
    以上就是GBDT的原理总结,后面会讲GBDT的scikit-learn调参,敬请期待。
(欢迎转载,转载请注明出处。欢迎沟通交流: liujianping-)&
阅读(...) 评论(),介绍完回归任务下的GBDT后,这篇文章将介绍在分类任务下的GBDT,大家将可以看到,对于回归和分类,其实GBDT过程简直就是一模一样的。如果说最大的不同的话,那就是在于由于loss function不同而引起的初始化不同、叶子节点取值不同。
GB的一些基本原理都已经在上文中介绍了,下面直接进入正题。
下面是分类任务的GBDT算法过程,其中选用的loss function是logloss。
<span class="MathJax" id="MathJax-Element-152-Frame" tabindex="0" data-mathml="L(yi,Fm(xi))=&#x2212;{yilogpi+(1&#x2212;yi)log(1&#x2212;pi)}" role="presentation" style="position:">L(yi,Fm(xi))=-{yilogpi+(1-yi)log(1-pi)}L(yi,Fm(xi))=-{yilogpi+(1-yi)log(1-pi)}。
其中<span class="MathJax" id="MathJax-Element-153-Frame" tabindex="0" data-mathml="pi=11+e(&#x2212;Fm(xi))" role="presentation" style="position:">pi=11+e(-Fm(xi))pi=11+e(-Fm(xi))
这里简单推导一下logloss通常化简后的式子:
<span class="MathJax" id="MathJax-Element-154-Frame" tabindex="0" data-mathml="L(yi,Fm(xi))=&#x2212;{yilogpi+(1&#x2212;yi)log(1&#x2212;pi)}" role="presentation" style="position:">L(yi,Fm(xi))=-{yilogpi+(1-yi)log(1-pi)}L(yi,Fm(xi))=-{yilogpi+(1-yi)log(1-pi)}
(先不带入负号)
带入<span class="MathJax" id="MathJax-Element-155-Frame" tabindex="0" data-mathml="pi" role="presentation" style="position:">pipi=&<span class="MathJax" id="MathJax-Element-156-Frame" tabindex="0" data-mathml="yilog(11+e(&#x2212;Fm(xi)))+(1&#x2212;yi)log(e(&#x2212;Fm(xi))1+e(&#x2212;Fm(xi)))" role="presentation" style="position:">yilog(11+e(-Fm(xi)))+(1-yi)log(e(-Fm(xi))1+e(-Fm(xi)))yilog(11+e(-Fm(xi)))+(1-yi)log(e(-Fm(xi))1+e(-Fm(xi)))
=&<span class="MathJax" id="MathJax-Element-157-Frame" tabindex="0" data-mathml="&#x2212;yilog(1+e(&#x2212;Fm(xi)))+(1&#x2212;yi){log(e(&#x2212;Fm(xi)))&#x2212;log(1+e(&#x2212;Fm(xi)))}" role="presentation" style="position:">-yilog(1+e(-Fm(xi)))+(1-yi){log(e(-Fm(xi)))-log(1+e(-Fm(xi)))}-yilog(1+e(-Fm(xi)))+(1-yi){log(e(-Fm(xi)))-log(1+e(-Fm(xi)))}
=&<span class="MathJax" id="MathJax-Element-158-Frame" tabindex="0" data-mathml="&#x2212;yilog(1+e(&#x2212;Fm(xi)))+log(e(&#x2212;Fm(xi)))&#x2212;log(1+e(&#x2212;Fm(xi)))&#x2212;yilog(e(&#x2212;Fm(xi)))+yilog(1+e(&#x2212;Fm(xi)))" role="presentation" style="position:">-yilog(1+e(-Fm(xi)))+log(e(-Fm(xi)))-log(1+e(-Fm(xi)))-yilog(e(-Fm(xi)))+yilog(1+e(-Fm(xi)))-yilog(1+e(-Fm(xi)))+log(e(-Fm(xi)))-log(1+e(-Fm(xi)))-yilog(e(-Fm(xi)))+yilog(1+e(-Fm(xi)))
=&<span class="MathJax" id="MathJax-Element-159-Frame" tabindex="0" data-mathml="yiFm(xi)&#x2212;log(1+eFm(xi))" role="presentation" style="position:">yiFm(xi)-log(1+eFm(xi))yiFm(xi)-log(1+eFm(xi))
最后加上负号可以得:
<span class="MathJax" id="MathJax-Element-160-Frame" tabindex="0" data-mathml="L(yi,Fm(xi))=&#x2212;{yilogpi+(1&#x2212;yi)log(1&#x2212;pi)}=&#x2212;{yiFm(xi)&#x2212;log(1+eFm(xi))}" role="presentation" style="position:">L(yi,Fm(xi))=-{yilogpi+(1-yi)log(1-pi)}=-{yiFm(xi)-log(1+eFm(xi))}L(yi,Fm(xi))=-{yilogpi+(1-yi)log(1-pi)}=-{yiFm(xi)-log(1+eFm(xi))}
<span class="MathJax MathJax_FullWidth" id="MathJax-Element-63-Frame" tabindex="0" style="position:" data-mathml="Algorithm&#xA0;3:BinomiaDeviance&#x005F;TreeBoost&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;&#x005F;F0(x)=0.5&#x2217;log(&#x2211;i=1Nyi&#x2211;i=1N(1&#x2212;yi))From=1&#xA0;to&#xA0;M&#xA0;do:&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;yi&#x007E;=&#x2212;[&#x2202;L(yi,F(xi))&#x2202;F(xi)]F(x)=Fm&#x2212;1(x)=yi&#x2212;11+e(&#x2212;Fm&#x2212;1(xi))&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;{Rjm}1J=J&#x2212;terminal&#xA0;node&#xA0;tree({y&#x007E;i,xi}1N)&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x03B3;jm=&#x2211;xi&#x2208;Rjmy&#x007E;i&#x2211;xi&#x2208;Rjm(yi&#x2212;y&#x007E;i)&#x2217;(1&#x2212;yi+y&#x007E;i)&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;Fm(x)=Fm&#x2212;1(x)+&#x2211;j=1J&#x03B3;jmI(x&#x2208;Rjm)" role="presentation">Algorithm&3:BinomiaDeviance_TreeBoost____________________________________F0(x)=0.5*log(∑Ni=1yi∑Ni=1(1-yi))From=1&to&M&do:&&&&&&&yi~=-[?L(yi,F(xi))?F(xi)]F(x)=Fm-1(x)=yi-11+e(-Fm-1(xi))&&&&&&&{Rjm}J1=J-terminal&node&tree({y?&i,xi}N1)&&&&&&&γjm=∑xi∈Rjmy?&i∑xi∈Rjm(yi-y?&i)*(1-yi+y?&i)&&&&&&&Fm(x)=Fm-1(x)+∑j=1JγjmI(x∈Rjm)Algorithm&3:BinomiaDeviance_TreeBoost____________________________________F0(x)=0.5*log(∑i=1Nyi∑i=1N(1-yi))From=1&to&M&do:&&&&&&&yi~=-[?L(yi,F(xi))?F(xi)]F(x)=Fm-1(x)=yi-11+e(-Fm-1(xi))&&&&&&&{Rjm}1J=J-terminal&node&tree({y~i,xi}1N)&&&&&&&γjm=∑xi∈Rjmy~i∑xi∈Rjm(yi-y~i)*(1-yi+y~i)&&&&&&&Fm(x)=Fm-1(x)+∑j=1JγjmI(x∈Rjm)
算法3就是GBDT用于分类任务时,loss funcion选用logloss的算法流程。
可以看到,和回归任务是一样的,并没有什么特殊的处理环节。
(其实在sklearn源码里面,虽然回归任务的模型定义是GradientBoostingRegressor()而分类任务是GradientBoostingClassifier(),但是这两者区分开来是为了方便用户使用,最终两者都是共同继承BaseGradientBoosting(),算法3这些流程都是在BaseGradientBoosting()完成的,GradientBoostingRegressor()、GradientBoostingClassifier()只是完成一些学习器参数配置的任务)
下面同样以一个简单的数据集来大致的介绍一下GBDT的过程。
<span class="MathJax" id="MathJax-Element-64-Frame" tabindex="0" data-mathml="xi" role="presentation" style="position:">xixi
<span class="MathJax" id="MathJax-Element-65-Frame" tabindex="0" data-mathml="yi" role="presentation" style="position:">yiyi
参数配置:
1. 以logloss为损失函数
2. 以MSE为分裂准则
3. 树的深度为1
4. 学习率为0.1
算法3的第一步,初始化。
<span class="MathJax" id="MathJax-Element-66-Frame" tabindex="0" data-mathml="F0(x)=log(&#x2211;i=1Nyi&#x2211;i=1N(1&#x2212;yi))=log(46)=&#x2212;0.4054" role="presentation" style="position:">F0(x)=log(∑Ni=1yi∑Ni=1(1-yi))=log(46)=-0.4054F0(x)=log(∑i=1Nyi∑i=1N(1-yi))=log(46)=-0.4054
拟合第一颗树(<span class="MathJax" id="MathJax-Element-67-Frame" tabindex="0" data-mathml="m=1" role="presentation" style="position:">m=1m=1)
计算负梯度值:
<span class="MathJax" id="MathJax-Element-68-Frame" tabindex="0" data-mathml="yi&#x007E;=&#x2212;[&#x2202;L(yi,F(xi))&#x2202;F(xi)]F(x)=Fm&#x2212;1(x)=yi&#x2212;11+e(&#x2212;Fm&#x2212;1(xi))=yi&#x2212;11+e(&#x2212;F0(xi))" role="presentation" style="position:">yi~=-[?L(yi,F(xi))?F(xi)]F(x)=Fm-1(x)=yi-11+e(-Fm-1(xi))=yi-11+e(-F0(xi))yi~=-[?L(yi,F(xi))?F(xi)]F(x)=Fm-1(x)=yi-11+e(-Fm-1(xi))=yi-11+e(-F0(xi))
比如计算第一个样本(<span class="MathJax" id="MathJax-Element-69-Frame" tabindex="0" data-mathml="i=1" role="presentation" style="position:">i=1i=1)有:
<span class="MathJax" id="MathJax-Element-70-Frame" tabindex="0" data-mathml="y1&#x007E;=0&#x2212;11+e(0.4054)=&#x2212;0.400" role="presentation" style="position:">y1~=0-11+e(0.4054)=-0.400y1~=0-11+e(0.4054)=-0.400
同样地,其他计算后如下表:
<span class="MathJax" id="MathJax-Element-71-Frame" tabindex="0" data-mathml="xi" role="presentation" style="position:">xixi
<span class="MathJax" id="MathJax-Element-72-Frame" tabindex="0" data-mathml="y&#x007E;i" role="presentation" style="position:">y?&iy~i
接着,我们需要以<span class="MathJax" id="MathJax-Element-73-Frame" tabindex="0" data-mathml="y&#x007E;i" role="presentation" style="position:">y?&iy~i为目标,拟合一颗树。
拟合树的过程上篇文章已经详细介绍了,这里就不再累述了。拟合完后结果如下:
可以得出建好树之后叶子节点的区域:
<span class="MathJax" id="MathJax-Element-74-Frame" tabindex="0" data-mathml="R11" role="presentation" style="position:">R11R11为<span class="MathJax" id="MathJax-Element-75-Frame" tabindex="0" data-mathml="xi&=8" role="presentation" style="position:">xi&=8xi&=8,<span class="MathJax" id="MathJax-Element-76-Frame" tabindex="0" data-mathml="R21" role="presentation" style="position:">R21R21为<span class="MathJax" id="MathJax-Element-77-Frame" tabindex="0" data-mathml="xi&8" role="presentation" style="position:">xi&8xi&8
下面计算可以叶子节点的值<span class="MathJax" id="MathJax-Element-78-Frame" tabindex="0" data-mathml="&#x03B3;jm" role="presentation" style="position:">γjmγjm
由公式:<span class="MathJax" id="MathJax-Element-79-Frame" tabindex="0" data-mathml="&#x03B3;jm=&#x2211;xi&#x2208;Rjmy&#x007E;i&#x2211;xi&#x2208;Rjm(yi&#x2212;y&#x007E;i)&#x2217;(1&#x2212;yi+y&#x007E;i)" role="presentation" style="position:">γjm=∑xi∈Rjmy?&i∑xi∈Rjm(yi-y?&i)*(1-yi+y?&i)γjm=∑xi∈Rjmy~i∑xi∈Rjm(yi-y~i)*(1-yi+y~i)
对于区域<span class="MathJax" id="MathJax-Element-80-Frame" tabindex="0" data-mathml="R11" role="presentation" style="position:">R11R11有如下:
<span class="MathJax" id="MathJax-Element-81-Frame" tabindex="0" data-mathml="&#x2211;xi&#x2208;R11y&#x007E;i=(y&#x007E;1+y&#x007E;2+y&#x007E;3+y&#x007E;4+y&#x007E;5+y&#x007E;6+y&#x007E;7+y&#x007E;8)=&#x2212;1.2" role="presentation" style="position:">∑xi∈R11y?&i=(y?&1+y?&2+y?&3+y?&4+y?&5+y?&6+y?&7+y?&8)=-1.2∑xi∈R11y~i=(y~1+y~2+y~3+y~4+y~5+y~6+y~7+y~8)=-1.2
<span class="MathJax" id="MathJax-Element-82-Frame" tabindex="0" data-mathml="&#x2211;xi&#x2208;R11(yi&#x2212;y&#x007E;i)&#x2217;(1&#x2212;yi+y&#x007E;i)=(y1&#x2212;y&#x007E;1)&#x2217;(1&#x2212;y1+y&#x007E;1)+(y2&#x2212;y&#x007E;2)&#x2217;(1&#x2212;y2+y&#x007E;2)+(y3&#x2212;y&#x007E;3)&#x2217;(1&#x2212;y3+y&#x007E;3)+(y4&#x2212;y&#x007E;4)&#x2217;(1&#x2212;y4+y&#x007E;4)+(y5&#x2212;y&#x007E;5)&#x2217;(1&#x2212;y5+y&#x007E;5)+(y6&#x2212;y&#x007E;6)&#x2217;(1&#x2212;y6+y&#x007E;6)+(y7&#x2212;y&#x007E;7)&#x2217;(1&#x2212;y7+y&#x007E;7)+(y8&#x2212;y&#x007E;8)&#x2217;(1&#x2212;y8+y&#x007E;8)=1.92" role="presentation" style="position:">∑xi∈R11(yi-y?&i)*(1-yi+y?&i)=(y1-y?&1)*(1-y1+y?&1)+(y2-y?&2)*(1-y2+y?&2)+(y3-y?&3)*(1-y3+y?&3)+(y4-y?&4)*(1-y4+y?&4)+(y5-y?&5)*(1-y5+y?&5)+(y6-y?&6)*(1-y6+y?&6)+(y7-y?&7)*(1-y7+y?&7)+(y8-y?&8)*(1-y8+y?&8)=1.92∑xi∈R11(yi-y~i)*(1-yi+y~i)=(y1-y~1)*(1-y1+y~1)+(y2-y~2)*(1-y2+y~2)+(y3-y~3)*(1-y3+y~3)+(y4-y~4)*(1-y4+y~4)+(y5-y~5)*(1-y5+y~5)+(y6-y~6)*(1-y6+y~6)+(y7-y~7)*(1-y7+y~7)+(y8-y~8)*(1-y8+y~8)=1.92
对于区域<span class="MathJax" id="MathJax-Element-83-Frame" tabindex="0" data-mathml="R21" role="presentation" style="position:">R21R21有如下:
<span class="MathJax" id="MathJax-Element-84-Frame" tabindex="0" data-mathml="&#x2211;xi&#x2208;R21y&#x007E;i=(y&#x007E;9+y&#x007E;10)=1.2" role="presentation" style="position:">∑xi∈R21y?&i=(y?&9+y?&10)=1.2∑xi∈R21y~i=(y~9+y~10)=1.2
<span class="MathJax" id="MathJax-Element-85-Frame" tabindex="0" data-mathml="&#x2211;xi&#x2208;R21(yi&#x2212;y&#x007E;i)&#x2217;(1&#x2212;yi+y&#x007E;i)=(y9&#x2212;y&#x007E;9)&#x2217;(1&#x2212;y9+y&#x007E;9)+(y10&#x2212;y&#x007E;10)&#x2217;(1&#x2212;y10+y&#x007E;10)=0.48" role="presentation" style="position:">∑xi∈R21(yi-y?&i)*(1-yi+y?&i)=(y9-y?&9)*(1-y9+y?&9)+(y10-y?&10)*(1-y10+y?&10)=0.48∑xi∈R21(yi-y~i)*(1-yi+y~i)=(y9-y~9)*(1-y9+y~9)+(y10-y~10)*(1-y10+y~10)=0.48
故最后可以得到两个叶子节点的值:
<span class="MathJax" id="MathJax-Element-86-Frame" tabindex="0" data-mathml="&#x03B3;11=&#x2212;1.21.92=&#x2212;0.625" role="presentation" style="position:">γ11=-1.21.92=-0.625γ11=-1.21.92=-0.625、<span class="MathJax" id="MathJax-Element-87-Frame" tabindex="0" data-mathml="&#x03B3;21=1.20.480=2.5" role="presentation" style="position:">γ21=1.20.480=2.5γ21=1.20.480=2.5
最后通过<span class="MathJax" id="MathJax-Element-88-Frame" tabindex="0" data-mathml="Fm(x)=Fm&#x2212;1(x)+&#x2211;j=1J&#x03B3;jmI(x&#x2208;Rjm)" role="presentation" style="position:">Fm(x)=Fm-1(x)+∑Jj=1γjmI(x∈Rjm)Fm(x)=Fm-1(x)+∑j=1JγjmI(x∈Rjm)更新<span class="MathJax" id="MathJax-Element-89-Frame" tabindex="0" data-mathml="F1(x)" role="presentation" style="position:">F1(x)F1(x),需要注意的是,这里同样也用shrinkage,即乘一个学习率<span class="MathJax" id="MathJax-Element-90-Frame" tabindex="0" data-mathml="&#x03B7;" role="presentation" style="position:">ηη,具体表现为:
<span class="MathJax" id="MathJax-Element-91-Frame" tabindex="0" data-mathml="Fm(x)=Fm&#x2212;1(x)+&#x03B7;&#x2217;&#x2211;j=1J&#x03B3;jmI(x&#x2208;Rjm)" role="presentation" style="position:">Fm(x)=Fm-1(x)+η*∑Jj=1γjmI(x∈Rjm)Fm(x)=Fm-1(x)+η*∑j=1JγjmI(x∈Rjm)。
以计算<span class="MathJax" id="MathJax-Element-92-Frame" tabindex="0" data-mathml="x1" role="presentation" style="position:">x1x1为例:
<span class="MathJax" id="MathJax-Element-93-Frame" tabindex="0" data-mathml="F1(x1)=F0(x1)+0.1&#x2217;(&#x2212;0.625)=&#x2212;0.4054&#x2212;0.0625=&#x2212;0.4679" role="presentation" style="position:">F1(x1)=F0(x1)+0.1*(-0.625)=-0.4054-0.0625=-0.4679F1(x1)=F0(x1)+0.1*(-0.625)=-0.4054-0.0625=-0.4679
其他计算完毕后如下表供参考:
<span class="MathJax" id="MathJax-Element-94-Frame" tabindex="0" data-mathml="xi" role="presentation" style="position:">xixi
<span class="MathJax" id="MathJax-Element-95-Frame" tabindex="0" data-mathml="F1(xi)" role="presentation" style="position:">F1(xi)F1(xi)
至此,第一颗树已经训练完成。可以再次看到其训练过程和回归基本没有区别。
下面简单提一下拟合第二颗树(<span class="MathJax" id="MathJax-Element-96-Frame" tabindex="0" data-mathml="m=2)" role="presentation" style="position:">m=2)m=2)
计算负梯度值:
比如对于<span class="MathJax" id="MathJax-Element-97-Frame" tabindex="0" data-mathml="x1" role="presentation" style="position:">x1x1有:
=&<span class="MathJax" id="MathJax-Element-98-Frame" tabindex="0" data-mathml="y&#x007E;1=y1&#x2212;11+e(&#x2212;F1(x1))=0&#x2212;0.38509=&#x2212;0.38509" role="presentation" style="position:">y?&1=y1-11+e(-F1(x1))=0-0.38509=-0.38509y~1=y1-11+e(-F1(x1))=0-0.38509=-0.38509
其他同理,可得下表:
<span class="MathJax" id="MathJax-Element-99-Frame" tabindex="0" data-mathml="xi" role="presentation" style="position:">xixi
<span class="MathJax" id="MathJax-Element-100-Frame" tabindex="0" data-mathml="y&#x007E;i" role="presentation" style="position:">y?&iy~i
之后也是以新的<span class="MathJax" id="MathJax-Element-101-Frame" tabindex="0" data-mathml="y&#x007E;i" role="presentation" style="position:">y?&iy~i为目标拟合一颗回归树后计算叶子节点的区间和叶子节点的值。
当只有2颗树的时候,其预测过程也是和下面这个图一样
相比于回归任务,分类任务需把要最后累加的结果<span class="MathJax" id="MathJax-Element-102-Frame" tabindex="0" data-mathml="Fm(x)" role="presentation" style="position:">Fm(x)Fm(x)转成概率。(其实<span class="MathJax" id="MathJax-Element-103-Frame" tabindex="0" data-mathml="Fm(x)" role="presentation" style="position:">Fm(x)Fm(x)可以理解成一个得分)。具体来说:
对于采用logloss作为损失函数的情况下,<span class="MathJax" id="MathJax-Element-104-Frame" tabindex="0" data-mathml="pi=11+e(&#x2212;Fm(xi))" role="presentation" style="position:">pi=11+e(-Fm(xi))pi=11+e(-Fm(xi))。
对于采用指数损失作为损失函数的情况下,<span class="MathJax" id="MathJax-Element-105-Frame" tabindex="0" data-mathml="pi=11+e(&#x2212;2Fm(xi))" role="presentation" style="position:">pi=11+e(-2Fm(xi))pi=11+e(-2Fm(xi))。
当然这里的<span class="MathJax" id="MathJax-Element-106-Frame" tabindex="0" data-mathml="pi" role="presentation" style="position:">pipi指的是正样本的概率。
这里再详细一点,比如对于上面例子,当我们拟合完第二颗树后,计算<span class="MathJax" id="MathJax-Element-107-Frame" tabindex="0" data-mathml="F2(x)" role="presentation" style="position:">F2(x)F2(x)可有有下表:
<span class="MathJax" id="MathJax-Element-108-Frame" tabindex="0" data-mathml="xi" role="presentation" style="position:">xixi
<span class="MathJax" id="MathJax-Element-109-Frame" tabindex="0" data-mathml="F2(xi)" role="presentation" style="position:">F2(xi)F2(xi)
此时计算相应的概率值有:
<span class="MathJax" id="MathJax-Element-110-Frame" tabindex="0" data-mathml="F2(x)" role="presentation" style="position:">F2(x)F2(x)可有有下表:
<span class="MathJax" id="MathJax-Element-111-Frame" tabindex="0" data-mathml="xi" role="presentation" style="position:">xixi
<span class="MathJax" id="MathJax-Element-112-Frame" tabindex="0" data-mathml="pi" role="presentation" style="position:">pipi
(表中的概率为正样本的概率,即<span class="MathJax" id="MathJax-Element-113-Frame" tabindex="0" data-mathml="yi=1" role="presentation" style="position:">yi=1yi=1的概率)
Sklearn源码简单分析
写在前面:Sklearn源码分析后面有时间有添加一些内容,下面先简单了解GDBT分类的核心代码。
当loss function选用logloss时,对应的是sklearn里面的loss=’deviance’。
计算负梯度、初始化、更新叶子节点、转成概率都在一个名叫BinomialDeviance()的类中。
class BinomialDeviance(ClassificationLossFunction):
"""Binomial deviance loss function for binary classification.
Binary classificati here, we only need to
fit one tree instead of ``n_classes`` trees.
def __init__(self, n_classes):
if n_classes != 2:
raise ValueError("{0:s} requires 2 classes.".format(
self.__class__.__name__))
super(BinomialDeviance, self).__init__(1)
def init_estimator(self):
return LogOddsEstimator()
def __call__(self, y, pred, sample_weight=None):
"""Compute the deviance (= 2 * negative log-likelihood). """
pred = pred.ravel()
if sample_weight is None:
return -2.0 * np.mean((y * pred) - np.logaddexp(0.0, pred))
return (-2.0 / sample_weight.sum() *
np.sum(sample_weight * ((y * pred) - np.logaddexp(0.0, pred))))
def negative_gradient(self, y, pred, **kargs):
"""Compute the residual (= negative gradient). """
return y - expit(pred.ravel())
def _update_terminal_region(self, tree, terminal_regions, leaf, X, y,
residual, pred, sample_weight):
"""Make a single Newton-Raphson step.
our node estimate is given by:
sum(w * (y - prob)) / sum(w * prob * (1 - prob))
we take advantage that: y - prob = residual
terminal_region = np.where(terminal_regions == leaf)[0]
residual = residual.take(terminal_region, axis=0)
y = y.take(terminal_region, axis=0)
sample_weight = sample_weight.take(terminal_region, axis=0)
numerator = np.sum(sample_weight * residual)
denominator = np.sum(sample_weight * (y - residual) * (1 - y + residual))
if abs(denominator) & 1e-150:
tree.value[leaf, 0, 0] = 0.0
tree.value[leaf, 0, 0] = numerator / denominator
def _score_to_proba(self, score):
proba = np.ones((score.shape[0], 2), dtype=np.float64)
proba[:, 1] = expit(score.ravel())
proba[:, 0] -= proba[:, 1]
return proba
def _score_to_decision(self, score):
proba = self._score_to_proba(score)
return np.argmax(proba, axis=1)
下面这是用于计算负梯度值。注意的函数expit就是<span class="MathJax" id="MathJax-Element-114-Frame" tabindex="0" data-mathml="11+e&#x2212;x" role="presentation" style="position:">11+e-x11+e-x
代码中的y_pred或者pred表达的就是<span class="MathJax" id="MathJax-Element-115-Frame" tabindex="0" data-mathml="Fm&#x2212;1(x)" role="presentation" style="position:">Fm-1(x)Fm-1(x)
def negative_gradient(self, y, pred, **kargs):
"""Compute the residual (= negative gradient). """
return y - expit(pred.ravel())
更新叶子节点,关键在于计算numerator和denominator。
另外代码里的residual代表的是负梯度值。
def _update_terminal_region(self, tree, terminal_regions, leaf, X, y,
residual, pred, sample_weight):
"""Make a single Newton-Raphson step.
our node estimate is given by:
sum(w * (y - prob)) / sum(w * prob * (1 - prob))
we take advantage that: y - prob = residual
terminal_region = np.where(terminal_regions == leaf)[0]
residual = residual.take(terminal_region, axis=0)
y = y.take(terminal_region, axis=0)
sample_weight = sample_weight.take(terminal_region, axis=0)
numerator = np.sum(sample_weight * residual)
denominator = np.sum(sample_weight * (y - residual) * (1 - y + residual))
if abs(denominator) & 1e-150:
tree.value[leaf, 0, 0] = 0.0
tree.value[leaf, 0, 0] = numerator / denominator
初始化的类:
class LogOddsEstimator(object):
"""An estimator predicting the log odds ratio."""
scale = 1.0
def fit(self, X, y, sample_weight=None):
if sample_weight is None:
pos = np.sum(y)
neg = y.shape[0] - pos
pos = np.sum(sample_weight * y)
neg = np.sum(sample_weight * (1 - y))
if neg == 0 or pos == 0:
raise ValueError('y contains non binary labels.')
self.prior = self.scale * np.log(pos / neg)
def predict(self, X):
check_is_fitted(self, 'prior')
y = np.empty((X.shape[0], 1), dtype=np.float64)
y.fill(self.prior)
其中,下面这个用于初始化,可以看到有一个因子self.scale,这是由于在Sklearn里提供两种loss function用于分类,一种是logloss,一种是指数损失,两者的初始化仅仅只是在系数上不同,前者是1.0,后者是0.5。
def fit(self, X, y, sample_weight=None):
if sample_weight is None:
pos = np.sum(y)
neg = y.shape[0] - pos
pos = np.sum(sample_weight * y)
neg = np.sum(sample_weight * (1 - y))
if neg == 0 or pos == 0:
raise ValueError('y contains non binary labels.')
self.prior = self.scale * np.log(pos / neg)
最后是转化成概率,这里有个细节,就是正样本的概率是放在第2列(从1数起)。
def _score_to_proba(self, score):
proba = np.ones((score.shape[0], 2), dtype=np.float64)
proba[:, 1] = expit(score.ravel())
proba[:, 0] -= proba[:, 1]
return proba
至此,GBDT用于回归和分类的两种情况都已经说明完毕,欠缺的可能是源码部分说的不够深入,由于最近时间的关系没办法做到太深入,所以后面找时间会把代码再深入的分析后补充在这。
对于多分类问题也需要单独讨论详细请看。
(各种loss function的推导结果)
(本文主要参考的超级著名论文 greedy function approximation: a gradient boosting machine)
sklearn与GBDT入门案例
GBDT概念自行网上搜索下,下面入门调用sklearn包中的GBDT
SCIKIT-LEARN是一个基于Python/numpy/scipy的机器学习库
这段代码展示了一个简单的...
SCIKIT-LEARN与GBDT使用案例
安装SCIKIT-LEARN是一个基于python/numpy/scipy的机器学习库
windows下最简单的安装方式是使用winpython进行安装
WinPython地址GBDT使用这段代码...
sklearn:使用GBDT选择特征
(1)如何在numpy数组中选取若干列或者行?
&&&import numpy as np
&&&tmp_a = np.array([[1,1], [0.4, 4], [1., 0.9]]) ...
sklearn:GBDT
一、GBDT分类
(1)模型参数初始化:
from sklearn.ensemble import GradientBoostingClassifier
gbdt = GradientBoostin...
Sklearn-GBDT(GradientBoostingDecisonTree)梯度提升树
GBDT类库概述
GBDT有很多简称,有GBT(Gradient
Boosting Tree), GTB(Gradient Tree Boosting
), GBRT(Gradient Boosti...
参考:http://www.cnblogs.com/pinard/p/6143927.html
一、GBDT类库概述
在scikit-learn中,GradientBoosti...
GBDT原理与Sklearn源码分析-回归篇
本文将非常详细的介绍GBDT(Gradient Boosting Decision Tree)的原理以及Sklearn里面具体是如何实现一个GBDT的。本次内容将分为两篇文章,一篇是GBDT...
带你搞懂朴素贝叶斯算法原理
一、朴素贝叶斯是什么,怎么用?
贝叶斯定理:朴素贝叶斯定理体现了后验概率 P(y|x)P(y|x)P(y|x) 、先验概率 P(y)P(y)P(y) 、条件概率 P(x|y)P(x|y)P(x...
没有更多推荐了,

我要回帖

更多关于 gbdt分类回归区别 的文章

 

随机推荐