XGBoost是Boosting算法的一种,Boosting算法的核心思想就是将许多基模型加在一起,形成一个强分类器。XGBoost就是将许多的CART树结合在一起,对于那些一颗树无法很好拟合的数据用多颗CART树不断地去逼近。本文从Boosting方法-->BDT(提升树)-->GBDT(梯度提升树)-->XGBoost的脉络来描述XGBoost.
Boosting方法是一种用来提高弱分类算法准确度的方法,这种方法通过构造一个基函数系列,然后以一定的方式将他们组合成一个预测函数。所以Boosting是一系列算法,其中包含AdaBoost,GradientBoosting,LogitBoost等算法。Boosting算法有两个重要的要素:加法模型和前向分步算法。
加法模型可以表达成如下的形式:
其中,表示基模型,表示样本集,表示基模型的参数,表示基模型的系数。如果要能够应用加法模型,则要求这个选取的基模型是要可加的,一般来说我们会选择树模型或者线性模型来作为基模型,不太适合选取较复杂的模型。既然有了模型的表达式,那么要构造模型,只需要给出经验风险然后极小化经验风险即可,经验风险最小化可以表达为如下形式:
从上面这个式子可以看出,要求得极小值还是比较复杂的,因为L中还有一个求和函数,所以这个时候引入前向分步算法来转换成迭代的方式来一步步的逼近最优值。前向分步算法的基本思想:从一个最基础的基模型开始,逐步优化,每次学习只学习一个基函数及其系数,逐步逼近上述目标函数,具体的,每一步只需要优化如下:
前向分步算法的伪代码如下:
算法 1. 前向分步算法
输入:训练集,其中,损失函数,基函数集合
第1步:构造初始模型
第2步:对于
1.构造每一步迭代的损失函数
2.极小化损失函数
3.更新当前模型
第三步:输出加法模型
提升决策树就是用Boosting的方法,以回归树作为基模型,来构造加法模型,加法模型可以表示成如下的形式:
其中,表示决策树,表示样本集,表示决策树的参数。与Boosting算法一样,BDT也是采用前向分步算法,以一颗树桩开始进行迭代,那么我们可以依照着Boosting算法的流程给出每一步迭代的模型:
然后通过经验风险最小化来确定下一步的决策树的参数:
得到了上面这个式子,如果给出了决策树的具体定义形式,我们应该就可以求出每一步的参数值了。我们知道,对于一颗决策树来说,在给定一个样本之后,是通过一系列的中间节点的判断然后最终都会走到一个叶子节点上,也就是说对于一颗确定的决策树来说,一个样本也就对应着一个叶子界面。
上面说过,BDT是以回归树作为基模型,输入空间假设为,其实我们是把输入空间进行了一个划分,假设划分成了个区域,那么这个区域是互不相交的,对于一颗回归树来说,每个区域的输出都是一个常量值,假设为,那么我们就可以定义决策树的形式:
上式中的为指示函数。有了决策树的定义之后,假设经验风险我们采用的是平方误差,我们把经验风险的计算打开来看一下:
仔细看上述转换之后的形式,就会发现,是模型在经过了m-1次学习之后与实际值的误差,那么上面这个式子也就可以理解为对于决策树的第m次学习就是在学习这个残差,接下来只要在这个基础上使得经验风险最小化就可以得到第m次学习的决策树的参数,下面给出BDT的前向分步算法:
算法 2. 回归问题的提升决策树算法
输入:训练集,其中,损失函数
第1步:构造第一颗决策树,其实就是一颗只有一个根节点的树桩
第2步:对于
1.计算当前模型与实际值的残差
2.对于残差拟合出一颗决策树
3.更新当前决策树模型
第三步:输出提升决策树模型
梯度提升决策树的大致步骤和BDT是一致的,唯一的不同点就在于梯度提升决策树在第m次迭代时,不是去拟合残差,而是使用损失函数的负梯度在当前模型的值作为残差的近似值,去拟合一个回归树。
至于为什么这么干,肯定是因为这么干可以得到更高的精度,我这里也只能从一个角度去看待这个问题(如有不对请指正):对于梯度上升法,我们一般的做法都是不断地优化模型的参数,比如线性回归时,我们是在参数空间来优化模型,那么回过头来看一下加法模型,每一次的迭代其实也是这样的形式,,只不过我们加上的是一个基模型,那么我们是否可以把在参数空间上解决问题的思路上升到假设空间中,这也就是这么干的思路。
定义当前模型的损失函数的负梯度为:
算法 3. GBDT的梯度提升算法
输入:训练集,其中,损失函数
输出:梯度提升决策树模型
第1步:初始化
第2步:对
1. 对,计算
2. 对拟合一个回归树,得到第m棵树的叶节点区域
3. 对,计算
4. 更新
第3步:得到梯度提升决策树
XGBoost在GBDT的基础上加了亮点:正则化项和引入了二阶泰勒展开。回顾一下上面提到的几种加法模型,发现在构造树的时候,我们都是一直不断的去拟合残差,不断的去逼近实际的曲线,如果考虑到实际情况比较复杂而且还带有噪点时,就很有可能产生过拟合。而且我们之前除了去调整在树生长的时候的阈值之外,没有其他的办法去控制树的生长,因此,XGBoost在之前GBDT的基础之上,在树的生成过程中还考虑到了树的复杂程度。至于引入二阶泰勒展开,则是因为展开到二阶的精度要比一阶的精度高。
先给出XGBoost对于决策树模型的定义:
其中是一个映射函数,将,其中T表示的是叶子节点的个数,可以理解为对于一个输入样本x,将其通过之后映射到叶子节点中的某一个的下标。,可以理解为w是一个数组,其中存着每一个叶子节点对应的输出。
再定义XGBoost的模型预测输出:
其中k表示第k棵决策树。损失函数定义如下:
其中就是正则化项,其描述的就是树的复杂程度,展开为,从这个式子中大致可以看出,T是叶子节点的个数,当树越复杂时其叶子节点的数量必然叶会越多,第二项的定义形式我目前也没找到一个好的解释。
有了损失函数之后,就是通过不断地迭代去优化,给出第t轮的目标函数如下:
将l在处二阶泰勒展开:
其中.
由于我们优化只需要考虑变量就好,所以将上式化简,移除常数项:
上面的式子中,包含两个求和,这个在迭代过程中不太好办,于是就想办法把后面的求和合并到前面那个里面去。这个地方我们观察一下后面的那个求和的式子,其中T表示叶子节点的数量,j遍历所有的叶子节点,然后考虑到一个点:所有的样本进来之后,不管走的哪条路,最终都会落在一个叶子节点上,那么我们从叶子节点的角度来看的话,就是一个叶子节点对应一个或多个输入样本,这样的一对多的关系,那么再看前面的求和的式子,是对所有的输入样本进行求和,那么其实这两个求和的迭代次数是一样的,那么就有可能把这两个合并起来。具体是这么干的:先引入一个在叶子节点上的下标集合,表示第j个叶子节点上面,有着一个集合,这个集合里面存储着样本的下标(也就是落到这个叶子节点上的样本有哪些),然后有了这个之后,后面的求和就可以和前面的合并了。
接下来,令
可得到
代入每个叶结点 j 的最优分数,得到最优化目标函数值
假设??和??分别为分裂后左右结点的实例集,令?=??∪??,则分裂后损失减少量由下式得出,下面这个式子可以用来评估分裂之后的增益。