XGBoost是陈天奇等人开发的一个开源机器学习项目,高效地实现了GBDT算法并进行了算法和工程上的许多改进,被广泛应用在Kaggle竞赛及其他许多机器学习竞赛中并取得了不错的成绩。XGBoost全名叫(eXtreme Gradient Boosting)极端梯度提升。它是大规模并行boosted tree的工具,它是目前最快最好的开源boosted tree工具包。XGBoost 所应用的算法就是 GBDT(gradient boosting decision tree)的改进,既可以用于分类也可以用于回归问题中。与GBDT最大的区别是xgboost通过对目标函数做二阶泰勒展开,从而求出下一步要拟合的树的叶子节点权重(需要先确定树的结构),从而根据损失函数求出每一次分裂节点的损失减小的大小,从而根据分裂损失选择合适的属性进行分裂。
这个利用二阶展开得到的损失函数公式与分裂节点的过程是息息相关的。先遍历所有节点的所有属性进行分裂,假设选择了这个a属性的一个取值作为分裂节点,根据泰勒展开求得的公式可计算该树结构各个叶子节点的权重,从而计算损失减小的程度,从而综合各个属性选择使得损失减小最大的那个特征作为当前节点的分裂属性。依次类推,直到满足终止条件。
2 XGboost思路首先明确下我们的目标,希望建立K个回归树,使得树群的预测值尽量接近真实值(准确率)而且有尽量大的泛化能力(更为本质的东西),从数学角度看这是一个泛函最优化,多目标,看下目标函数:
其中 i 表示第i个样本,表示第 i 个样本的预测误差,误差越小越好。后面表示树的复杂度的函数,越小复杂度越低,泛化能力越强。表达式为
一般的目标函数都包含下面两项
其中,误差/损失函数鼓励我们的模型尽量去拟合训练数据,使得最后的模型会有比较少的 bias。而正则化项则鼓励更加简单的模型。因为当模型简单之后,有限数据拟合出来结果的随机性比较小,不容易过拟合,使得最后模型的预测更加稳定。
直观上看,目标要求预测误差尽量小,叶子节点尽量少,节点数值尽量不极端(这个怎么看,如果某个样本label数值为4,那么第一个回归树预测3,第二个预测为1;另外一组回归树,一个预测2,一个预测2,那么倾向后一种,为什么呢?前一种情况,第一棵树学的太多,太接近4,也就意味着有较大的过拟合的风险)
怎么实现呢?上面这个目标函数跟实际的参数怎么联系起来,记得我们说过,回归树的参数:(1)选取哪个feature分裂节点呢;(2)节点的预测值。上述公式并没有“直接”解决这两个,那么是如何间接解决的呢?
先说答案:贪心策略+最优化(二次最优化)
通俗解释贪心策略:就是决策时刻按照当前目标最优化决定,说白了就是眼前利益最大化决定,“目光短浅”策略,他的优缺点细节大家自己去了解,经典背包问题等等。
这里是怎么用贪心策略的呢,刚开始你有一群样本,放在第一个节点,这时候T=1,w多少呢,是求出来的,这时候所有样本的预测值都是w(这个地方自己好好理解,决策树的节点表示类别,回归树的节点表示预测值),带入样本的label数值,此时loss function变为
如果这里的l(w−yi)误差表示用的是平方误差,那么上述函数就是一个关于w的二次函数求最小值,取最小值的点就是这个节点的预测值,最小的函数值为最小损失函数。
这里处理的就是二次函数最优化!
要是损失函数不是二次函数咋办,使用泰勒展开式,不是二次的想办法近似为二次。
接着来,接下来要选个feature分裂成两个节点,变成一棵弱小的树苗,那么需要:(1)确定分裂用的feature,how?最简单的是粗暴的枚举,选择loss function效果最好的那个(关于粗暴枚举,Xgboost的改良并行方式咱们后面看);(2)如何确立节点的w以及最小的loss function?即二次函数的求最值(细节的会注意到,计算二次最值是不是有固定套路,导数=0的点)
那么步骤是,选择一个feature分裂,计算loss function最小值,然后再选一个feature分裂,又得到一个loss function最小值…你枚举完,找一个效果最好的,把树给分裂,就得到了小树苗。在分裂的时候,你可以注意到,每次节点分裂,loss function被影响的只有这个节点的样本,因而每次分裂,计算分裂的增益(loss function的降低量)只需要关注打算分裂的那个节点的样本。
接下来,继续分裂,按照上述的方式,形成一棵树,再形成一棵树,每次在上一次的预测基础上取最优进一步分裂/建树。
凡是这种循环迭代的方式必定有停止条件:
(1)当引入的分裂带来的增益小于一个阀值的时候,我们可以剪掉这个分裂,所以并不是每一次分裂loss function整体都会增加的,有点预剪枝的意思,阈值参数为 γ 正则项里叶子节点数 T 的系数;
(2)当树达到最大深度时则停止建立决策树,设置一个超参数max_depth,这个好理解吧,树太深很容易出现的情况学习局部样本,过拟合;
(3)当样本权重和小于设定阈值时则停止建树,这个解释一下,涉及到一个超参数-最小的样本权重和min_child_weight,和GBM的 min_child_leaf 参数类似,但不完全一样,大意就是一个叶子节点样本太少了,也终止同样是过拟合;
(4)貌似看到过有树的最大数量的
那节点分裂的时候是按照哪个顺序来的,比如第一次分裂后有两个叶子节点,先裂哪一个?答:同一层级的(多机)并行,确立如何分裂或者不分裂成为叶子节点 。
3 原理推导上面一部分我们知道了集成学习方法的预测模型,因为XGBoost也是集成学习方法的一种。对于XGBoost的预测模型同样可以表示为:
其中为树的总个数,表示第颗树,表示样本的预测结果。
其中损失函数也同样表示为:
其中为样本的训练误差,表示第棵树的正则项。
3.1 训练误差看到了这里,我们可能会想到,现在知道了模型预测函数和损失函数,那我们是不是直接就能求出其预测模型了呢?答案肯定不是,我们首先需要明确知道优化和求解的参数是什么呢?由上面的预测模型中,我们可以看到对于每棵树的预测值是如何计算的。那么需要求解和优化的就是每个叶子节点的得分值,也就是的值。另外我们知道XGBoost是以CART树中的回归树作为基分类器,在给定训练数据后,其单个树的结构(叶子节点个数、树深度等等)基本可以确定了。但XGBoost并不是简单重复的将几个CART树进行组合。它是一种加法模型,将模型上次预测(由t-1棵树组合而成的模型)产生的误差作为参考进行下一棵树(第t棵树)的建立。以此,每加入一棵树,将其损失函数不断降低。如下图就为加法模型案例,它将模型预测值与实际值残差作为下一颗树的输入数据。
类似之前GBDT的套路,XGBoost也是需要将多棵树的得分累加得到最终的预测得分(每一次迭代,都在现有树的基础上,增加一棵树去拟合前面树的预测结果与真实值之间的残差)。
对于加法策略可以表示如下:
初始化(模型中没有树时,其预测结果为0):
往模型中加入第一棵树:
往模型中加入第二棵树:
……
往模型中加入第t棵树:
其中表示第棵树,表示组合棵树模型对样本的预测结果。
我们知道,每次往模型中加入一棵树,其损失函数便会发生变化。另外在加入第 t 棵树时,则前面第 t-1 棵树已经训练完成,此时前面 t-1 棵树的正则项和训练误差都成已知常数项。对于每棵树的正则项部分,我们将在后面再细说。
如果损失函数采用均方误差时,其目标损失函数变为:
另外对于目标损失函数中的正则项(复杂度)部分,我们从单一的树来考虑。对于其中每一棵回归树,其模型可以写成:
其中为叶子节点的得分值,表示样本对应的叶子节点。为该树的叶子节点个数。
3.2 正则项,树的复杂度因此,在这里。我们将该树的复杂度写成:
复杂度计算例子如下:
此时,对于XGBoost的目标函数我们可以写为:
3.3 用泰勒展开式表示目标函数
现在我们只需要找到 f(t) 来优化上式目标函数。
事实上,如果不考虑工程实现、解决问题上的一些差异,XGBoost与GBDT比较大的不同就是目标函数的定义。
在推导之前,我们先介绍下泰勒展开式:
在数学中,泰勒公式(英语:Taylor's Formula)是一个用函数在某点的信息描述其附近取值的公式。这个公式来自于微积分的泰勒定理(Taylor's theorem),泰勒定理描述了一个可微函数,如果函数足够光滑的话,在已知函数在某一点的各阶导数值的情况之下,泰勒公式可以用这些导数值做系数构建一个多项式来近似函数在这一点的邻域中的值,这个多项式称为泰勒多项式(Taylor polynomial)。
相当于告诉我们可由利用泰勒多项式的某些次项做原函数的近似。
泰勒定理:
设 n 是一个正整数。如果定义在一个包含 a 的区间上的函数 f 在 a 点处 n+1 次可导,那么对于这个区间上的任意 x,都有:其中的多项式称为函数在a 处的泰勒展开式,剩余的是泰勒公式的余项,是的高阶无穷小。
这里我们用泰勒展开式来近似原来的目标函数,将看作。则原目标函数可以写成:
令,,同时对于第t棵树时,为常数。同时去除所有常数项。故目标损失函数可以写成:
由上面介绍的复杂度时,我们知道:,同时我们将目标函数全部转换成在第 t 棵树叶子节点的形式。因为目前对于可以看做是每个样本在第 t 棵树的叶子节点得分值相关函数的结果之和,所以我们也能从第 t 棵树的叶子节点上来表示。
上式中,前两行 i=1~n 求和为在样本中遍历,后两行 j = 1~T求和为在叶子节点上遍历,其中为第 t 棵树中总叶子节点的个数,表示在第个叶子节点上的样本,为第个叶子节点的得分值。
在这里,令,。
则:
对求偏导,并使其导函数等于0,则有:
求解得:
其目标函数可以为:
3.4 根据目标函数,如何分裂样本数据?对于一个叶子节点如何进行分裂,xgboost作者在其原始论文中给出了两种分裂节点的方法:
3.4.1 枚举所有不同树结构的贪心法现在的情况是只要知道树的结构,就能得到一个该结构下的最好分数,那如何确定树的结构呢?
一个想当然的方法是:不断地枚举不同树的结构,然后利用打分函数来寻找出一个最优结构的树,接着加入到模型中,不断重复这样的 *** 作。而再一想,你会意识到要枚举的状态太多了,基本属于无穷种,那咋办呢?
我们试下贪心法,从树深度0开始,每一节点都遍历所有的特征,比如年龄、性别等等,然后对于某个特征,先按照该特征里的值进行排序,然后线性扫描该特征进而确定最好的分割点,最后对所有特征进行分割后,我们选择所谓的增益Gain最高的那个特征。
枚举不同树的结构,然后利用打分函数来寻找出一个最优结构的树,接着加入到模型中,不断重复这样的 *** 作。这个寻找的过程使用的就是贪心算法。选择一个feature分裂,计算loss function最小值,然后再选一个feature分裂,又得到一个loss function最小值,你枚举完,找一个效果最好的,把树给分裂,就得到了小树苗。
3.4.2 近似算法主要针对数据太大,不能直接进行计算:
3.4.3 从所有的树结构中寻找最优的树结构 不同的决策树算法采用不同的准则, 如IC3算法采用信息增益, C4.5算法为了克服信息增益中容易偏向取值较多的特征而采用信息增益比, CART算法使用基尼指数和平方误差, XGBoost也有特定的准则来选取最优分裂。
通过将预测值代入到损失函数中可求得损失函数的最小值:
容易计算出分裂前后损失函数的差值为:
XGBoost采用最大化这个差值作为准则来进行决策树的构建, 通过遍历所有特征的所有取值, 寻找使得损失函数前后相差最大时对应的分裂方式。 此外, 由于损失函数前后存在差值一定为正的限制, 此时γ起到了一定的预剪枝效果。
总而言之,XGBoost使用了和CART回归树一样的想法,利用贪婪算法,遍历所有特征的所有特征划分点,不同的是使用的目标函数不一样。具体做法就是分裂后的目标函数值比单子叶子节点的目标函数的增益,同时为了限制树生长过深,还加了个阈值,只有当增益大于该阈值才进行分裂。从而继续分裂,形成一棵树,再形成一棵树,每次在上一次的预测基础上取最优进一步分裂/建树。 以下便为设定的阈值:
3.5 如何停止树的循环生成凡是这种循环迭代的方式必定有停止条件,什么时候停止呢?简言之,设置树的最大深度、当样本权重和小于设定阈值时停止生长以防止过拟合。具体而言,则
- 当引入的分裂带来的增益小于设定阀值的时候,我们可以忽略掉这个分裂,所以并不是每一次分裂loss function整体都会增加的,有点预剪枝的意思,阈值参数为(即正则项里叶子节点数T的系数);
- 当树达到最大深度时则停止建立决策树,设置一个超参数max_depth,避免树太深导致学习局部样本,从而过拟合;
- 样本权重和小于设定阈值时则停止建树。什么意思呢,即涉及到一个超参数-最小的样本权重和min_child_weight,和GBM的 min_child_leaf 参数类似,但不完全一样。大意就是一个叶子节点样本太少了,也终止同样是防止过拟合;
(1)w是最优化求出来的,不是啥平均值或规则指定的,这个算是一个思路上的新颖吧;
(2)正则化防止过拟合的技术,loss function里面就有;
(3)支持自定义loss function,只要能泰勒展开(能求一阶导和二阶导)就行;
(4)支持并行化,这是xgboost的闪光点,直接的效果是训练速度快。xgboost在实现上支持并行化,这里的并行化并不是类似于rf那样树与树之间的并行化,xgboost同boosting方法一样,在树的粒度上是串行的,但是在构建树的过程中,也就是在选择最佳分裂点,进行枚举的时候并行,比如同时计算多个属性的多个取值作为分裂特征及其值,然后,选择收益最大的特征及其取值对节点分裂。(据说恰好这个也是树形成最耗时的阶段)。
Attention:同层级节点可并行。具体的对于某个节点,节点内选择最佳分裂点,候选分裂点计算增益用多线程并行。
较少的离散值作为分割点倒是很简单,比如“是否是单身”来分裂节点计算增益是很easy,但是“月收入”这种feature,取值很多,从5k~50k都有,总不可能每个分割点都来试一下计算分裂增益吧?(比如月收入feature有1000个取值,难道你把这1000个用作分割候选?缺点1:计算量,缺点2:出现叶子节点样本过少,过拟合)我们常用的习惯就是划分区间,那么问题来了,这个区间分割点如何确定,作者是这么做的:
方法名字:Weighted Quantile Sketch
大家还记得每个样本在节点(将要分裂的节点)处的loss function一阶导数gi和二阶导数hi,衡量预测值变化带来的loss function变化,举例来说,将样本“月收入”进行升序排列,5k、5.2k、5.3k、…、52k,分割线为“收入1”、“收入2”、…、“收入j”,满足(每个间隔的样本的hi之和/总样本的hi之和)为某个百分比ϵ(我这个是近似的说法),那么可以一共分成大约1/ϵ个分裂点。
(5)XGBoost还特别设计了针对稀疏数据的算法
假设样本的第i个特征缺失时,无法利用该特征对样本进行划分,xgboost添加了对稀疏数据的支持,在计算分裂收益的时候只利用没有missing值的那些样本,但是在推理的时候,也就是在确定了树的结构,需要将样本映射到叶子节点的时候,需要对含有缺失值的样本进行划分,xgboost分别假设该样本属于左子树和右子树,比较两者分裂增益,选择增益较大的那一边作为该样本的分裂方向。
算法的主要思想是,分别假设特征缺失的样本属于右子树和左子树,而且只在不缺失的样本上迭代,分别计算缺失样本属于右子树和左子树的增益,选择增益最大的方向为缺失数据的默认方向(咋一看如果缺失情况为3个样本,那么划分的组合方式岂不是有8种?指数级可能性啊,仔细一看,应该是在不缺失样本情况下分裂后,把第一个缺失样本放左边计算下loss function和放右边进行比较,同样对付第二个、第三个…缺失样本,这么看来又是可以并行的?)(答:论文中“枚举”指的不是枚举每个缺失样本在左边还是在右边,而是枚举缺失样本整体在左边,还是在右边两种情况。 分裂点还是只评估特征不缺失的样本。);
(6)可实现后剪枝
(7)交叉验证,方便选择最好的参数,early stop,比如你发现30棵树预测已经很好了,不用进一步学习残差了,那么停止建树。
(8)行采样、列采样,随机森林的套路(防止过拟合)
(9)Shrinkage,你可以是几个回归树的叶子节点之和为预测值,也可以是加权,比如第一棵树预测值为3.3,label为4.0,第二棵树才学0.7,….再后面的树还学个鬼,所以给他打个折扣,比如3折,那么第二棵树训练的残差为4.0-3.3*0.3=3.01,这就可以发挥了啦,以此类推,作用是啥,防止过拟合,如果对于“伪残差”学习,那更像梯度下降里面的学习率;
(10)xgboost还支持设置样本权重,这个权重体现在梯度g和二阶梯度h上,是不是有点adaboost的意思,重点关注某些样本;
(11)xgboost 在实现时,需要将所有数据导入内存,做一次pre-sort(exact algorithm),这样在选择分裂节点时比较迅速;
(12)类似于随机森林,XGBoost在构建树的过程中,对每棵树随机选择一些属性作为分裂属性。
分裂算法有两种,一种是精确的分裂,一种是近似分裂算法。精确分裂算法就是把每个属性的每个取值都当作一次阈值进行遍历,采用的决策树是CART。近似分裂算法是对每个属性的所有取值进行分桶,按照各个桶之间的值作为划分阈值。xgboost提出了一个特殊的分桶策略,一般的分桶策略是每个样本的权重都是相同的,但是xgboost使每个样本的权重为损失函数在该样本点的二阶导(泰勒展开不应该是损失函数关于模型的展开吗?为什么会有在该样本点的二阶导这种说法? 因为模型是对所有样本点都通用的,把该样本输入到二阶导公式中就可以得到了)。
(13)除了类似于GBDT的缩减系数外,xgboost对每棵树的叶子节点个数和权重都做了惩罚,避免过拟合。
4.2 缺点- level-wise 建树方式,能够同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合;但不加区分的对待同一层的叶子,带来了很多没必要的开销(因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂);对当前层的所有叶子节点一视同仁,有些叶子节点分裂收益非常小,对结果没影响,但还是要分裂,加重了计算代价。
- 预排序方法空间消耗比较大,不仅要保存特征值,也要保存特征的排序索引,同时时间消耗也大,在遍历每个分裂点时都要计算分裂增益(不过这个缺点可以被近似算法所克服)
Xgboost第一感觉就是防止过拟合+各种支持分布式/并行,所以一般传言这种大杀器效果好(集成学习的高配)+训练效率高(分布式),与深度学习相比,对样本量和特征数据类型要求没那么苛刻,适用范围广。
说下GBDT:有两种描述版本,把GBDT说成一个迭代残差树,认为每一棵迭代树都在学习前N-1棵树的残差;把GBDT说成一个梯度迭代树,使用梯度迭代下降法求解,认为每一棵迭代树都在学习前N-1棵树的梯度下降值。有说法说前者是后者在loss function为平方误差下的特殊情况。仍然举个例子:第一棵树形成之后,有预测值ŷ i,真实值(label)为yi,前者版本表示下一棵回归树根据样本(xi,yi−ŷ i)进行学习,后者的意思是计算loss function在第一棵树预测值附近的梯度负值作为新的label,也就是对应xgboost中的−gi。
Xgboost和深度学习的关系,陈天奇在Quora上的解答如下:
不同的机器学习模型适用于不同类型的任务。深度神经网络通过对时空位置建模,能够很好地捕获图像、语音、文本等高维数据。而基于树模型的XGBoost则能很好地处理表格数据,同时还拥有一些深度神经网络所没有的特性(如:模型的可解释性、输入数据的不变性、更易于调等)。
这两类模型都很重要,并广泛用于数据科学竞赛和工业界。举例来说,几乎所有采用机器学习技术的公司都在使用tree boosting,同时XGBoost已经给业界带来了很大的影响。
XGBoost与GBDT有什么不同?
除了算法上与传统的GBDT有一些不同外,XGBoost还在工程实现上做了大量的优化。总的来说,两者之间的区别和联系可以总结成以下几个方面。
- GBDT是机器学习算法,XGBoost是该算法的工程实现。
- 在使用CART作为基分类器时,XGBoost显式地加入了正则项来控制模型的复杂度,有利于防止过拟合,从而提高模型的泛化能力。
- GBDT在模型训练时只使用了代价函数的一阶导数信息,XGBoost对代 价函数进行二阶泰勒展开,可以同时使用一阶和二阶导数。
- 传统的GBDT采用CART作为基分类器,XGBoost支持多种类型的基分类器,比如线性分类器。
- 传统的GBDT在每轮迭代时使用全部的数据,XGBoost则采用了与随机森林相似的策略,支持对数据进行采样。
- 传统的GBDT没有设计对缺失值进行处理,XGBoost能够自动学习出缺失值的处理策略。
XGBoost使用了一阶和二阶偏导, 二阶导数有利于梯度下降的更快更准。使用泰勒展开取得函数做自变量的二阶导数形式,可以在不选定损失函数具体形式的情况下,仅仅依靠输入数据的值就可以进行叶子分裂优化计算,本质上也就把损失函数的选取和模型算法优化/参数选择分开了. 这种去耦合增加了XGBoost的适用性, 使得它按需选取损失函数, 可以用于分类, 也可以用于回归。
终于有人说清楚了--XGBoost算法 - mantch - 博客园
通俗理解kaggle比赛大杀器xgboost_结构之法 算法之道-CSDN博客_通俗理解kaggle比赛大杀器xgboost
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)