XGBoost原理学习总结

XGBoost原理学习总结

前言

​ XGBoost是一个上限提别高的机器学习算法,和Adaboost、GBDT等都属于Boosting类集成算法。虽然现在深度学习算法大行其道,但很多数据量往往没有太大,无法支持神经网络,并且如风控等一些业务需要可解释的算法模型,所以XGBoost等一众集成学习算法还是有很大的用武之地的。最近在一直准备找工作,重新复盘机器学习算法,所以把最近学得的东西记录在此,方便之后学习回顾。

1. XGBoost与GBDT的联系与区别

​ XGBoost具体上是GBDT的工程实现,但是有很多细节进行了具体的改进,具体上主要有以下三点:

(1) 算法本身的优化

​ 在算法的弱学习器模型选择上,对比GBDT只支持决策树,还可以直接很多其他的弱学习器。在算法的损失函数上,除了本身的损失,还加上了正则化部分。在算法的优化方式上,GBDT的损失函数只对误差部分做负梯度(一阶泰勒)展开,而XGBoost损失函数对误差部分做二阶泰勒展开,更加准确。算法本身的优化是我们后面讨论的重点。

(2) 算法运行效率的优化

​ 对每个弱学习器,比如决策树建立的过程做并行选择,找到合适的子树分裂特征和特征值。在并行选择之前,先对所有的特征的值进行排序分组,方便前面说的并行选择。对分组的特征,选择合适的分组大小,使用CPU缓存进行读取加速。将各个分组保存到多个硬盘以提高IO速度。

(3)算法健壮性优化

​ 对于缺失值的特征,通过枚举所有缺失值在当前节点是进入左子树还是右子树来决定缺失值的处理方式。算法本身加入了L1和L2正则化项,可以防止过拟合,泛化能力更强。

2. XGBoost的原理

原理概述:首先,像GBDT这类集成模型一样,确定一棵树的损失函数,由两部分组成,一部分是预测值与真实值的偏差,另一部分是正则项。接下来的目的就是通过二级泰勒展开式简化损失函数,为下一步寻找损失函数的最小值做准备。此时,是一个含有一次和二次导数的损失函数,并且损失函数是在n个CART决策树上迭代产生的。然后,经过损失函数变形,转化为在不同叶子节点上迭代,通过偏导计算,可得在此节点的最优权重。在选择CART决策树的分裂特征值时,不是采用一般的均方误差,而是根据之前计算的损失函数确定的。

(1)XGBoost的损失函数

​ XGBoost的损失函数与一般的GBDT集成算法类似,都是表示预测值与真实值的偏差,不同的是,XGBoost的损失函数增添了正则项,具体公式如下:

\[L(\phi)=\sum_il(\widehat y_i,y_i)+\sum_i\Omega(f_k) \\ where\ \Omega(f)=\gamma T+\dfrac{1}{2}\lambda||w||^2\tag{1} \]

​ 上式中\(\Omega(f)\)表示正则化项,其中\(T\)为叶子节点数,\(\omega\)为每个叶子节点的权重大小。

​ 泰勒级数的公式为:

\[\begin{array}{*{20}{l}} {f{ \left( {x} \right) }=\mathop{ \sum }\limits_{{n=0}}^{{ \infty }}\frac{{1}}{{n!}}\mathop{{f}}\nolimits^{{n}}{ \left( {\mathop{{x}}\nolimits_{{0}}} \right) }{\mathop{{ \left( {x-\mathop{{x}}\nolimits_{{0}}} \right) }}\nolimits^{{n}}},x \in U{ \left( {\mathop{{x}}\nolimits_{{0}}} \right) }}\tag{2}\\ \end{array} \]

​ 由泰勒级数可以将(1)式的损失函数进行化简。

\[L(t)=\sum_i^nl(y_i,\widehat y_i^{(t-1)}+f_t(X_i))+\sum_i\Omega(f_k) \tag{3} \]

​ 将上式中的\(f_t(X_i))\)用二阶泰勒级数展开,得到如下(4)式:

\[L^{(t)}\approx\sum_i^nl[(y_i,\widehat y_i^{(t-1)})+g_if_t(X_i)+h_i\dfrac{1}{2}f_t^2(X_i)]+\sum_i\Omega(f_t) \tag{4} \]

​ 将上式中常数项\(l(y_i,\widehat y_i^{t-1})\)省略,因为前\(t-1\)颗数已经生成,不会对当前决策树生产生影响。并将以n颗数遍历得到损失函数变换为以T个叶子节点遍历得到损失函数。

