树算法系列之四:XGBoost
项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步
1.CART树回顾
在正式讲Xgboost之前,我们先回顾一下CART树。
CART树的生成过程,就是递归构建二叉树的过程,本质上是在某个特征维度对样本空间进行划分。这种空间划分是一种NP Hard问题,因此一般都是用启发式的方式去求解,即求某个特征j的且分点s,使得
minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]\min_ { j , s } \left[ \min _ { c _ { 1 } } \sum _ { x _ { i } \in R _ { 1 } ( j , s ) } \left( y _ { i } - c _ { 1 } \right) ^ { 2 } + \min _ { c _ { 2 } } \sum _ { x _ { i } \in R _ { 2 } ( j , s ) } \left( y _ { i } - c _ { 2 } \right) ^ { 2 } \right]j,smin⎣⎡c1minxi∈R1(j,s)∑(yi−c1)2+c2minxi∈R2(j,s)∑(yi−c2)2⎦⎤
2.XGBoost叶子节点取值推导
XGBoost的大体思路跟GBDT一样,也是通过不断进行特征分裂添加树,来学习一个新的弱学习器,来拟合上次预测结果的残差。
首先来看一下XGBoost的损失函数
L(ϕ)=∑il(yi,y^i)+∑kΩ(fk),其中Ω(fk)=γT+12λ∣∣w∣∣2L ( \phi ) = \sum _ { i } l \left( y _ { i } , \hat { y } _ { i } \right) + \sum _ { k } \Omega \left( f _ { k } \right) ,\quad 其中 \quad \Omega \left( f _ { k } \right) = \gamma T + \frac { 1 } { 2 } \lambda | | w | | ^ { 2 }L(ϕ)=i∑l(yi,y^i)+k∑Ω(fk),其中Ω(fk)=γT+21λ∣∣w∣∣2
很容易可以看出来
∑il(yi,y^i)\sum\limits_i l \left( y _ { i } , \hat { y } _ { i } \right)i∑l(yi,y^i)是代表预测值与真实值的偏差,而∑kΩ(fk)\sum\limits_k \Omega \left( f _ { k } \right)k∑Ω(fk)为正则项。
其中,yiy_iyi为样本的真实值,y^i\hat y_ iy^i为预测值。fkf_kfk表示第k棵树,γ\gammaγ为叶子树的正则项,起到剪枝的作用,T为树的叶子节点个数,www为叶子节点的分数,λ\lambdaλ为叶子节点分数的正则项,起到防止过拟合的作用。
前面树系列的文章提到过很多次,boost系列算法的核心思想是用新生成的树拟合上次预测的残差(对于GBDT来说是上次预测的负梯度方向)。生成第t棵树后,预测值可以写为
y^i=y^it−1+ft(x)\hat y_i = \hat y_i ^{t-1} + f_t(x)y^i=y^it−1+ft(x)
此时,损失函数可以表示为
Lt=∑i=1l(yit,y^it−1)+ft(xi)+Ω(ft)L^t = \sum _{i=1} l ( y _i^t , \hat y_i ^{t-1}) + f_t(x_i) + \Omega (f _t)Lt=i=1∑l(yit,y^it−1)+ft(xi)+Ω(ft)
于是我们要找一个ftf_tft让损失函数最小。对上面的式子进行泰勒展开
L(t)≃∑i=1n[l(yi,y^(t−1))+gift(xi)+12hift2(xi)]+Ω(ft)\mathcal { L } ^ { ( t ) } \simeq \sum _ { i = 1 } ^ { n } \left[ l \left( y _ { i } , \hat { y } ^ { ( t - 1 ) } \right) + g _ { i } f _ { t } \left( \mathbf { x } _ { i } \right) + \frac { 1 } { 2 } h _ { i } f _ { t } ^ { 2 } \left( \mathbf { x } _ { i } \right) \right] + \Omega \left( f _ { t } \right)L(t)≃i=1∑n[l(yi,y^(t−1))+gift(xi)+21hift2(xi)]+Ω(ft)
其中,
gi=∂l(yi,y^it−1)∂y^it−1g_i = \frac{\partial l(y_i, \hat y_i ^{t-1})} {\partial \hat y_i ^{t-1}}gi=∂y^it−1∂l(yi,y^it−1)
hi=∂2l(yi,y^it−1)∂(y^it−1)2h_i = \frac{\partial ^ 2 l(y_i, \hat y_i ^{t-1})} {\partial (\hat y_i ^{t-1})^2}hi=∂(y^it−1)2∂2l(yi,y^it−1)
其中gi,hig_i, h_igi,hi的含义可以按如下方式理解
假设目前已经有t-1棵数,这t-1棵树组成的模型对第i个训练样本有一个预测值y^i\hat y_iy^i,y^i\hat y_iy^i与真实值yiy_iyi肯定有差距,这个差距可以用l(yi,y^i)l(y_i, \hat y_i)l(yi,y^i)这个损失函数来衡量,所以此处的gig_igi与hih_ihi就是对于该损失函数的一阶导和二阶导。(参考文献1)
搞定了前面的损失函数部分,接下来再观察一下正则项
Ω(ft)=γT+12λ∑j=1Twj2\Omega (f_t) = \gamma T + \frac{1}{2} \lambda \sum_{j=1}^T w_j^2Ω(ft)=γT+21λj=1∑Twj2
对于上面的正则项,我们最简单的理解方式为:要使模型尽可能简单,那就是叶子节点的个数T要小,同时叶子节点上的值也尽可能小。
因为前t-1棵树的预测分数与y的残差对目标函数优化不影响, 可以直接去掉,所以损失函数可以写成
L(t)≃∑i=1n[gift(xi)+12hift2(xi)]+Ω(ft)\mathcal { L } ^ { ( t ) } \simeq \sum _ { i = 1 } ^ { n } \left[ g _ { i } f _ { t } \left( \mathbf { x } _ { i } \right) + \frac {1} {2} h_i f _ { t } ^ { 2 } \left( \mathbf { x } _ { i } \right) \right] + \Omega \left( f _ { t } \right)L(t)≃i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)
上面式子的含义是将每个样本的损失函数相加,而每个样本最终都会落到一个叶子节点上,所以我们可以将所有同一个叶子结点的样本重组起来
L^(t)=∑i=1n[gift(xi)+12htft2(xi)]+Ω(ft)=∑i=1n[gift(xi)+12htft2(xi)]+γT+12λ∑j=1Twj2=∑j=1T[(∑i∈Ijgi)wj+12(∑i∈Ijhi+λ)wj2]+γT=12∑j=1T(Hj+λ)(wj+GjHj+λ)2+γT−12∑j=1TGj2Hj+λ\begin{aligned} \hat{ \mathcal { L }}^{(t)} & =\sum_{i=1}^n [g_if_t(x_i) + \frac12h_tf_t^2(x_i)] + \Omega(f_t) \\ & =\sum_{i=1}^n [g_if_t(x_i) + \frac12h_tf_t^2(x_i)] + \gamma T+\frac{1}{2}\lambda\sum\limits_{j=1}^{T}w_j^2 \\ & = \sum\limits_{j=1}^{T} [(\sum\limits_{i\in I_j}g_i)w_j+\frac{1}{2}(\sum\limits_{i\in I_j}h_i+\lambda) w_j^2]+\gamma T \\ & = \frac{1}{2}\sum\limits_{j=1}^{T} (H_j+\lambda)(w_j + \frac{G_j}{H_j+\lambda})^2+\gamma T -\frac{1}{2}\sum\limits_{j=1}^{T}\frac{G_j^2}{H_j+\lambda} \end{aligned} L^(t)=i=1∑n[gift(xi)+21htft2(xi)]+Ω(ft)=i=1∑n[gift(xi)+21htft2(xi)]+γT+21λj=1∑Twj2=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2]+γT=21j=1∑T(Hj+λ)(wj+Hj+λGj)2+γT−21j=1∑THj+λGj2
其中,Gj=∑i∈IjgiG_j = \sum _{i \in I_j} g_iGj=∑i∈Ijgi是落入叶子节点i所有样本一阶梯度的总和,而Hj=∑i∈IjhiH_j = \sum _{i \in I_j} h_iHj=∑i∈Ijhi是落入叶子节点i所有样本二阶梯度的总和。
基于中学数学的原理,我们可以得知:
当
wj∗=−GjHj+λw_j^* = -\frac{G_j}{H_j+\lambda}wj∗=−Hj+λGj
时,损失函数最小。此时最终的损失函数大小为
L^∗=−12∑j=1TGj2Hj+λ+γT\hat{\mathcal { L }}^{*} =-\frac{1}{2}\sum\limits_{j=1}^{T}\frac{G_j^2}{H_j+\lambda}+\gamma TL^∗=−21j=1∑THj+λGj2+γT
3.与牛顿法的关系
上面我们推导过程中,用到了二阶导数。而常见的基于二阶导数的优化方法就是牛顿法,下面我们看看与牛顿法的关系。
假设有一个函数f(x)f(x)f(x)二阶可微,我们对其进行泰勒展开到二阶,有
f(x)=f(xk)+f′(xk)(x−xk)+f′′(xk)(x−xk)2f(x) = f(x_k) + f'(x_k)(x-x_k) + f''(x_k)(x-x_k)^2f(x)=f(xk)+f′(xk)(x−xk)+f′′(xk)(x−xk)2
上面的式子对x求导,并令导数等于0
f′(xk)+f′′(xk)(x−xk)=0f'(x_k) + f''(x_k)(x-x_k) = 0f′(xk)+f′′(xk)(x−xk)=0
可以得到x的迭代公式为
x=xk−f′(xk)f′′(xk)x = x_k - \frac{f'(x_k)}{f''(x_k)}x=xk−f′′(xk)f′(xk)
这就是牛顿法的迭代公式。
对比一下叶子节点的取值方式
wj∗=−GjHj+λw_j^* = -\frac{G_j}{H_j+\lambda}wj∗=−Hj+λGj
与牛顿法唯一的区别在于,分母上二阶导多了一个正则项λ\lambdaλ,形式上完全一致,就是一阶导除以二阶导,与牛顿法的形式完全一致。
4.XGBoost的树分裂方式
上面推导了这么一大串,都是在分析叶子节点怎么取值。提升树系列的算法,还有很重要的一点是树的分裂方式。
首先我们回顾一下其他算法比如GBDT是怎么进行分裂的。标准的做法是选择MSE作为损失函数,采用启发式的方式来选择某个特征j的切分点s进行分裂。这个分裂的过程,并不一定总与损失函数相关。比如在分类树中,我们一般都会用对数损失函数(交叉熵)来评估模型的最终效果,这个时候树分裂的结果与损失函数就没有直接的关系。
XGBoost的树分裂是直接与损失函数相关的。在分裂的时候,会计算损失函数的增益Gain
Gain=12[GL2HL+λ+GR2HR+λ−(GL+GR)2HL+HR+λ]−γGain = \frac{1}{2} \left[\frac{G_L^2}{H_L+\lambda} + \frac{G_R^2}{H_R+\lambda} - \frac{(G_L + G_R)^2}{H_L + H_R+\lambda} \right] - \gammaGain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
上面这个式子其实很容易理解
(GL+GR)2HL+HR+λ\frac{(G_L + G_R)^2}{H_L + H_R+\lambda}HL+HR+λ(GL+GR)2是分裂前最终的损失函数值,而
GL2HL+λ,GR2HR+λ\frac{G_L^2}{H_L+\lambda}, \frac{G_R^2}{H_R+\lambda} HL+λGL2,HR+λGR2
分别为分裂后的左右子树的损失函数值
那树进行分裂的时候,自然希望损失函数减少得越多越好。
前面我们推导出了损失函数最终的表达式
L^∗=−12∑j=1TGj2Hj+λ+γT\hat{\mathcal { L }}^{*} =-\frac{1}{2}\sum\limits_{j=1}^{T}\frac{G_j^2}{H_j+\lambda}+\gamma TL^∗=−21j=1∑THj+λGj2+γT
那我们想要的结果就是分列前的损失函数减去分裂后的损失函数,差值最大,注意前面的负号,最后的分裂准则为
max(GL2HL+λ+GR2HR+λ−(GL+GR)2HL+HR+λ)max\left(\frac{G_L^2}{H_L+\lambda} + \frac{G_R^2}{H_R+\lambda} - \frac{(G_L + G_R)^2}{H_L + H_R+\lambda}\right)max(HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2)
5.XGBoost的各种Tricks总结
防止过拟合方法:
1.加入了正则项,对叶子节点数量,叶子节点分数加入了正则惩罚项。
2.加入了学习率,减少了单棵树影响,给后面树的优化留了更多空间。
3.列采样(特征采样),与随机森林类似,不仅来带来更好的效果,还可以提高运行速度。
缺失值的处理
在别的算法中,一般会使用中位数,均值或者两者进行融合计算的方式去处理缺失值。但是xgboost能处理缺失值,模型允许缺失值存在。
在寻找分裂点的时候,不对该特征缺失的样本进行遍历,只遍历该特征有的样。具体实现时,将该特征值缺失的样本分别分配到左叶子节点与右叶子节点,分别计算增益,然后选择增益较大的方向进行分裂即可。如果训练样本中没有缺失值,而预测时有缺失,默认将缺失值划分到右子节点。
分裂点的选择
并不把所有特征值间的间隔点作为候选分裂点,而是用分位数的方法将其作为分裂的候选集。
并行处理
XGBoost中虽然树之间是串行关系,但是同层级的节点可以并行。对于某个节点,节点内选择最佳分裂点与计算分裂点的增益是可以并行的。
基学习器(弱学习器)
传统的GBDT与CART树作为基分类器,XGBoost不仅支持CART树,还支持线性分类器,这个时候xgboost就相当于带L1与L2正则项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
6.XGBoost 与GBDT的对比
主要的区别其实就是上面的Tricks。
参考文献
1.https://www.jianshu.com/p/ac1c12f3fba1
树算法系列之四:XGBoost相关推荐
- 树模型系列之XGBoost算法
文章目录 树模型系列之XGBoost算法 概要 原理 损失函数 子树 xgboost算法流程总结 关键问题 缺失值处理(稀疏问题的分裂点查找 Sparsity-aware Split Finding) ...
- 面向对象的程序开发技术C++教学课件系列之四
面向对象的程序开发技术C++教学课件系列之四 转载于:https://blog.51cto.com/hnxdd/13205
- [转]DPM2012系列之四:配置邮件报警功能
DPM2012系列之四:配置邮件报警功能 源地址:http://543925535.blog.51cto.com/639838/1049285 ============================ ...
- 进程——Windows核心编程学习手札系列之四
进程 --Windows核心编程学习手札系列之四 进程是一个正在运行的程序的实例,有两个部分组成:一个是操作系统用来管理进程的内核对象,内核对象是系统用来存放关于进程的统计信息的地方:另一个是地址空间 ...
- mongodb与java结合_MongoDB初探系列之四:MongoDB与Java共舞
MongoDB初探系列之四:MongoDB与Java共舞 来源:互联网 作者:佚名 时间:2015-08-05 08:20 对各位注意到这个帖子的朋友说一声对不起,我不是故意的测试服务器一直没关,一忙 ...
- nginx系列之四:web服务器
** 前言 ** nginx系列之一:nginx入门 nginx系列之二:配置文件解读 nginx系列之三:日志配置 nginx系列之四:web服务器 nginx系列之五: 负载均衡 nginx系列之 ...
- ASP.NET企业开发框架IsLine FrameWork系列之四--DataProvider 数据访问(上)
ASP.NET企业开发框架IsLine FrameWork系列之四--DataProvider 数据访问(上) 接上文 DataProvider是日常编程中最常用的Provider,它为项目提供了与数 ...
- Red Gate系列之四 SQL Data Compare 10.2.0.885 Edition 数据比较同步工具 完全破解+使用教程...
Red Gate系列之四 SQL Data Compare 10.2.0.885 Edition 数据比较同步工具 完全破解+使用教程 Red Gate系列文章: Red Gate系列之一 SQL C ...
- SCCM2012系列之四,SCCM2012部署前的SQL Server准备
SCCM2012系列之四,SCCM2012部署前的SQLServer准备 部署SCCM2012需要SQL数据库的支持.如果在生产环境,肯定是把数据库产品单独部署在服务器上,有可能还要考虑到高可用的因素 ...
- 隐马尔科夫模型(Hidden Markov Models) 系列之四
隐马尔科夫模型(Hidden Markov Models) 系列之四 介绍(introduction) 生成模式(Generating Patterns) 隐含模式(Hidden Patterns) ...
最新文章
- android 长按赋值功能,android实现WebView中长按选中复制文本操作
- html加上 extjs右键,extjs 处理HTML事件和自定义事件
- Chapter1-1_Speech_Recognition(Overview)
- 2知识图谱的生命周期
- 如何在WPF 表格中嵌套按钮
- android build.gradle的repositories设置
- 谨慎设计方法签名(40)
- asp.net中前台javascript与c#函数相互调方法
- NodeJS链接MongDB
- win10 mfc 连接mysql_win10下使用c语言连接mysql
- 单利计算与复利计算程序
- 为了方便在微博上看小黄图,我写了一段JS
- 如何在网上买到下铺票2020_网上订票怎么选下铺
- iOS-调用系统的短信和发送邮件功能,实现短信分享邮件分享
- 王者荣耀入门技能树-解答
- 用数字暗示我喜欢你_我喜欢数字!
- SAP中报表清单导出的常用方法
- python爬取笔趣阁
- STM32F105 UC/OS-II (B OSStartHang)问题
- android TextView 添加下划线