xgboost 学习总结

    最近重温了一下XGBoost,现在总结一下XGBoost的基本常识,公式是自己在草稿纸上推导然后码出来的,和原始论文不太相同。在推导过程中也不断多问自己几个为什么。

 

1. XGBoost 的数学推导:

 

   1.1  目标与惩罚函数

       设$\lbrace(x_{i},y_{i})\mid x_{i}\in\mathbb{R}^{P}, y_{i}\in\mathbb{R}, i=1,...,N\rbrace$为训练数据集,可以来自于分类也来自于回归问题,我们希望用一种前向分布可加树模型$F=\sum_{i=1}^{M}f_{i}$训练该数据集,使得其目标损失函数:

\begin{equation}L(F)=\sum_{i}^{N}l(F(x_{i}),y_{i})\end{equation}

尽量小,其中l为函数$l:\mathbb{R}^{2}\longrightarrow \mathbb{R}$

 

不仅仅如此,我们还得加入限制模型复杂度的惩罚项.。与传统的GBDT不同的是,XGboost在生成树的阶段就考虑到对模型复杂度的控制,而不是等到剪枝阶段,下面我们定义惩罚项。

       对于任意的决策树$T$, 我们记其叶节点个数为$\vert T\vert$, 而函数$d:\mathbb{R}^{P}\longrightarrow \lbrace1,2,...,\vert T\vert\rbrace$是将模型的输入值分配到决策树叶节点的函数,也就是值将输入值$x$分配到第$d(x)$个叶结点。其次,我们记该决策树在第$i$个叶节点上统一赋值$w_{i}$,我们称之为第$i$个叶结点的权重。那么我们知道,实际上该决策树模型对于输入值$x$所输出的预测值为$w_{d(x)}$。

      想象一下,如果叶节点的权值的绝对值过高,很有可能是由于训练数据集的输出噪声造成的,特别是在树的深度过长,节点过多的时候,最终影响到模型的泛化能力,所以有必要通过一个惩罚项限制节点的权值。我们不妨考虑一下加入$l_{2}$惩罚项:

\begin{equation}\frac{\lambda}{2}\sum_{i=1}^{\vert T\vert}w_{i}^{2}\end{equation}

     其次,数的叶节点过多,也就是树的结构过于复杂也会造成类似的问题,鲁棒性差,泛化能力差,所以叶应该考虑限制树叶节点的个数。综上考虑我们考虑决策树的惩罚项:

\begin{equation}\Omega_{\gamma,\lambda}(f)\triangleq\gamma\vert T\vert+ \frac{\lambda}{2}\sum_{i=1}^{\vert T\vert}w_{i}^{2},\end{equation}

因此这个时候我们可以考虑加入惩罚项的新的目标函数:

\begin{equation}L(F)=\sum_{i=1}^{N}l(F(x_{i}),y_{i})+\sum_{i=1}^{N}\Omega_{\gamma,\lambda}(f_{i}),\end{equation}

并应用前向分步可加模型使得其尽量小。

 

 

   1.2 具体推导

      假设第$m-1$步我们已经得到一个模型:

$$F_{m-1}=\sum_{i=1}^{m-1} f_{i}$$

现在我们想办法得到一个模型$f_{m}$使得:

$$L(F_{m})=\sum_{i=1}^{N}l(F_{m-1}(x_{i})+f_{m}(x_{i}),y_{i})+\sum_{i=1}^{N}\Omega_{\gamma,\lambda}(f_{i})$$

尽量小。

     现在我们假设模型$f_{m}$对应的一棵决策树为$T$, 且和1.1中一样记叶节点个数记为$\vert T\vert$,第$i$个叶节点的权值为$w_{i}$。注意到,由泰勒展开式,我们有:

\begin{equation}l(F_{m-1}(x_{i})+f_{m}(x_{i}),y_{i})\approx l(F_{m-1}(x_{i}),y_{i})+(\partial_{1}l)(F_{m-1}(x_{i}),y_{i})f_{m}(x_{i})+\frac{1}{2}(\partial^{2}_{11}l)(F_{m-1}(x_{i}),y_{i})f^{2}_{m}(x_{i}).\end{equation} 

    为了方便起见,我们令:

