XGBoost(eXtreme Gradient Boosting)中文名叫极端梯度提升,可以看出它是一种gradient boosting算法,事实上,XGBoost是GBDT的一种高效实现,并对GBDT做了一定的改进和优化,所以咱们最好结合着上一篇讲过的GBDT来理解XGBoost。
1 XGBoost 原理
我们先来捋一遍XGBoost的原理及实现,首先,XGBoost和GBDT的基本思想都是一样的,通过加法模型和前向分步算法来一步一步的靠近目标,得到很多个弱学习器,将这些弱学习器的预测值加起来作为最终的预测结果,如下图所示预测家人对电脑游戏的喜欢程度:
在前向分布算法中,每一步的前面各步的学习器都已经确定,因此针对每一步的目标函数是:
还是从损失函数是平方损失MSE开始理解,将MSE代入并展开可得(下式中两个constant不同,第二个多了个项):
这个形式非常好,是当前变量的一次项(残差)+二次项的形式,由这个形式去继续推导和优化的话,想必是很nice的,但是对一般的损失函数来说,是很难展开成这个形式的,这意味着我们由MSE推导出的结果不具有普适性,每个损失函数的推导过程都会不一样,怎么办呢?如果能让所有损失函数都成为一次项+二次项的形式就好了,于是大神们想到了泰勒展开,没错,这就是二阶泰勒展开在XGBoost中的重要作用之一,它统一了损失函数的形式,解耦了损失函数与弱学习器之间的联系,并以此优良特性支持了用户自定义损失函数,我们来看看二阶泰勒展开的过程及结果:
其中为定值,令:
则目标函数为:
这样形式就统一成一次项+二次项的形式了,
上式中就是我们在当前轮迭代中生成决策树的目标函数,.可以发现,一个重要的特点是这个目标函数中,我们要求的与损失函数相关的项是解耦的,这是一种“模块化”的形式,正是这种损失函数和弱学习器的独立性,使得XGBoost 能够支持我们自定义损失函数,只要我们定义的损失函数二阶可导,就不会影响XGBoost 的运行流程。
用二阶展开还有一个好处,我们知道GBDT中使用损失函数的一阶泰勒展开作为伪残差来近似的,而二阶泰勒展开式展开后与原目标函数更接近,算法收敛的更快更准,类似于牛顿法对比梯度下降的优势。
至此目标函数中损失部分的形式确定了,还剩下正则项,在每一步的目标函数中,正则项表示当前步弱学习器(决策树)的复杂程度,对决策树的复杂度我们一般考虑其叶子结点数量,XGBoost还加入了对其叶子结点取值的正则,首先我们看看决策树的表示:
因此正则化可表示为:
现在,综合我们以上对XGBoost 算法原理及正则化的讨论,可得我们的目标函数的形式:
我们把每个叶子节点区域样本的一阶和二阶导数的和单独表示如下:
损失函数可以表示为:
2 损失函数优化求解
目标是要最小化,我们知道,中其实有两个未知数是需要计算的,一个是比较明显的,表示在每个叶子结点上的取值;另一个是,表示这棵树有多少个叶子结点。先看的求法,这个比较简单,就是一个二次函数的极值求法,求导可得:
代入目标函数可得:
要求叶子结点数量,实际上就是树的分裂问题,而且这次的分裂是有目标函数的,这跟以前的决策树分裂不太一样,以前都是自己根据样本和label设定规则分裂的。既然有目标函数我们就按使得目标函数减小的方法分裂,我们定义:
这个很好理解,其中的几项分别是: 1) 分裂后新的左子树的目标函数值; 2) 分裂后新的右子树的目标函数值;3) 为分裂时的原始树的目标函数值;4) 正则项。思路就是找到分裂后使得目标函数减小最多的特征,以这个特征作为分裂特征即可。
3 正则化
除了原理中提到的目标函数中的正则项外,XGBoost还使用了另外两种技术来进一步防止过拟合。
-
第一种技术是由Friedman引入的Shrinkage。Shrinkage与模型优化中的学习率相似,收缩减小了每棵树的影响,为未来树改进模型留下了空间,在GBDT和Adaboost中也有这种方法的使用;
-
第二种技术是列(特征)子抽样。使用列子抽样比传统的行子抽样(XGBoost也支持行子抽样)更能防止过拟合,这就类似于随机森林中的做法,而且列子样本的使用也加速了后面描述的并行算法的计算。
4 缺失值的处理
缺失值导致三个问题:
- 分裂特征选择时怎么处理存在缺失值的特征?
- 对于选定的分裂特征存在缺失值的情况,缺失值分裂到哪一边?
- 预测时遇到缺失值,往那边分支走?
不同于GBDT中靠决策树自身的能力来处理缺失值,xgboost模型提供了自己特有的缺失值处理方法,下面是针对上述三个问题xgboost给出的解决方法:
- 在寻找split point的时候,不会对该特征为missing的样本进行遍历统计,只对该列特征值为non-missing的样本上对应的特征值进行遍历,通过这个技巧来减少了为稀疏离散特征寻找split point的时间开销。在逻辑实现上,为了保证完备性,会分别处理将missing该特征值的样本分配到左叶子结点和右叶子结点的两种情形,计算增益后选择增益大的方向进行分裂即可,如下图所示:
-
根据上面的方法得到分裂后缺失值的方向,可以作为缺失值或者指定的值指定分支的默认方向,这能大大提升算法的效率;
-
如果在训练中没有缺失值而在预测中出现缺失,那么会自动将缺失值的划分方向放到右子树。
5 工程上的优化——提速
树学习中最耗费时间的部分是将数据排序。为了降低排序成本,我们建议将数据存储在内存单元中,我们称之为块。每个块中的数据以压缩列(compressed column, CSC)格式存储,每列按对应的特征值排序。这个输入数据布局只需要在训练之前计算一次,并且可以在以后的迭代中重用。
6 总结
主要对比GBDT
- XGBoost使用CART做基分类器的时候,显式地加入了正则项来控制模型的复杂度,有利于防止过拟合,提高泛化能力 ;
- 正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和,能更有效的防止过拟合;
- GBDT在模型训练的时候只使用了一阶导数信息,XGBoost对代价函数进行了二阶泰勒展开,同时使用一阶、二阶导数信息,并且可以自定义代价函数;
- 传统GBDT使用CART作为基分类器;XGB支持多种类型的基分类器,如线性分类器,这时候其实XGBoost就是一个线性模型;
- 缺失值: 传统GBDT没有专门针对缺失值进行处理;XGBoost有专门的缺失值的处理策略:
- 指定缺失值的分隔方向:可以为缺失值或者指定的值指定分支的默认方向,为了保证完备性,会分别处理将missing该特征值的样本分配到左叶子结点和右叶子结点的两种情形,分到那个子节点带来的增益大,默认的方向就是哪个子节点,这能大大提升算法的效率;
- 忽略缺失值:在寻找splitpoint的时候,不会对该特征为missing的样本进行遍历统计,只对该列特征值为non-missing的样本上对应的特征值进行遍历,通过这个工程技巧来减少了为稀疏离散特征寻找splitpoint的时间开销 。
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。每次迭代,增加新的模型,在前面成上一个小于1的系数,降低优化的速度,每次走一小步逐步逼近最优模型比每次走一大步逼近更加容易避免过拟合现象;
- 抽样
- 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样(即每次的输入特征不是全部特征),不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
- 行抽样:传统GBDT在每轮迭代时使用全部的数据;XGB则采用了类似RF的策略,支持对数据进行采样
- 并行化处理:
- 在训练之前,预先对每个特征内部进行了排序找出候选切割点,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。
- 在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行,即在不同的特征属性上采用多线程并行方式寻找最佳分割点。
BoostedTree.pdf ——Tianqi Chen
XGBoost: A Scalable Tree Boosting System ——Tianqi Chen
XGBoost文档
网友评论