GBDT(Gradient Boost Decision Tree)
首先介绍下XGBoost的前身,gbdt
损失函数
gbdt的损失函数为:
\[Loss_t = \frac{1}{n}\sum\nolimits^{n}_{i=1}{l(y_i, \hat{y}_i^{(t)})}, \qquad \hat{y}_i^{(t)} = \sum\nolimits_{k=1}^{t}{f_{k}(x_{i})}
\]
每轮迭代时,损失函数为:
\[Loss = \frac{1}{n}\sum\nolimits^{n}_{i=1}{l(y_i, \hat{y}_i^{(t-1)} + f_t(x_i))}, \quad \hat{y}_i^{(t-1)} = \sum\nolimits_{k=1}^{t-1}{f_{k}(x_{i})}
\]
Loss=MSE时拟合残差
当损失函数为MSE(均方误差)时,Loss变为:
\[\begin{align}
Loss &= \frac{1}{n}\sum_{i=1}^{n}{[y_i-({\hat{y}}_i^{(t-1)}+f_t(x_i))]^2} \\
&= \frac{1}{n}\sum_{i=1}^{n}{[(y_i-{\hat{y}}_i^{(t-1)})-f_t(x_i)]^2}
\end{align}
\]
要使得此时得Loss最小,即让当前树\(f_t(x_i)\)尽量拟合\((y_i-{\hat{y}}_i^{(t-1)})\),即上一轮模型的残差
不为MSE时拟合负梯度
当函数不为MSE时,需要用一阶泰勒展开求取当前树的拟合目标
泰勒公式:一个用函数在某点的信息描述其附近取值的公式
在GBDT中即使用Loss函数在上一轮的预测值上的信息来描述本轮的预测值。GBDT使用了上一轮的Loss值、一阶导;xgb则还用到了二阶导。
一阶泰勒展开如下
\[f(x){\approx}f(x_0)+f'(x_0)(x-x_0)\\
f(x_0+{\Delta}x){\approx}f(x_0)+f'(x_0)*{\Delta}x
\]
将一阶泰勒展开代入Loss函数得
\[Loss_{(t)} = \sum\nolimits_{i=1}^{n}{l(y_i,{\hat{y}}_i^{(t-1)}+f_t(x_i))}=\sum\nolimits_{i=1}^{n}{l(y_i, {\hat{y}}_i^{t-1})+\frac{\partial{l(y_i, {\hat{y}}_i^{t-1})}}{\partial{{\hat{y}}_i^{t-1}}}*{f_t{(x_i)}}}\\
Loss_{(t)} =Loss_{(t-1)}+\sum\nolimits_{i=1}^{n}{l'(y_i, {\hat{y}}_i^{t-1})*f_t{(x_i)}}
\]
要使得Loss在本轮下降,即保证\(l'(y_i, {\hat{y}}_i^{t-1})*f_t{(x_i)} < 0\),只要保证\(f_t{(x_i)}=-l'(y_i, {\hat{y}}_i^{t-1})\)即可,即本轮树拟合上一轮的负梯度。当损失函数为MSE时,负梯度就是上一轮的残差。
这里有个问题为什么拟合负梯度,而不是保证和梯度相反的随机值。我个人理解是因为当梯度更大时,上一轮的拟合值离Loss最小值更远,所以本轮走的步长应该更长,所以拟合值的大小与梯度大小是相关的。这个理解有个限制:保证Loss是凸函数,但似乎常用的Loss函数都是凸函数,不知道是不是故意这样设计的。比如回归问题中的\(MSE、MAE、log(cosh(y-{\hat{y}}))\),分类问题中的\(-ylog(y)-(1-y)log(1-y)\)。
XGBoost推导
Loss函数/目标函数
相比GBDT,xgboost做了几点提升
- 使用二阶泰勒展开,余项更小,能更精准地逼近真实的损失函数
- 添加正则项,限制树结构,防止过拟合
- ....
XGBoost的损失函数(目标函数)
\[Object=\sum_{i=1}^{n}{l({y}_i, {\hat{y}}_i)}+\sum_{k=1}^{K}{\Omega{(f_k)}}\\
{\Omega}(f)={\gamma}T+\frac{1}{2}{\lambda}||w||^2
\]
代入二阶泰勒展开
泰勒二阶展开极其变形
\[f(x){\approx}f(x_0)+f'(x_0)(x-x_0)+\frac{f''(x_0)}{2}(x-x_0)^2\\
f(x_0+{\Delta}x){\approx}f(x_0)+f'(x_0)*{\Delta}x+\frac{f''(x_0)}{2}*{\Delta}x^2
\]
把二阶泰勒展开代入Loss得
\[Obj=\sum_{i=1}^{n}{\large{[}}l(y_i, {\hat{y}}^{(t-1)})+g_if_t(x_i)+\frac{1}{2}h_if_i^2{(x_i)}{\large{]}}+{\Omega}{(f_t)}\\
g_i=\partial_{{\hat{y}}^{(t-1)}}(l(y_i, {\hat{y}}^{(t-1)})), \quad h_i=\partial^2_{{\hat{y}}^{(t-1)}}(l(y_i, {\hat{y}}^{(t-1)}))
\]
Loss变形,把遍历n个样本变为遍历每个叶子节点,\(I_j\)表示第\(j\)个叶子结点内的样本ID的集合。
\[\begin{align}
Obj &= \sum_{i=1}^n{[g_if_t{(x_i)}+\frac{1}{2}h_if_t^2{(x_i)}]}+{\gamma}T+\frac{1}{2}{\lambda}\sum_{i=1}^T{w_j^2}\\
&= \sum_{j=1}^T{[(\sum_{i\in{I_j}}g_i)w_j+\frac{1}{2}(\sum_{i\in{I_j}}h_i+{\lambda})w_j^2]+{\gamma}T}
\end{align}
\]
这个变形很巧妙的一点就是把正则融入到了损失函数里,方便对\(w_j\)求解
把Loss看做以\(w_j\)为变量的二次函数,求解\(w_j\)得
\[w_j=-\frac{\sum_{i\in{I_j}}g_i}{\sum_{i\in{I_j}}h_i+{\lambda}}
\]
代入Loss求最优解
\[Obj=-\frac{1}{2}\sum_{j=1}^{T}{\frac{(\sum\nolimits_{i{\in}I_j}g_i)^2}{\sum\nolimits_{i{\in}I_j}h_i+{\lambda}}}+{\gamma}T\\
Obj=-\frac{1}{2}\sum_{j=1}^{T}{\frac{(G_j)^2}{H_j+{\lambda}}}+{\gamma}T\\
G_j=\sum\nolimits_{i{\in}I_j}g_i,H_j=\sum\nolimits_{i{\in}I_j}h_i
\]
这里有个问题是,\(w_j\)是Loss极小值的解,还是极大值的解。个人理解,如果Loss函数为凸函数,则二阶导\(h_i>0\),\(\sum_{i\in{I_j}}h_i+{\lambda}>0\),那Loss最优解对应的二次函数二次项为正,开口朝上,为极小值的解。
如果Loss函数非凸,就母鸡了。与上述问题相同,Loss函数似乎都是凸函数?
求取分裂增益
求取增益时只需要考虑两个点
- 增益=分裂前得Loss-分裂后的Loss
- 增益只与叶子节点相关
那么增益如下
\[\begin{align}
Gain_{split} &={\LARGE{(}}-\frac{G^2}{2(H+{\lambda})}+{\gamma}{\LARGE{)}}-{\LARGE{(}}-\frac{G_L^2}{2(H_L+{\lambda})}+{\gamma}-\frac{G_R^2}{2(H_R+{\lambda})}+{\gamma}{\LARGE{)}}\\
&=\frac{1}{2}{\LARGE{(}}\frac{G_L^2}{H_L+{\lambda}}+\frac{G_R^2}{H_R+{\lambda}}-\frac{G^2}{H+{\lambda}}{\LARGE{)}}-{\gamma}
\end{align}
\]
XGBoost的优化
针对GBDT做的优化
- 二阶泰勒展开
- 正则
- 缺失值处理:xgboost可以指定哪种取值是缺失值。所以可以把Nan输入到模型内?待验证。https://tech.meituan.com/2019/08/15/problems-caused-by-missing-xgboost-values-and-their-in-depth-analysis.html
- 并行计算
- 缩放
- 列抽样
- 支持自定义Loss函数
- 特征分桶,过于复杂, 看不懂(https://yxzf.github.io/2017/04/xgboost-v2/)
原paper地址
github地址+讨论
文档