您的位置:首页 > 编程语言 > Python开发

GBDT梯度提升树原理剖析

2017-10-18 11:15 225 查看

GBDT梯度提升树原理剖析

在前面的文章中,我写了ID3及CART决策树的思路,但我们只是将其用来做分类。注意到CART的名字:Classification And Regression Tree,当我们利用其进行回归时,它的名字就变成了回归树。GBDT(Gradient Boosting Decision Tree)就是一种基于回归的预测方法。今天我们来看一看这个算法的原理。

先来熟悉一下回归树的方法。

回想决策树,用于连续变量时,我们在根节点上计算所有变量的某个指标(ID3:信息增益,CART:GINI指数),这个指标的计算需要将该变量下的所有样本值一分为二,如果有M个值不同的样本,我们取出M-1个阈值,遍历所有阈值,计算不同阈值下该变量的指标得分。也就是,遍历所有变量,遍历每一个变量下的所有阈值,从而得到一个最优变量和最优阈值,将该变量作为此时的最优划分属性,即为根节点。随后,根据阈值,将样本划分为二类,然后,在左孩子节点和右孩子节点分别利用上述方法进行迭代,生成一棵决策树。收敛条件为某一节点中的样本全部属于一类,或者变量已经用完但最后一个变量仍无法获得单一类别的样本(这一点通常在连续变量时不成立,因为假如父节点使用了某一属性,并不会禁止在子节点中使用该属性,只不过阈值会改变),或者达到预设条件(比如在sklearn的tree函数中,参数内可以设置树的最大深度max_depth,区分一个内部节点所需要的最小样本数min_samples_split等),在达到预设条件后,如果叶子结点中仍然无法获得单一类别样本,那么此时选用样本中多数样例的类别作为该叶子结点的类别。

相似于决策树,回归树在应用时,也要基于属性及其阈值来不断划分属性,只不过在每一个节点上,都会有一个预测值,这个预测值由该节点上所有样例的标签(输出变量y)的均值决定(以误差平方和为损失函数时)。同样,回归树基于某种指标来选择最优划分属性,由于是回归问题,通常采用最小化平方误差或者均方误差来作为划分指标。具体操作类似决策树:遍历所有变量,遍历每个变量下的分割阈值,找到使得分割完成后左孩子中所有样例对其均值的平方误差和与右孩子中所有样例对其均值平方误差和的和达到最小的变量与变量阈值,利用这两个值,确定本节点,随后对节点上的所有样例进行划分,得到左孩子节点与右孩子节点,继续迭代,直到生成一棵回归树。收敛条件为直到每个叶子结点上的样例的y值唯一(这几乎不可能实现),或者类似于决策树,在达到预设条件后,取叶节点上所有样例的y值均值作为该叶子节点的预测值。

好,我们讲完了回归树,接下来进入正题:Gradient Boosting Decision Tree。看到算法名字中有Boosting,很显然其属于一种集成方法。那么,看字面意思,应该是利用boosting方法,利用每个决策树的残差不断迭代决策树,来实现最后决策。来看细节。

这里引用[5]的例子,很形象。假设训练集中有4个人,ABCD,年龄分别为14、16、24、26,通过购物金额、上网时长、经常到百度知道提问或回答等属性,来预测其年龄。利用传统回归树来训练,得到下图结果:



我们利用GBDT来做这件事,由于数据较少,我们限定叶子结点最多有两个,即每棵树都只有一个分支,限定只学两棵树,可得下图结果:



在左侧树中,AB样本被预测为15岁,实际分别为14、16岁,因而可得残差-1、+1,同理,CD残差为-1、+1岁。有了这个残差,我们将其代替ABCD的年龄,在第二棵树中进行训练,也就是利用x(样本)和y_residual(y的残差)进行训练,得到右侧树。因此,来一个新样本,比如说把训练数据中的A拿过来,14岁,在左侧训练完为15岁,在右侧训练完为-1岁,将其累加,可得预测值14岁。基本思路大概如此。