$$g_{i}=(\partial_{1}l)(F_{m-1}(x_{i}),y_{i}),\ \  h_{i}=(\partial^{2}_{11}l)(F_{m-1}(x_{i}),y_{i}),$$

则:

\begin{split}L(F_{m})\approx &\sum_{i=1}^{N} l(F_{m-1}(x_{i}),y_{i})\newline &+\sum_{i=1}^{N}g_{i}f_{m}(x_{i})+\frac{1}{2}h_{i}^{m-1}f^{2}_{m-1}(x_{i}) \newline &+\gamma\vert T\vert+\frac{1}{2}\lambda ||w||^{2} \end{split}

由之前的讨论,$f_{m}(x_{i})=w_{d(x_{i})}$, 由此我们可以计算上式第二项:

\begin{split}&\sum_{i=1}^{N}g_{i}f_{m}(x_{i})+\frac{1}{2}h_{i}f^{2}_{m}(x_{i})& \newline &=\sum_{i=1}^{N}g_{i}w_{d(x_{i})}+\frac{1}{2}h_{i}w^{2}_{d(x_{i})} \newline &=\sum_{j=1}^{T_{m}}\sum_{i:d(x_{i})=j}g_{i}w_{j}+\frac{1}{2}h_{i}w^{2}_{j} \newline &=\sum_{j=1}^{\vert T\vert}G_{j}w_{j}+\frac{1}{2}H_{j}w^{2}_{j}\end{split}

所以我们有:

\begin{equation}\begin{split}L(F_{m})\approx &\sum_{i=1}^{N} l(F_{m-1}(x_{i}),y_{i})+\sum_{j=1}^{\vert T\vert}[G_{j}w_{j}+\frac{1}{2}(H_{j}+\lambda)w^{2}_{j}]+\gamma\vert T\vert \newline=&\sum_{i=1}^{N} l(F_{m-1}(x_{i}),y_{i})+\sum_{j=1}^{\vert T\vert}\frac{1}{2}(H_{j}+\lambda)(w_{j}+\frac{G_{j}}{H_{j}+\lambda})^{2}-\sum_{j=1}^{\vert T\vert}\frac{G_{j}^{2}}{2(H_{j}+\lambda)} \newline +&\gamma\vert T\vert\end{split}\end{equation}

在此,可以得出一个重要结论:固定决策树$T$,则当:

$$w_{j}=-\frac{G_{j}}{H_{j}+\lambda},$$

对任意$j=1,...,\vert T_{m}\vert$ 的时候,$L(F_{m})$ 最小。

 

 

   1.3 结点的分裂算法:

       就如在CART算法中一样,我们考虑如何构造树$T$, 也即考虑如何逐步分裂结点。 由(6)我们很容易知道,任意一个结点以某种方式分裂为左右两个节点后其Score Function 的增益(也是目标函数L的减少量)为: 

$$Gain =\frac{1}{2}[\frac{G_L^2}{H_{L}+\lambda}+\frac{G_R^2}{H_{R}+\lambda}-\frac{(G_{L}+G_{R})^2}{H_{L}+H_{R}+\lambda}]-\gamma$$

那么我们在逐步分裂节点的时候,就可以考虑所有特征的所有切分点,然后选出使得相应的Gain最大的那个特征以及切分点,以完成一次结点的分裂。值得注意的是Gain的表达式中的前半部分$\frac{1}{2}[\frac{G_L^2}{H_{L}+\lambda}+\frac{G_R^2}{H_{R}+\lambda}-\frac{(G_{L}+G_{R})^2}{H_{L}+H_{R}+\lambda}]$ 其实是在我们不要关于树的叶节点个数惩罚项$\gamma \vert T\vert$的时候的增益。注意到如果树的深度已经足够大导致对所有的分裂方式都有:$$\frac{1}{2}[\frac{G_L^2}{H_{L}+\lambda}+\frac{G_R^2}{H_{R}+\lambda}-\frac{(G_{L}+G_{R})^2}{H_{L}+H_{R}+\lambda}]\leq\gamma$$的时候我们则选择不分裂该节点,这就是增加惩罚项$\gamma \vert T\vert$的意义。

 

