赞
踩
本篇文章主要介绍GBDT基本原理以及一些细节性的东西,这些东西更多在面试使用,或者对于二次创新使用,主要内容有以下几个方面:
如果读者对以上各个方面都很熟悉,那么恭喜你已经成功掌握GBDT了。
在正式开讲GBDT之前,我先熟悉一下江湖中传说的集成学习的两个派系,分别是Boosting和Bagging。所谓的集成学习主要是通过学习多个弱学习器结合组合策略组成强学习以达到“多个臭皮匠顶个诸葛亮”的作用。集成学习中最典型的两个代表就是Boosting家族和Bagging家族。Boosting家族最典型的代码属于AdaBoosting以及提升树(典型的GBDT)。Bagging家族最典型的代表是Random Forest(随机森林,以下简称RF)。
以下两张图片可以清楚描述Boosting和Bagging的差异:Boosting中的弱学习器之间存在依赖关系,这也是GBDT为什么不能并行的原因。
Bagging算法
Boosting算法
GBDT基本原理是 通过多轮迭代,每轮迭代产生一个弱分类器(利用cart回归树构建),每个分类器在上一轮分类器的残差基础上进行训练。
数学语言描述:利用前一轮迭代的学习器ft−1(x)ft−1(x)以及损失L(y,ft−1(x))L(y,ft−1(x)) 构建一棵cart回归树弱学习器模型ht(x)ht(x) 使得本次迭代的损失L(y,ft(x))=L(y,ft−1(x)+ht(x))L(y,ft(x))=L(y,ft−1(x)+ht(x)) 最小。
GBDT的思想可以用一个通俗的例子解释,假如有个人30岁,我们首先用20岁去拟合,发现损失有10岁,这时我们用6岁去拟合剩下的损失,发现差距还有4岁,第三轮我们用3岁拟合剩下的差距,差距就只有一岁了。如果我们的迭代轮数还没有完,可以继续迭代下面,每一轮迭代,拟合的岁数误差都会减小。
正如前面提到的GBDT每轮迭代都采用的是损失拟合的方法,但是如何拟合呢?针对这个问题,大牛Freidman提出了用损失函数的负梯度来拟合本轮损失的近似值,进而拟合一个CART回归树。第t轮的第i个样本的损失函数的负梯度表示:
rti=−[∂L(yi,f(xi)))∂f(xi)]f(x)=ft−1(x)rti=−[∂L(yi,f(xi)))∂f(xi)]f(x)=ft−1(x)
利用(xi,rti)(xi,rti)构建一颗cart回归树(具体如何构建请参考决策树),这样就得到了第t颗回归树以及其对应的叶节点区域Rtj,j=1,2,...,JRtj,j=1,2,...,J其中J为叶子节点的个数。然后针对每个叶子节点里面的样本,求出使损失函数最小,拟合叶子节点最好的的值ctjctj
ctj=argmin⏟c∑xi∈RtjL(yi,ft−1(xi)+c)ctj=argmin⏟c∑xi∈RtjL(yi,ft−1(xi)+c)
这样我们就得到本轮的cart回归树拟合函数:
ht(x)=∑j=1JctjI(x∈Rtj)ht(x)=∑j=1JctjI(x∈Rtj)
最终得到本轮的学习器:
ft(x)=ft−1(x)+ht(x)ft(x)=ft−1(x)+ht(x)
这就是通过损失函数的负梯度来拟合误差的办法。
对弱分类器的要求一般是足够简单,并且是低方差和高偏差的。因为训练的过程是通过降低偏差来不断提高最终分类器的精度。
主要优点:
主要缺点:
GBDT正则化有三种办法:
ft(x)=ft−1(x)+ηht(x)ft(x)=ft−1(x)+ηht(x)
其中0<η≤10<η≤1是步长,对于同样的训练集学习效果,较小的ηη意味着需要更多的迭代次数;通常用步长和迭代最大次数一起来决定算法的拟合效果。利用(xi,rti)(xi,rti)来构建cart回归树的时候,GBDT分裂会选取使得误差下降最多(如果cart树采用的是均方差作为损失,那么就是最小均方差)的特征进行分裂,如果这棵树不能拟合好,那么就要通过负梯度计算出新的残差向量来拟合新的cart回归树。对初始cart树可以直接用0,也可以采用样本的均值。既然已经有了分裂规则,那么如何停止继续分裂呢?主要可以通过以下手段进行操作:1、节点分裂时的最小样本数;2、树的最大深度;3、树的最大叶子节点数;4、loss满足约束条件。
前面提到,在构建cart树时使用了损失函数的负梯度,而不是所谓的残差=真值-预测值;实际上是一种更宽广的概念,但是在平方损失的情况下,上面等式是成立的。另外使用损失函数的梯度可以保证损失函数最小值。所以GBDT的梯度提升体现在构建cart树的所需的负梯度阶段,其利用最速下降的近似方法。
特征j的全局重要度通过特征j在单颗树中的重要度的平均值来衡量
J2j^=1M∑m=1MJ2j^(Tm)Jj2^=1M∑m=1MJj2^(Tm)
其中M是树的数量。特征j在单颗树中的重要度的如下
J2j^(T)=∑t=1L−1i2t^I(vt=j)Jj2^(T)=∑t=1L−1it2^I(vt=j)
其中,L为树的叶子节点数量,L−1即为树的非叶子节点数量(构建的树都是具有左右孩子的二叉树)vtvt和节点t相关联的特征,i2t^it2^是节点t分裂之后平方损失的减少值。
GBDT主要是利用残差逼近的方式,这就意味每棵树的值是连续的可叠加的,这一点和回归树输出连续值不谋而合,如果采用分类树,那么残差逼近进行叠加就会使得这种叠加没有意义,比如男+男+女=到底是男是女。这个是GBDT基本原理决定的。
对于机器学习来说,泛化误差可以理解为两部分,分别是偏差(bias)和方差(variance);偏差指的是算法的期望预测与真实预测之间的偏差程度,反应了模型本身的拟合能力;方差度量了同等大小的训练集的变动导致学习性能的变化,刻画了数据扰动所导致的影响。当模型越复杂时,拟合的程度就越高,模型的训练偏差就越小;但此时如果换一组数据可能模型的变化就会很大,即模型的方差很大,所以模型过于复杂的时候会导致过拟合。对于RF来说由于并行训练很多不同的分类器的目的就是降低这个方差(variance)。所以对于每个基分类器来说,目标就是如何降低这个偏差(bias),所以我们会采用深度很深甚至不剪枝的决策树。而对于GBDT来说由于利用的是残差逼近的方式,即在上一轮的基础上更加拟合原数据,所以可以保证偏差(bias),所以对于每个基分类器来说,问题就在于如何选择 variance 更小的分类器,即更简单的分类器,所以我们选择了深度很浅的决策树。
1、计算每个样本的负梯度;
2、分裂挑选最佳特征及其分割点时,对特征计算相应的误差及均值时;
3、更新每个样本的负梯度时;
4、最后预测过程中,每个样本将之前的所有树的结果累加的时候。
相同点:
1、GBDT与RF都是采用多棵树组合作为最终结果;这是两者共同点。
不同点:
1、RF的树可以是回归树也可以是分类树,而GBDT只能是回归树。
2、RF中树是独立的,相互之间不影响,可以并行;而GBDT树之间有依赖,是串行。
3、RF最终的结果是有多棵树表决决定,而GBDT是有多棵树叠加组合最终的结果。
4、RF对异常值不敏感,原因是多棵树表决,而GBDT对异常值比较敏感,原因是当前的错误会延续给下一棵树。
5、RF是通过减少模型的方差来提高性能,而GBDT是减少模型的偏差来提高性能的。(原理之前分析过)
6、RF不需要进行数据预处理,即特征归一化。而GBDT则需要进行特征归一化。
在讲两者的区别时,我们首先要知道XGBoost针对GBDT做了那些改进,首先,XGBoost在计算损失函数的时候,针对损失函数做了一次二阶泰勒展开,具体如下:
L(y,ft(x))=L(y,ft−1(x)+ht(x))≃L(y,ft−1(x))+[∂L(y,ft−1(x))∂ft−1(x)]ht(x)+12[∂2L(y,ft−1(x))∂2ft−1(x)]h2t(x)L(y,ft(x))=L(y,ft−1(x)+ht(x))≃L(y,ft−1(x))+[∂L(y,ft−1(x))∂ft−1(x)]ht(x)+12[∂2L(y,ft−1(x))∂2ft−1(x)]ht2(x)
令g(x)=∂L(y,ft−1(x))∂ft−1(x)g(x)=∂L(y,ft−1(x))∂ft−1(x) , h(x)=∂2L(y,ft−1(x))∂2ft−1(x)h(x)=∂2L(y,ft−1(x))∂2ft−1(x) 那么:
L(y,ft(x))≃L(y,ft−1(x))+g(x)ht(x)+h(x)h2t(x)L(y,ft(x))≃L(y,ft−1(x))+g(x)ht(x)+h(x)ht2(x)
从上式可以看出,因为L(y,ft−1(x))L(y,ft−1(x))是前一次迭代最小损失,对于本次迭代而言,主要是对:
L̃ =g(x)ht(x)+h(x)h2t(x)L~=g(x)ht(x)+h(x)ht2(x)
进行拟合,以达到最小损失的结果,如果加入正则化项那么:
L̃ =g(x)ht(x)+h(x)h2t(x)+Ω(ht(x))L~=g(x)ht(x)+h(x)ht2(x)+Ω(ht(x))
其中
Ω(ht(x))=ηht(x)+12λ∑j=1Jw2tjΩ(ht(x))=ηht(x)+12λ∑j=1Jwtj2
是正则化项,在这里我们可以将ht(x)ht(x)理解成树,它是关于wtjwtj的函数,wtjwtj是参数(权重),通过L̃ L~对wtjwtj求导可以解出wtjwtj。那么节点是如何分裂的呢?有两种办法:
实际上对损失函数的优化就是对第t棵树结构进行分裂,启发式的找到最优树结构的过程。而每次分裂,对应于将属于一个叶结点(初始情况下只有一个叶结点,即根结点)下的训练样本分配到分裂出的两个新叶结点上,每个叶结点上的训练样本都会对应一个模型学出的概率值,而损失函数本身满足样本之间的累加特性,所以,可以通过将分裂前的叶结点上样本的损失函数和与分裂之后的两个新叶结点上的样本的损失函数之和进行对比,从而找到可用的分裂特征以及特征分裂点。
XGBoost的创新
GBDT以cart回归树作为基分类器,XGBoost还支持线性分类器,这个时候XGBoost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
【问】xgboost/gbdt在调参时为什么树的深度很少就能达到很高的精度?
用xgboost/gbdt在在调参的时候把树的最大深度调成6就有很高的精度了。但是用DecisionTree/RandomForest的时候需要把树的深度调到15或更高。用RandomForest所需要的树的深度和DecisionTree一样我能理解,因为它是用bagging的方法把DecisionTree组合在一起,相当于做了多次DecisionTree一样。但是xgboost/gbdt仅仅用梯度上升法就能用6个节点的深度达到很高的预测精度,使我惊讶到怀疑它是黑科技了。请问下xgboost/gbdt是怎么做到的?它的节点和一般的DecisionTree不同吗?
【答】
这是一个非常好的问题,题主对各算法的学习非常细致透彻,问的问题也关系到这两个算法的本质。这个问题其实并不是一个很简单的问题,我尝试用我浅薄的机器学习知识对这个问题进行回答。
一句话的解释,来自周志华老师的机器学习教科书( 机器学习-周志华):Boosting主要关注降低偏差,因此Boosting能基于泛化性能相当弱的学习器构建出很强的集成;Bagging主要关注降低方差,因此它在不剪枝的决策树、神经网络等学习器上效用更为明显。
随机森林(random forest)和GBDT都是属于集成学习(ensemble learning)的范畴。集成学习下有两个重要的策略Bagging和Boosting。
Bagging算法是这样做的:每个分类器都随机从原样本中做有放回的采样,然后分别在这些采样后的样本上训练分类器,然后再把这些分类器组合起来。简单的多数投票一般就可以。其代表算法是随机森林。Boosting的意思是这样,他通过迭代地训练一系列的分类器,每个分类器采用的样本分布都和上一轮的学习结果有关。其代表算法是AdaBoost, GBDT。
其实就机器学习算法来说,其泛化误差可以分解为两部分,偏差(bias)和方差(variance)。这个可由下图的式子导出(这里用到了概率论公式D(X)=E(X^2)-[E(X)]^2)。偏差指的是算法的期望预测与真实预测之间的偏差程度,反应了模型本身的拟合能力;方差度量了同等大小的训练集的变动导致学习性能的变化,刻画了数据扰动所导致的影响。这个有点儿绕,不过你一定知道过拟合。
如下图所示,当模型越复杂时,拟合的程度就越高,模型的训练偏差就越小。但此时如果换一组数据可能模型的变化就会很大,即模型的方差很大。所以模型过于复杂的时候会导致过拟合。
当模型越简单时,即使我们再换一组数据,最后得出的学习器和之前的学习器的差别就不那么大,模型的方差很小。还是因为模型简单,所以偏差会很大。
模型复杂度与偏差方差的关系图
也就是说,当我们训练一个模型时,偏差和方差都得照顾到,漏掉一个都不行。
对于Bagging算法来说,由于我们会并行地训练很多不同的分类器的目的就是降低这个方差(variance) ,因为采用了相互独立的基分类器多了以后,h的值自然就会靠近.所以对于每个基分类器来说,目标就是如何降低这个偏差(bias),所以我们会采用深度很深甚至不剪枝的决策树。
对于Boosting来说,每一步我们都会在上一轮的基础上更加拟合原数据,所以可以保证偏差(bias),所以对于每个基分类器来说,问题就在于如何选择variance更小的分类器,即更简单的分类器,所以我们选择了深度很浅的决策树。
最近引起关注的一个Gradient Boosting算法:xgboost,在计算速度和准确率上,较GBDT有明显的提升。xgboost 的全称是eXtreme Gradient Boosting,它是Gradient Boosting Machine的一个c++实现,作者为正在华盛顿大学研究机器学习的大牛陈天奇 。xgboost最大的特点在于,它能够自动利用CPU的多线程进行并行,同时在算法上加以改进提高了精度。它的处女秀是Kaggle的 希格斯子信号识别竞赛,因为出众的效率与较高的预测准确度在比赛论坛中引起了参赛选手的广泛关注。值得我们在GBDT的基础上对其进一步探索学习。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。