上述为一个简单的提升树(Boosting Tree)的思路,不断基于模型的残差学习新的回归树,最后将所有回归树累加,得到提升树的模型。接下来我们来看梯度提升树。

定义如下损失函数列表:



对于这些损失函数,Freidman提出:利用最速下降的近似方法,即利用损失函数的负梯度在当前模型的值,作为回归问题中提升树算法的残差的近似值,来拟合出一个回归树。也就是说,梯度提升树是利用损失函数的负梯度在当前模型的值代替前面说的简单提升树中的残差,进而不断训练新的回归树。其算法如下:



将这个算法对照我前面讲的简单提升树,还是很容易理解的。它们不同的地方就在于,损失函数可以有很多种,除了平方损失函数这种每一步优化都很简单的损失函数,对于一般损失函数而言,我们采用其负梯度作为残差的近似来拟合。我们来看算法的初始化部分,估计出使得损失函数最小的γ值,将其赋值给根节点。也就是说,现在的y值都是负梯度,我们选择出合适的γ使得损失函数最小,那么这个γ就代表了根节点所有样例负梯度的一个区域的代表,相对应于简单提升树中的节点均值。如果我们以平方误差损失函数为例,那么该损失函数的负梯度就是我们通常说的残差<
edce
span style="position: absolute; clip: rect(1.869em 1000em 3.256em -0.477em); top: -2.717em; left: 0.003em;">residual=y−ypre。接下来,第2步的(a)中,对于每一个样本,我们计算其负梯度,这里对应于简单提升树中的每个样本的残差,Freidman用负梯度来作为残差的近似,如果是平方损失函数,那负梯度就是我们通常理解的残差。(b)中,利用这些负梯度拟合出回归树,这里的Rj代表了输入空间的一个划分情况,比如训练样本50个,通过CART可以将其划分为M个单元R1、R2、...RM,也可以简单理解为叶子结点的个数,也即回归树的复杂度。(c)步,我们要在这里计算出每个叶子结点的代表,从而使得损失函数最小,这里的代表就是γ的值。随后,(d)步对回归树进行更新。不断迭代M次,得到M棵回归树,将其加和,作为最后的梯度提升树。这就是梯度提升树的主要思路。

在具体操作的时候,比如利用sklearn中的GradientBoostingClassifier()这一梯度提升树框架,还要考虑一个学习率learning_rate的问题。这个学习率代表了一种shrinkage,认为每次走一小步逼近的结果,比每次迈一大步的结果更容易避免过拟合。也就是说,我生成了一棵残差回归树,但是我不完全信任它,我认为它只学到了真相的一部分,因此我累加的时候只累加它的一部分,虽然小,但是我可以多学几棵来弥补这个不足。因此,我的实际迭代公式为fm(x)=fm−1(x)+vhm(x),这里的v就代表了学习率。显然0≤v≤1。于是我们在利用gridsearchcv进行网格调参的时候,可以一起调学习率和树的个数这两个模型参数,其他重要的参数,还有一个损失函数,GradientBoostingClassifier()默认为均方差‘ls’,其他还有绝对损失‘lad’、Huber损失‘huber’和分位数损失‘quatile’等。一般来说,如果数据噪声点不多,可以直接用默认的均方差损失函数,如果噪音点较多的时候,推荐使用抗噪音的‘huber’,如果需要对训练集进行分段预测,可采用‘quatile’损失函数。

参考文献:

[1]Hastie T, Friedman J, Tibshirani R. The Elements of Statistical Learning[M], 2001.

[2]李航. 统计学习方法[M], 2012.

[3]http://blog.csdn.net/cherdw/article/details/54982805

[4]http://www.jianshu.com/p/005a4e6ac775

[5]http://blog.csdn.net/suranxu007/article/details/49910323
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息