GBDT算法原理以及实例理解

该内容完全转载自同名CSDN博客(http://blog.csdn.net/zpalyq110/article/details/79527653)

GitHub
简书
CSDN
写在前面: 去年学习GBDT之初,为了加强对算法的理解,整理了一篇笔记形式的文章,发出去之后发现阅读量越来越多,渐渐也有了评论,评论中大多指出来了笔者理解或者编辑的错误,故重新编辑一版文章,内容更加翔实,并且在GitHub上实现了和本文一致的GBDT简易版(包括回归、二分类、多分类以及可视化),供大家交流探讨。感谢各位的点赞和评论,希望继续指出错误

Github:
https://github.com/Freemanzxp/GBDT_Simple_Tutorial

简介:
GBDT 的全称是 Gradient Boosting Decision Tree,梯度提升树,在传统机器学习算法中,GBDT算的上TOP3的算法。想要理解GBDT的真正意义,那就必须理解GBDT中的Gradient Boosting 和Decision Tree分别是什么?

1. Decision Tree:CART回归树

首先,GBDT使用的决策树是CART回归树,无论是处理回归问题还是二分类以及多分类,GBDT使用的决策树通通都是都是CART回归树。为什么不用CART分类树呢?因为GBDT每次迭代要拟合的是梯度值,是连续值所以要用回归树。

对于回归树算法来说最重要的是寻找最佳的划分点,那么回归树中的可划分点包含了所有特征的所有可取的值。在分类树中最佳划分点的判别标准是熵或者基尼系数,都是用纯度来衡量的,但是在回归树中的样本标签是连续数值,所以再使用熵之类的指标不再合适,取而代之的是平方误差,它能很好的评判拟合程度。

回归树生成算法:
输入: 训练数据集 D
输出: 回归树 f(x)
在训练数据集所在的输入空间中,递归的将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:

(1) 选择最优切分变量 j 与切分点 s, 求解:

\min_{j,s}[\min_{c_1}\sum_{x_i \in R_1(j, s)} (y_i-c_1)^2+\min_{c_2}\sum_{x_i \in R_2(j, s)} (y_i-c_2)^2]

遍历变量 j,对固定的切分变量j扫描切分点 s,选择使得上式达到最小值的对 (j,s).简要解释一下上述公式:中括号里面的公式是求出每个特征变量在哪一个划分点时损失函数最小,最外面的 \min 是在所有特征值,求得使损失函数全局最小的特征及其切分点(j^*, s^*);

(2) 用选定的对 (j,s) 划分区域并决定相应的输出值:

R_1(j, s)=\{x|x^{(j)}\leq s\},R_2(j, s)=\{x|x^{(j)} > s\}

\hat {c_m}=\frac{1}{N}\sum_{x_1 \in R_m(j, s)}y_i, x \in R_m, m=1,2

求划分区域的输出值就是将该区域的所有样本的输出值求平均。

(3)继续对两个子区域调用步骤(1)和(2),直至满足停止条件。

(4)将输入空间划分为M个区域R_1, R_2...R_M,得到决策树
f(x)=\sum_{m=1}^M \hat{c_m}I(x \in R_m)

2. Gradient Boosting:拟合负梯度

梯度提升树(Grandient Boosting)是提升树(Boosting Tree)的一种改进算法,所以在讲梯度提升树之前先来说一下提升树。

先来个通俗理解:假如有个人30岁,我们首先用20岁去拟合,发现损失有10岁,这时我们用6岁去拟合剩下的损失,发现差距还有4岁,第三轮我们用3岁拟合剩下的差距,差距就只有一岁了。如果我们的迭代轮数还没有完,可以继续迭代下面,每一轮迭代,拟合的岁数误差都会减小。最后将每次拟合的岁数加起来便是模型输出的结果。

提升树算法:

(1) 初始化 f_0(x)=0

(2) 对m=1, 2...M

(a)计算残差
r_{mi}=y_i-f_{m-1}(x_i), i=1, 2,...,M

(b) 拟合残差 r_{mi} 学习一个回归树,得到 h_m(x)

(c) 更新 f_m(x)=f_{m-1}(x)+h_m(x)

(3)得到回归树
f_{M}(x)=\sum_{m=1}^Mh_m(x)

上面伪代码中的残差是什么?

在提升树算法中,假设我们前一轮迭代得到的强学习器是
f_{t-1}(x)
损失函数是

L(y, f_{t-1}(x))

我们本轮迭代的目标是找到一个弱学习器
h_{t}(x)

当采用平方损失函数时

\begin{aligned} & L(y, f_{t-1}(x)+h_t(x)) \\ & = (y - f_{t-1}(x) - h_t(x))^2 \\ & =(r - h_t(x))^2 \\ \end{aligned}

这里,
r = y - f_{t-1}(x)

是当前模型拟合数据的残差(residual)。所以,对于提升树来说只需要简单地拟合当前模型的残差。

回到我们上面讲的那个通俗易懂的例子中,第一次迭代的残差是10岁,第二次残差4岁...

当损失函数是平方损失和指数损失函数时,梯度提升树每一步优化是很简单的,但是对于一般损失函数而言,往往每一步优化起来不那么容易,针对这一问题,Freidman提出了梯度提升树算法,这是利用最速下降的近似方法,其关键是利用损失函数的负梯度作为提升树算法中的残差的近似值。

那么负梯度长什么样呢?

第t轮的第i个样本的损失函数的负梯度为:

-[\frac{\partial {L(y, f(x_i))}}{\partial {f(x_i)}}]_{f(x)=f_{t-1}(x)}

此时不同的损失函数将会得到不同的负梯度,如果选择平方损失

L(y, f(x_i))=\frac{1}{2}(y-f(x_i))^2

负梯度为
-[\frac{\partial {L(y, f(x_i))}}{\partial {f(x_i)}}]_{f(x)=f_{t-1}(x)}=-[\frac{\partial \frac{1}{2}(y-f(x_i))^2}{\partial {f(x_i)}}]_{f(x)=f_{t-1}(x)}=y-f(x_i)

此时我们发现GBDT的负梯度就是残差,所以说对于回归问题,我们要拟合的就是残差。

那么对于分类问题呢?二分类和多分类的损失函数都是log loss,本文以回归问题为例进行讲解。

3. GBDT算法原理

上面两节分别将Decision Tree和Gradient Boosting介绍完了,下面将这两部分组合在一起就是我们的GBDT了。

GBDT算法:
(1)初始化弱学习器
f_0(x)=\arg \min_{c}\sum_{i=1}^{N}L(y_i, c)

后面有证明,党委平方损失时,f_0(x)=\frac{\sum_{i=1}^N y_i}{N}

(2) 对m=1,2,…,M有:

(a)对每个样本i=1,2,…,N,计算负梯度,即残差

r_{im}=-[\frac{\partial{L(y_i, f(x_i))}}{\partial f(x_i)}]_{f(x)=f_{m-1}(x)}

(b)将上步得到的残差作为样本新的真实值,并将数据 (x_i, x_im), i=1, 2,...,N 作为下棵树的训练数据,得到一颗新的回归树 f_m(x),其对应的叶子节点区域为 R_jm, j=1, 2,...,J。其中J为回归树t的叶子节点的个数。

(c)对叶子区域 j =1,2,..J 计算最佳拟合值
\gamma_{jm}=\arg \min_{\gamma}\sum_{x_i \in R_{jm}}L(y_i, f_{m-1}(x_i)+\gamma) (对 \gamma求导并令导数为0即可求得)
(d)更新强学习器
f_m{x}=f_{m-1}(x)+\sum_{j=1}^{J}\gamma_{jm}I(x \in R_{jm})

(3)得到最终学习器

f(x)=f_M{x}=f_{0}(x)+\sum_{m=1}^{M}\sum_{j=1}^{J}\gamma_{jm}I(x \in R_{jm})

4. 实例详解

**==本人用python以及pandas库实现GBDT的简易版本,在下面的例子中用到的数据都在github可以找到,大家可以结合代码和下面的例子进行理解,欢迎star~== **

Github:https://github.com/Freemanzxp/GBDT_Simple_Tutorial

数据介绍:

如下表所示:一组数据,特征为年龄、体重,身高为标签值。共有5条数据,前四条为训练样本,最后一条为要预测的样本。

[图片上传失败...(image-3f17ae-1556349610350)]

训练阶段:

参数设置:

  • 学习率:learning_rate=0.1

  • 迭代次数:n_trees=5

  • 树的深度:max_depth=3

1.初始化弱学习器:

f_0(x)=arg min_c\sum_{i=1}^N L(y_i, c)

损失函数为平方损失,因为平方损失函数是一个凸函数,直接求导,倒数等于零,得到 c

\sum_{i=1}^N\frac{\partial {L(y_i, c)}}{\partial c}=\sum_{i=1}^N \frac{\partial {\frac{1}{2}(y_i-c)^2}}{\partial c}=\sum_{i=1}^N(c - y_i)

令导数等于0

\sum_{i=1}^N(c - y_i)=c - \sum_{i=1}^N y_i = 0

c = (\sum_{i=1}^N y_i)/N

所以初始化时,c 取值为所有训练样本标签值的均值。c=(1.1+1.3+1.7+1.8)/4=1.475,此时得到初始学习器 f_0(x)

f_0(x)=c=1.475

2.对迭代轮数m=1,2,…,M:

由于我们设置了迭代次数:n_trees=5,这里的M=5。

计算负梯度,根据上文损失函数为平方损失时,负梯度就是残差残差,再直白一点就是 y与上一轮得到的学习器 f_{m-1} 的差值

r_{i1}==-[\frac{\partial{L(y_i, f(x_i))}}{\partial f(x_i)}]_{f(x)=f_{0}(x)}

残差在下表列出

[图片上传失败...(image-397f0d-1556349610350)]

此时将残差作为样本的真实值来训练弱学习器 f_1(x),即下表数据:

[图片上传失败...(image-daae9f-1556349610350)]

接着,寻找回归树的最佳划分节点,遍历每个特征的每个可能取值。从年龄特征的5开始,到体重特征的70结束,分别计算分裂后两组数据的平方损失(Square Error),SE_l左节点平方损失,SE_r 右节点平方损失,找到使平方损失和 SE_{sum}=SE_l+SE_r 最小的那个划分节点,即为最佳划分节点。

例如:以年龄7为划分节点,将小于7的样本划分为到左节点,大于等于7的样本划分为右节点。

左节点包括 x_0,右节点包括样本 x_1,x_2,x_3SE_l=0, SE_r=0.047, SE_{sum}=0.047,所有可能划分情况如下表所示:
[图片上传失败...(image-f7c880-1556349610350)]

以上划分点是的总平方损失最小为0.025有两个划分点:年龄21和体重60,所以随机选一个作为划分点,这里我们选 年龄21
 现在我们的第一棵树长这个样子:

[图片上传失败...(image-461482-1556349610350)]

我们设置的参数中树的深度max_depth=3,现在树的深度只有2,需要再进行一次划分,这次划分要对左右两个节点分别进行划分:

对于左节点,只含有0,1两个样本,根据下表我们选择年龄7划分

[图片上传失败...(image-f8ae62-1556349610350)]

对于右节点,只含有2,3两个样本,根据下表我们选择年龄30划分(也可以选体重70

[图片上传失败...(image-479889-1556349610350)]

现在我们的第一棵树长这个样子:

[图片上传失败...(image-35c0d1-1556349610350)]

此时我们的树深度满足了设置,还需要做一件事情,给这每个叶子节点分别赋一个参数Υ,来拟合残差。

\gamma_{j1}=\arg \min_{\gamma}\sum_{x_i \in R_{j1}}L(y_i, f_{0}(x_i)+\gamma)

这里其实和上面初始化学习器是一个道理,平方损失求导,令导数等于零,化简之后得到每个叶子节点的参数 Υ,其实就是标签值的均值。这个地方的标签值不是原始的 y,而是本轮要拟合的标残差 y-f_0(x)

根据上述划分结果,为了方便表示,规定从左到右为第1,2,3,4个叶子结点

[图片上传失败...(image-c81c59-1556349610350)]

此时的树长这个样子:

[图片上传失败...(image-31ce0f-1556349610350)]

此时可更新强学习器,需要用到参数学习率:learning_rate=0.1,用lr表示。

f_1(x)=f_0(x)+ lr * \sum_{j=1}^4\gamma_{j1}I(x \in R_{j1})

为什么要用学习率呢?这是Shrinkage的思想,如果每次都全部加上(学习率为1)很容易一步学到位导致过拟合。

重复此步骤,直到 m>5 结束,最后生成5棵树。

下面将展示每棵树最终的结构,这些图都是GitHub上的代码生成的,感兴趣的同学可以去一探究竟

Github:https://github.com/Freemanzxp/GBDT_Simple_Tutorial
第一棵树:

[图片上传失败...(image-f67be8-1556349610350)]

第二棵树:
[图片上传失败...(image-ead09a-1556349610350)]

第三棵树:
[图片上传失败...(image-e80379-1556349610350)]

第四棵树:
[图片上传失败...(image-1d91ae-1556349610350)]

第五棵树:
[图片上传失败...(image-28d117-1556349610350)]

**3.得到最后的强学习器: **
f(x)=f_5(x)=f_0(x)+lr*(\sum_{m=1}^5\sum_{j=1}^4\gamma_{jm}I(x \in R_{jm}))

5.预测样本5:

f_0(x)=1.475

f_1(x)在中,样本4的年龄为25,大于划分节点21岁,又小于30岁,所以被预测为0.2250

f_2(x)在中,样本4的…此处省略…所以被预测为0.2025

==为什么是0.2025?这是根据第二颗树得到的,可以GitHub简单运行一下代码==

f_3(x)在中,样本4的…此处省略…所以被预测为0.1823

f_4(x)在中,样本4的…此处省略…所以被预测为0.1640

f_5(x)在中,样本4的…此处省略…所以被预测为0.1476

最终预测结果:
f(x)=1.475+0.1*(0.225+0.2025+0.1823+0.164+0.1476)=1.56714

5. 总结

本文章从GBDT算法的原理到实例详解进行了详细描述,但是目前只写了回归问题,GitHub上的代码也是实现了回归、二分类、多分类以及树的可视化,希望大家继续批评指正,感谢各位的关注,如果对你有用欢迎star。

参考:

  1. 李航 《统计学习方法》

  2. Friedman J H . Greedy Function Approximation: A Gradient Boosting Machine[J]. The Annals of Statistics, 2001, 29(5):1189-1232.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342