大家好,又见面了,我是你们的朋友全栈君。
说明:本文是阅读XGBoost有关的论文和一些博客后的入门笔记。有什么不足之处还望大家多多赐教,欢迎交流,转载。
一. 前言
- XGBoost是提升方法中的一个可扩展的机器学习系统。XGBoost在许多机器学习和数据挖掘问题中产生了广泛的影响。2015年发表在Kaggle竞赛的博客的29个冠军解决方案中,有17个是使用XGBoost解决的,其中有8个是仅使用了XGBoost方法去训练模型,剩余的是用XGBoost和其他模型相结合使用的。相比较而言,第二个受欢迎的方法是深度神经网络,有11个是使用该方法的。
- XGBoost成功的最重要因素就是它在任何场景下的可扩展性。XGBoost系统在单台机器上的运行速度比现有流行的解决方案快10倍以上,并可在分布式或内存限制设置中扩展到数十亿个示例。XGBoost的可扩展性是由于在它的系统实现中的一些创新,包括:
- 为处理稀疏数据使用了一个新颖的树学习算法;
- 理论上合理的加权分位数草图过程,使得能够在近似树学习中处理实例权重;
- 平行和分布式计算使得学习更快,从而能够更快的进行模型探索;
- 最重要的是XGBoost使用核外计算并且能够让数据科学家在台式机上处理数以亿计的示例;
- 更令人兴奋的是结合这些技术使端到端系统以最少的集群资源扩展到更大的数据。
下面开始介绍XGBoost的模型训练原理
二. XGBoost原理
1. 学习目标
在讨论学习目标之前,先说一说XGBoost是如何预测输出值的。XGBoost是一个树集成模型,它使用的是K(树的总数为K)个树的每棵树对样本的预测值的和作为该样本在XGBoost系统中的预测,定义函数如下:
对于所给的数据集有n个样本,m个特征,定义为:
其中Xi表示第i个样本,yi表示第i个样本的类别标签。CART树的空间为F,如下:
其中q表示每棵树的结构映射每个样本到相应的叶节点的分数,即q表示树的模型,输入一个样本,根据模型将样本映射到叶节点输出预测的分数;Wq(x)表示树q的所有叶节点的分数组成集合;T是树q的叶节点数量。
所以,由(1)式可以看出,XGBoost的预测值为每棵树的预测值之和,即每棵树相应的叶节点的得分之和(Wi的和,Wi表示第i个叶节点的得分)。
我们的目标就是学习这样的K个树模型f(x).。为了学习模型f(x),我们定义下面的目标函数:
其中,(2)式右边第一项为损失函数项,即训练误差,是一个可微的凸函数(比如用于回归的均方误差和用于分类的Logistic误差函数等),第二项为正则化项,即每棵树的复杂度之和,目的是控制模型的复杂度,防止过拟合。我们的目标是在L(φ)取得最小化时得出对应的模型f(x)。
由于XGBoost模型中的优化参数是模型f(x),不是一个具体的值,所以不能用传统的优化方法在欧式空间中进行优化,而是采用additive training的方式去学习模型。每一次保留原来的模型不变,加入一个新的函数f到模型中,如下:
预测值在每一次迭代中加入一个新的函数f目的是使目标函数尽量最大地降低。
因为我们的目标是最小化L(φ)时得到模型f(x),但是L(φ)中并没有参数f(x),所以,我们将上图中的最后一式代入L(φ)中可得到如下式子:
对于平方误差(用于回归)来说(3)式转换成如下形式:
对于不是平方误差的情况下,一般会采用泰勒展开式来定义一个近似的目标函数,以方便我们的进一步计算。
根据如下的泰勒展开式,移除高阶无穷小项,得:
(3)式等价于下面的式子:
由于我们的目标是求L(φ)最小化时的模型f(x)(也是变量),当移除常数项时模型的最小值变化,但是取最小值的变量不变(比如:y=x^2+C,无论C去何值,x都在0处取最小值)。所以,为了简化计算,我们移除常数项,得到如下的目标函数:
定义 为叶节点j的实例,重写(4)式,将关于树模型的迭代转换为关于树的叶子节点的迭代,得到如下过程:
此时我们的目标是求每棵树的叶节点j的分数Wj,求出Wj后,将每棵树的Wj相加,即可得到最终的预测的分数。而要想得到最优的Wj的值,即最小化我们的目标函数,所以上式对Wj求偏导,并令偏导数为0,算出此时的W*j为:
将W*j代入原式得:
方程(5)可以用作得分(score)函数来测量树结构q的质量。该得分类似于评估决策树的不纯度得分,除了它是针对更广泛的目标函数得出的。下图表示得分(score)是如何被计算的:
由上图可以看出,当我们指定一颗树的结构的时候,每棵树的得分(score)只与损失函数的一阶导数和二阶倒数相关(γ和λ是在实际应用中需要自己调参的),而该得分表示我们在目标上面最多减少多少。我们可以把它叫做结构分数(structure score)。你可以认为这个就是类似吉尼系数一样更加一般的对于树结构进行打分的函数。
到这里,我们的XGBoost学习目标的原理已经介绍完毕,接下来就是如何进行节点的切分了。2. 节点的划分
树学习的其中之一的重要问题就是找到最好的节点划分,而节点划分的目的是寻找一个最优结构的树。假设IL和IR是一个节点切分后的左右节点,I等于IL和IR的并集。然后切分后的损失函数定义如下:
上面的式子通常在实践中被使用来评估切分的候选节点。我们的目标是希望找到一个特征及其对应的大小,使得上式取得最大值。
节点划分有两种方式,一是基本精确的贪心算法(Basic Exact Greedy Algorithm),二是近似算法(Approximate Algorithm)。下面我们来分别介绍这两种方式。2.1 基本精确的贪心算法(Basic Exact Greedy Algorithm)
为了找到节点最好的划分,该算法的原理是在所有特征上枚举所有的可能的划分,我们称这个算法为Basic Exact Greedy Algorithm。大多数已存在的单台机器上的树提升算法库都是用这种方法实现的,例如scikit-learn,R’s gbm 以及 XGBoost的单机版本都支持这种算法。该算法要求为连续特征枚举所有可能的切分,这对计算机的要求很高,所以该算法为了有效的做到这一点,首先根据特征值排序数据并且按照顺序访问数据,以累积方程(6)中结构分数的梯度统计量。该算法如下:
2.2 近似算法
Basic Exact Greedy Algorithm是一个非常精确的算法,因为它枚举的所有可能的切分点。但是,当数据不能完全的加载到内存时,它可能不是特别有效地。同样的问题也出现在分布式的设置中。为了有效的支持在这两种设置中的有效的梯度提升,一个近似算法需要被使用。该算法首先根据特征分布的百分位数提出n个候选切分节点,然后,算法将位于相邻分位点之间的样本分在一个桶中,在遍历该特征的时候,只需要遍历各个分位点,从而计算最优划分。注意到上面算法流程中表明有全局的近似(global)和局部(local)的近似,所谓全局就是在新生成一棵树之前就对各个特征计算分位点并划分样本,之后在每次分裂过程中都采用近似划分,而局部就是在具体的某一次分裂节点的过程中采用近似算法。该算法如下所示:
以上两个算法都应用在了XGBoost中。
接下来,最后一个问题就是近似算法中的如何根据分位数来提出候选切分点。2.3 带权重的分位数草图(Weighted Quantile Sketch)
Weighted Quantile Sketch是近似算法中的一个重要步骤,主要用于解决近似算法中如何选取候选切分点的问题。通常,特征的百分位数用于使候选节点均匀地分布在数据上。也就是在特征集上选取一个百分数,然后根据这个百分数来依次的选取候选节点。比如某个特征的样本点是1~100,特征的百分位数设为2%,则候选节点的选择就是100*0.02*1=2,4,…,100。用数学公式表示,定义一个rank function,如下:
上式表示特征值k小于z的实例的比例。其中:
表示每个训练样本的第k个特征值和二阶梯度值。
其中ε是近似因子,也就是切分百分数。这意味着大概有1/ε个候选切分点。
到这里,XGBoost的理论部分已经介绍完毕。有不足之处希望指出,一起学习。接下来就是介绍XGBoost的一些优缺点。三. XGBoost的优缺点:
3.1 与GBDT相比:
1)GBDT以传统CART作为基分类器,而XGBoost支持线性分类器,相当于引入L1和L2正则化项的逻辑回归(分类问题)和线性回归(回归问题);
2)GBDT在优化时只用到一阶导数,XGBoost对代价函数做了二阶Talor展开,引入了一阶导数和二阶导数。XGBoost支持自定义的损失函数,只要是能满足二阶连续可导的函数均可以作为损失函数;
3)XGBoost在损失函数中引入正则化项,用于控制模型的复杂度。正则化项包含全部叶子节点的个数,每个叶子节点输出的score的L2模的平方和。从Bias-variance tradeoff角度考虑,正则项降低了模型的方差,防止模型过拟合,这也是xgboost优于传统GBDT的一个特性。
4)当样本存在缺失值是,xgBoosting能自动学习分裂方向,即XGBoost对样本缺失值不敏感;
5)XGBoost借鉴RF的做法,支持列抽样,这样不仅能防止过拟合,还能降低计算,这也是xgboost异于传统gbdt的一个特性。
6)XGBoost在每次迭代之后,会将叶子节点的权重乘上一个学习率(相当于XGBoost中的eta,论文中的Shrinkage),主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点;
7)XGBoost工具支持并行,但并行不是tree粒度的并行,XGBoost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值),XGBoost的并行是在特征粒度上的。XGBoost在训练之前,预先对数据进行了排序,然后保存为(block)结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个块结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行;
8)可并行的近似直方图算法,树结点在进行分裂时,需要计算每个节点的增益,若数据量较大,对所有节点的特征进行排序,遍历的得到最优分割点,这种贪心法异常耗时,这时引进近似直方图算法,用于生成高效的分割点,即用分裂后的某种值减去分裂前的某种值,获得增益,为了限制树的增长,引入阈值,当增益大于阈值时,进行分裂;
9) XGBoost的原生语言为C/C++,这是也是它训练速度快的一个原因。
3.2 与LightGBM相比:
1)XGBoost采用预排序,在迭代之前,对结点的特征做预排序,遍历选择最优分割点,数据量大时,贪心法耗时,LightGBM方法采用histogram算法,占用的内存低,数据分割的复杂度更低,但是不能找到最精确的数据分割点;
2)XGBoost采用level-wise生成决策树策略,同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合,但很多叶子节点的分裂增益较低,没必要进行更进一步的分裂,这就带来了不必要的开销;LightGBM采用leaf-wise生长策略,每次从当前叶子中选择增益最大的叶子进行分裂,如此循环,但会生长出更深的决策树,产生过拟合,因此 LightGBM 在leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合)。另一个比较巧妙的优化是 histogram 做差加速。一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。
四. 参考文献/博文
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/138711.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...