2. 更多实现细节

  

    2.1 学习步长缩小 shrinkage 与列采样 column subsampling

      和GBRT一样,在XGBoost中每进行一次Boosting完毕后将新的树的每个权值乘以一个小于1的数(学习率),这样可以降低每一颗树对于后续的树的影响并且为后续的学习留下更多空间。同样也可以每次学习仅仅考虑部分特征而不是全部,这样也可以一定程度防止过拟合。根据部分XGBoost用户反映,列采样在降低过拟合方面的效果甚至强于传统的行采样[1],并且可以加快分裂节点时并行运算的速度。

 

   2.2 更多防止过拟合的方法

     XGBoost提供了更多方法剪枝,例如控制叶节点的最小权重和(min_child_weight),控制树的深度(max_length), 加入权重的L1惩罚项等等。

 

   2.3 节点分裂的近似算法

     CART中的节点分裂算法威力强大,因为其遍历了所有可能的分裂方式,但是有运算量大,难以并行运算的特点,而XGBoost采用了一种更加高效的近似算法。

     如下图是CART中传统的方法进行节点分裂的算法:

   xgboost 学习总结

 该算法遍历所有的节点分裂方式,能恰好找出最佳分裂方式但是运行效率比较低。

      XGBoost中采取了一种近似的方式如下:

xgboost 学习总结

   也就是,对于每一个特征,用某种方法(具体用如何分裂也有很多细节,详细见[1]的3.3)我们将该特征的取值范围分成若干个槽,计算每个槽中所有数据的g,h的和然后存储起来,然后可以并行对于每个特征做分裂最后整合找出最优分裂方式。注意到该算法并没有考虑所有分裂方式,不是像上面算法一样将数据逐一添加到左边节点而是分批量添加数据到左边,而且由于G,H已经储存,所以可以直接对不同特征并行运算。

 

   3.3 节点分裂时对稀疏矩阵的处理

  在实践中很多时候输入矩阵会是稀疏的,例如当矩阵经过独热编码后。XGBoost将0值的数据和非0值放在一起统一处理,具体算法见下图:

xgboost 学习总结

可以看出,我们先考虑所有0值都在右边的时候,看看最优分裂方式,再考虑将所有0值放到左边的时候的最优方式,再从中选出最优方式,而每次我们只依次添加非0值到相反方向,这样做可以大大减小运算量并且自动将分配了0值。

 

3. 与GBRT比较:

 

    3.1 XGBoost 在目标函数中就加入了惩罚项,可通过最优化该目标函数防止过拟合,而传统的GBRT只采用启发方法,即用剪枝防止过拟合。

    3.2 XGBoost 加入了Column Subsampling,这样可以进一步防止过拟合,还降低运算量。

    3.3 XGBoost 充分利用损失函数信息,不仅仅考虑一阶导数还考虑二阶导数,而且即使在分裂节点的阶段也是以目标函数的近似减少量(Gain)来作为分裂的标准,而GBRT只考虑一阶导数,在分裂节点时本质上是以MSE(最小均方误差)为标准,而不是原目标函数。因此,XGBoost具有更好的学习性能,收敛速度更快。

    3.4 采用节点分裂的近似算法,提高了运算速度,并且可以在分裂节点时进行并行运算。

    3.5 在分裂节点时还将0值与非0值做统一处理,这样更加快速并且增强了模型的鲁棒性。

 

 参考:

[1] Tianqi ChenCarlos Guestrin: XGBoost: A Scalable Tree Boosting System, https://arxiv.org/abs/1603.02754

[2] XGBoost 官方网站:https://xgboost.readthedocs.io/en/latest/index.html

 


上一篇:python-xgboost.train与XGBClassifier


下一篇:ML之xgboost:利用xgboost算法(sklearn+3Split+调参曲线)训练mushroom蘑菇数据集(22+1,6513+1611)来预测蘑菇是否毒性(二分类预测)