XGBoost 与 Boosted Tree
1. 前言
作为一个非常有效的机器学习方法,Boosted Tree是数据挖掘和机器学习中最常用的算法之一。因为它效果好,对于输入要求不敏感,往往是从统计学家到数据科学家必备的工具之一,它同时也是kaggle比赛冠军选手最常用的工具。最后,因为它的效果好,计算复杂度不高,也在工业界中有大量的应用。
2. Boosted Tree的若干同义词
说到这里可能有人会问,为什么我没有听过这个名字。这是因为Boosted Tree有各种马甲,比如GBDT, GBRT(gradient boosted regression tree),MART1^1,LambdaMART也是一种boosted tree的变种。网上有很多介绍Boosted tree的资料,不过大部分都是基于Friedman的最早一篇文章Greedy Function Approximation: A Gradient Boosting Machine的翻译。个人觉得这不是最好最一般地介绍boosted tree的方式。而网上除了这个角度之外的介绍并不多。这篇文章是我个人对于boosted tree和gradient boosting 类算法的总结,其中很多材料来自于我TA UW机器学习时的一份讲义2^2。
3. 有监督学习算法的逻辑组成
要讲boosted tree,要先从有监督学习讲起。在有监督学习里面有几个逻辑上的重要组成部件3^3,初略地分可以分为:模型,参数 和 目标函数。
i. 模型和参数
模型指给定输入xix_i如何去预测输出 yiy_i。我们比较常见的模型如线性模型(包括线性回归和logistic regression)采用了线性叠加的方式进行预测y^i=∑jwjxij\hat y_i = \sum_j w_j x_{ij} 。其实这里的预测yy可以有不同的解释,比如我们可以用它来作为回归目标的输出,或者进行sigmoid 变换得到概率,或者作为排序的指标等。而一个线性模型根据yy的解释不同(以及设计对应的目标函数)用到回归,分类或排序等场景。参数指我们需要学习的东西,在线性模型中,参数指我们的线性系数ww。
ii. 目标函数:损失 + 正则
模型和参数本身指定了给定输入我们如何做预测,但是没有告诉我们如何去寻找一个比较好的参数,这个时候就需要目标函数登场了。一般的目标函数包含下面两项
常见的误差函数有L=∑nil(yi,y^i)L = \sum_i^n l(y_i,\hat y_i) 比如平方误差l(yi,y^i)=(yi−y^i)2l(y_i,\hat y_i) = (y_i - \hat y_i)^2,logistic误差函数l(yi,y^i)=yiln(1+e−y^i)+(1−yi)ln(1+ey^i)l(y_i,\hat y_i) = y_i \ln(1 + e^{-\hat y_i}) + (1-y_i) \ln(1+e^{\hat y_i})等。而对于线性模型常见的正则化项有L2L2正则和L1L1正则。这样目标函数的设计来自于统计学习里面的一个重要概念叫做Bias-variance tradeoff4^4。比较感性的理解,Bias可以理解为假设我们有无限多数据的时候,可以训练出最好的模型所拿到的误差。而Variance是因为我们只有有限数据,其中随机性带来的误差。目标中误差函数鼓励我们的模型尽量去拟合训练数据,这样相对来说最后的模型会有比较少的 bias。而正则化项则鼓励更加简单的模型。因为当模型简单之后,有限数据拟合出来结果的随机性比较小,不容易过拟合,使得最后模型的预测更加稳定。
iii. 优化算法
讲了这么多有监督学习的基本概念,为什么要讲这些呢? 是因为这几部分包含了机器学习的主要成分,也是机器学习工具设计中划分模块比较有效的办法。其实这几部分之外,还有一个优化算法,就是给定目标函数之后怎么学的问题。之所以我没有讲优化算法,是因为这是大家往往比较熟悉的“机器学习的部分”。而有时候我们往往只知道“优化算法”,而没有仔细考虑目标函数的设计的问题,比较常见的例子如决策树的学习,大家知道的算法是每一步去优化gini entropy,然后剪枝,但是没有考虑到后面的目标是什么。
4. Boosted Tree
i. 基学习器:分类和回归树(CART)
话题回到boosted tree,我们也是从这几个方面开始讲,首先讲模型。Boosted tree 最基本的组成部分叫做回归树(regression tree),也叫做CART5^5。
上面就是一个CART的例子。CART会把输入根据输入的属性分配到各个叶子节点,而每个叶子节点上面都会对应一个实数分数。上面的例子是一个预测一个人是否会喜欢电脑游戏的 CART,你可以把叶子的分数理解为有多可能这个人喜欢电脑游戏。有人可能会问它和decision tree的关系,其实我们可以简单地把它理解为decision tree的一个扩展。从简单的类标到分数之后,我们可以做很多事情,如概率预测,排序。
ii. Tree Ensemble
一个CART往往过于简单无法有效地预测,因此一个更加强力的模型叫做tree ensemble。
在上面的例子中,我们用两棵树来进行预测。我们对于每个样本的预测结果就是每棵树预测分数的和。到这里,我们的模型就介绍完毕了。现在问题来了,我们常见的随机森林和boosted tree和tree ensemble有什么关系呢?如果你仔细的思考,你会发现RF和boosted tree的模型都是tree ensemble,只是构造(学习)模型参数的方法不同。第二个问题:在这个模型中的“参数”是什么。在tree ensemble中,参数对应了树的结构,以及每个叶子节点上面的预测分数。
最后一个问题当然是如何学习这些参数。在这一部分,答案可能千奇百怪,但是最标准的答案始终是一个:定义合理的目标函数,然后去尝试优化这个目标函数。在这里我要多说一句,因为决策树学习往往充满了heuristic(启发式)。 如先优化吉尼系数,然后再剪枝啦,限制最大深度,等等。其实这些heuristic的背后往往隐含了一个目标函数,而理解目标函数本身也有利于我们设计学习算法,这个会在后面具体展开。
对于tree ensemble,我们可以比较严格的把我们的模型写成是:
\hat y_i = \sum_{k=1}^K f_k(x_i), \quad f_k \in \mathcal{F}
其中每个 ff是一个在函数空间6^6( F\mathcal{F})里面的函数,而 F\mathcal{F}对应了所有regression tree的集合。我们设计的目标函数也需要遵循前面的主要原则,包含两部分
Obj(\Theta) = \sum_i^n l(y_i,\hat y_i) +\sum_{k=1}^K\Omega(f_k)
iii. 模型学习:additive training
其中第一部分是训练误差,也就是大家相对比较熟悉的如平方误差, logistic loss等。而第二部分是每棵树的复杂度的和。这个在后面会继续讲到。因为现在我们的参数可以认为是在一个函数空间里面,我们不能采用传统的如SGD之类的算法来学习我们的模型,因此我们会采用一种叫做additive training的方式(另外,在我个人的理解里面7^7,boosting就是指additive training的意思)。每一次保留原来的模型不变,加入一个新的函数ff到我们的模型中。
现在还剩下一个问题,我们如何选择每一轮加入什么ff呢?答案是非常直接的,选取一个ff来使得我们的目标函数尽量最大地降低8^8。
这个公式可能有些过于抽象,我们可以考虑当ll是平方误差的情况。这个时候我们的目标可以被写成下面这样的二次函数9^9:
更加一般的,对于不是平方误差的情况,我们会采用如下的泰勒展开近似来定义一个近似的目标函数,方便我们进行这一步的计算。
当我们把常数项移除之后,我们会发现如下一个比较统一的目标函数。这一个目标函数有一个非常明显的特点,它只依赖于每个数据点的在误差函数上的一阶导数和二阶导数10^{10}。有人可能会问,这个材料似乎比我们之前学过的决策树学习难懂。为什么要花这么多力气来做推导呢?
因为这样做使得我们可以很清楚地理解整个目标是什么,并且一步一步推导出如何进行树的学习。这一个抽象的形式对于实现机器学习工具也是非常有帮助的。传统的GBDT可能大家可以理解如优化平法a^a残差,但是这样一个形式包含可所有可以求导的目标函数。也就是说有了这个形式,我们写出来的代码可以用来求解包括回归,分类和排序的各种问题,正式的推导可以使得机器学习的工具更加一般。
iv. 树的复杂度
到目前为止我们讨论了目标函数中训练误差的部分。接下来我们讨论如何定义树的复杂度。我们先对于f的定义做一下细化,把树拆分成结构部分qq和叶子权重部分ww。下图是一个具体的例子。结构函数qq把输入映射到叶子的索引号上面去,而ww给定了每个索引号对应的叶子分数是什么。
当我们给定了如上定义之后,我们可以定义一棵树的复杂度如下。这个复杂度包含了一棵树里面节点的个数,以及每个树叶子节点上面输出分数的L2L2模平方。当然这不是唯一的一种定义方式,不过这一定义方式学习出的树效果一般都比较不错。下图还给出了复杂度计算的一个例子。
v. 关键步骤
接下来是最关键的一步11^{11},在这种新的定义下,我们可以把目标函数进行如下改写,其中I被定义为每个叶子上面样本集合 Ij={i|q(xi)=j}I_j = \{ i|q(x_i)=j\}
这一个目标包含了TT个相互独立的单变量二次函数。我们可以定义
G_j = \sum_{i \in I_j} g_i \quad H_j = \sum_{i \in I_j} h_i
那么这个目标函数可以进一步改写成如下的形式,假设我们已经知道树的结构 qq,我们可以通过这个目标函数来求解出最好的ww,以及最好的 ww对应的目标函数最大的增益
Obj^{(t)} = \sum_{j=1}^T [( \sum_{i \in I_j} g_i)w_j+\frac 1 2(\sum_{i \in I_j} h_i + \lambda)w_j^2] + \gamma T \\= \sum_{j=1}^T [G_j w_j + \frac 1 2 (H_j + \lambda) w_j^2] + \gamma T
这两个的结果对应如下,左边是最好的 ww,右边是这个ww对应的目标函数的值。到这里大家可能会觉得这个推导略复杂。其实这里只涉及到了如何求一个一维二次函数的最小值的问题 12^{12}。如果觉得没有理解不妨再仔细琢磨一下
w_j^* = - \frac {G_j} {H_j + \lambda} \quad Obj = - \frac 1 2 \sum_{j=1}^T \frac {G_j^2} {H_j + \lambda} + \gamma T
vi. 打分函数计算举例
Obj代表了当我们指定一个树的结构的时候,我们在目标上面最多减少多少。我们可以把它叫做结构分数(structure score)。你可以认为这个就是类似吉尼系数一样更加一般的对于树结构进行打分的函数。下面是一个具体的打分函数计算的例子
vii. 枚举所有不同树结构的贪心法
所以我们的算法也很简单,我们不断地枚举不同树的结构,利用这个打分函数来寻找出一个最优结构的树,加入到我们的模型中,再重复这样的操作。不过枚举所有树结构这个操作不太可行,所以常用的方法是贪心法,每一次尝试去对已有的叶子加入一个分割。对于一个具体的分割方案,我们可以获得的增益可以由如下公式计算
对于每次扩展,我们还是要枚举所有可能的分割方案,如何高效地枚举所有的分割呢?我假设我们要枚举所有 x<ax这样的条件,对于某个特定的分割aa我们要计算aa左边和右边的导数和。
我们可以发现对于所有的aa,我们只要做一遍从左到右的扫描就可以枚举出所有分割的梯度和GLG_L和GRG_R。然后用上面的公式计算每个分割方案的分数就可以了。
观察这个目标函数,大家会发现第二个值得注意的事情就是引入分割不一定会使得情况变好,因为我们有一个引入新叶子的惩罚项。优化这个目标对应了树的剪枝, 当引入的分割带来的增益小于一个阀值的时候,我们可以剪掉这个分割。大家可以发现,当我们正式地推导目标的时候,像计算分数和剪枝这样的策略都会自然地出现,而不再是一种因为heuristic而进行的操作了。
讲到这里文章进入了尾声,虽然有些长,希望对大家有所帮助,这篇文章介绍了如何通过目标函数优化的方法比较严格地推导出boosted tree的学习。因为有这样一般的推导,得到的算法可以直接应用到回归,分类排序等各个应用场景中去。
- 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
- 传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。
- xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性。
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点。(补充:传统GBDT的实现也有学习速率)
- 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
- 对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。
- xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
- 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。
XGBoost 与 Boosted Tree相关推荐
- 八、回归——XGBoost 与 Boosted Tree
作者:陈天奇,毕业于上海交通大学ACM班,现就读于华盛顿大学,从事大规模机器学习研究. 注解:truth4sex 编者按:本文是对开源xgboost库理论层面的介绍,在陈天奇原文<梯度提升法和B ...
- 【转】XGBoost 与 Boosted Tree
XGBoost 与 Boosted Tree http://www.52cs.org/?p=429 作者:陈天奇,毕业于上海交通大学ACM班,现就读于华盛顿大学,从事大规模机器学习研究. 注解:tru ...
- Boosted Tree:一篇很有见识的文章
Boosted Tree:一篇很有见识的文章 6,125 次阅读 - 文章 作者:陈天奇,毕业于上海交通大学ACM班,现就读于华盛顿大学,从事大规模机器学习研究. 注解:truth4sex 编者按: ...
- 机器学习——陈天奇Boosted Tree(GBDT)详解
工作了好多年,从最开始使用xgboost,到后来的lightGBM,它们的底层原理都是Boosted Tree,之前一直没有做过总结,今天我就把陈天奇的Boosted Tree翻译一下,让大家从原理了 ...
- 集成学习方法之 Boosted Tree 原理
之前在点云分类中用了SVM和Random Forest , 发现RF 的效果非常好,比SVM好许多,因此想再试一下其它基于树的集成学习方法,比如Boosted Tree 和 Xgboost,Xgboo ...
- Boosted Tree原理详解
简述 boosted Tree算法简要描述: 不断地添加树,不断地进行特征分裂来生长一棵树.每次添加一个树,其实是学习一个新函数,去拟合上次预测的残差.一个树在生长的过程中,挑选一个最佳特征的最佳分裂 ...
- Boosted Tree
本文是对开源xgboost库理论层面的介绍,在陈天奇原文<梯度提升法和Boosted Tree>的基础上,做了如下注解:1)章节划分:2)注解和参考链接(以蓝色和红色字体标注).备注:图片 ...
- ML之XGBoost:《XGBoost: A Scalable Tree Boosting System》的翻译与解读
ML之XGBoost:<XGBoost: A Scalable Tree Boosting System>的翻译与解读 目录 XGBoost: A Scalable Tree Boosti ...
- 论文精读:XGBoost: A Scalable Tree Boosting System
论文下载地址:XGBoost: A Scalable Tree Boosting System 一句话讲: 读前先问 读论文之前首先要问几个问题: 这篇论文大方向的目标是什么? 机器学习中的有监督学习 ...
最新文章
- python中非怎么表示_Python中非英语文本的标记化
- linux 查找目录或文件详解
- 动态时间规整_动态规划-数组系列(10%)
- Pandas知识点-比较操作
- Spring cloud (1)---Eureka 宣布闭源,Spring Cloud 何去何从?
- 苹果下半年推出M2芯片MacBook Air 配色更多更轻薄
- 10个开源且优秀的后台管理系统UI面板
- (网页)JS实现alert中显示换行的方法
- 一分钟搞定网页监控,实现网站链接百度自动推送
- AgileCDN加速情况数据测评
- 质谱借力ICL平台,静待LDT制度打开更大空间
- app移动端连接nginx时,总报错recv() failed (104: Connection reset by peer) while proxying upgraded connection
- linux服务器告警信息:Free inodes is less than xx% on /volume 排查
- CSS 滚动条,浮动问题,图片bug
- OpenSUSE安装配置TeXLive2015.iso、前端软件Kile、Lyx、TeXstudio及TeX的更新管理界面的使用
- Unity在UGUI上使用Polygon Collider 2D实现不规则图案匹配
- FANUC机器人编码器相关报警代码及处理方法
- 图床云存储项目课程随堂笔记
- 英文SEO分析竞争对手推广策略实操
- 【计算机网络:自顶向下方法】期末复习总结(USTC 2020秋 zq班)
热门文章
- 几种常见的版本控制工具
- c+和python先学哪个,c语言和python先学哪个比较简单
- 判断浏览器(微信、微博、QQ)、操作系统、横竖屏等
- 512 QAM 、1024 QAM 、 2048 QAM 、 4096 QAM 调制类型
- 前端面试 计算机网络知识,前端面试复习-1-浏览器和计算机网络等
- iOS马甲包遇到问题怎么过审,App Store
- python中ln怎么写_Python Decimal ln()用法及代码示例
- 中国量化对冲策略及产品的大全解读
- 【NOI2022省选挑战赛 Contest11 A】魔法球(二分)
- 解析XML文件——SAX解析技术