\[\tilde L^{(t)}=\sum_{i=1}^n[g_if_t(X_i)+h_i\dfrac{1}{2}f_t^2(X_i)]+\gamma T+\dfrac{1}{2}\lambda\sum_{j=1}^Tw_j^2\\ =\sum_{j=1}^T[(\sum_{i\in I_j}g_i)w_j+\dfrac{1}{2}(\sum_{i\in I_j}h_i+\lambda)w_j^2]+\gamma T\tag{5} \]

(2)通过损失函数,寻找最优权重

​ 如果我们已经求出了第t个决策树的J个最优的叶子节点区域,如何求出每个叶子节点区域的最优解呢?通过式(5),可以通过迭代每个叶子节点来计算决策树总体的损失函数。为了使总体的损失函数最小,在进行迭代时使用贪心的思想,使每一个叶子节点的损失函数最小,具体方法通过对损失函数求\(\omega_j\)的导数,并令其为0,计算得\(w\)权重如下:

\[w_j^*=-\dfrac{\sum_{i\in I_j}g_i}{\sum_{i\in I_j}h_i+\lambda}\tag{6} \]

​ 当每个叶子节点\(w\)取得最优值时,损失函数的表达式可以改写为:

\[\tilde L^{(t)}=-\dfrac{1}{2}\sum_{j=1}^T\dfrac{(\sum_{i\in I}^jg_i)^2}{\sum_{i\in I_j}h^i+\lambda}+\gamma T \tag{7} \]

(3)决策树分裂的最优特征值计算

​ 通过上节已经可以确定叶子节点分裂后的最优解,但是在这之前还应该确定该叶子节点是否应该分裂,以及最优的特征分裂值。由式(7)的损失函数计算,计算每个叶子节点分裂后损失值的减少程度。

\[L_{split}=L_{分裂前损失值}-(L_{分裂后左子树的损失值}+L_{分裂后右子树的损失值})\\ =\dfrac{1}{2}[\dfrac{(\sum_{i\in I_L}g_i)^2}{\sum_{i\in I_L}h_i+\lambda}+\dfrac{(\sum_{i\in I_R}g_i)^2}{\sum_{i\in I_R}h_i+\lambda}-\dfrac{(\sum_{i\in I}g_i)^2}{\sum_{i\in I}h_i+\lambda}]-\gamma\tag{8} \]

​ 遍历此特征的全部数值,当\(L_{split}\)大于0,且取到最大值时确定最优特征值;如果都小于等于0,则此节点分裂结束。

(4)XGBoost算法中其他方面的优化

  • XGBoost的并行计算

​ XGBoost为boosting系列集成算法,实质上是不能进行并行计算的,但这里说的并行计算实际上是在构建决策树时进行特征搜索是并行的,在此之前各个特征就已经排好序被存储block中。

  • 特征分裂值的选取有两种方法

​ 第一就是上文中使用的逐值遍历的方法,文中称为Basic Exact Greedy Algorithm;另一种就是近似分割算法, 精确贪心算法虽然很强大,但是当数据无法完全加载到内存中或者在分布式的条件下,这种基于穷举的分裂点寻找方法效率就会非常低下。于是作者提出了一种近似分割的算法,这种算法首先通过加权分位数的算法选出了一些可能的分裂点,然后再遍历这些较少的分裂点来找到最佳分裂点。

  • 通过收缩步长和列采样的方法来避免过拟合

收缩步长即在每一次迭代的过程中,对拟合的残差乘以一个收缩系数,从而限制当前迭代过程的学习步长;列采样即本次迭代参与优化的特征维度并不是所有的维度,而是通过采样得到的维度,更少的选择会让算法不容易将训练集的残差拟合得过好,从而造成过拟合;

3. XGBoost常见问题总结

(1)XGBoost为什么通过二阶泰勒级数展开优化损失函数?

​ 首先,采用二阶泰勒级数足以接近损失函数;通过引入二阶泰勒级数把损失函数化简有利于后续的计算,例如,将在不同决策树迭代计算损失函数转化为在不同叶子节点上迭代产生损失函数;还有就是有利于算法本身的可扩展性。

(2)XGBoost是怎样处理缺失值的?

​ XGBoost 在处理带缺失值的特征时,先对非缺失的样本进行排序,对该特征缺失的样本先不处理,然后在遍历每个分裂点时,将这些缺失样本分别划入左子树和右子树来计算损失然后求最优。如果训练样本中没有缺失值,而预测过程中出现了缺失值,那么样本会被默认分到右子树。

posted @ 2019-12-02 21:01  ffjsls  阅读(396)  评论(0编辑  收藏  举报