Boosting则是一种集成学习模式,通过将多个单个决策树(弱学习器)进行线性组合构成一个强学习器的过程, 一个提升树模型可以描述为: $$\begin{aligned} &\hat{y}{i}^{0}=0\ &\hat{y}{i}^{1}=f_{1}\left(x_{i}\right)=\hat{y}{i}^{0}+f{1}\left(x_{i}\right)\ &\hat{y}{i}^{2}=f{1}\left(x_{i}\right)+f_{2}\left(x_{i}\right)=\hat{y}{i}^{1}+f{2}\left(x_{i}\right)\ &...\ &\hat{y}{i}^{K}=\sum{k=1}^{K} f_{k}\left(x_{i}\right)=\hat{y}{i}^{k-1}+f{k}\left(x_{i}\right) \end{aligned}$$
我们的目标一般是求目标值$y_i$与预测值$\hat y_i$之间的最小值即: $$\begin{aligned} L^{(k)}&=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}{i}\right)\ &=\sum{i=1}^{n} l(y_i,\hat{y}{i}^{k-1}+f{k}(x_{i})) \end{aligned}$$
如何去求解每一个模型就衍生出了不同方法
大牛Freidman提出了用损失函数的负梯度来拟合本轮损失的近似值,进而拟合一个$Cart$回归树。
$$\hat{y}{i}^{K}=\hat{y}{i}^{k-1}+f_{k}\left(x_{i}\right)$$
在集成学习中要最小化损失函数
$$L^{(K)}=\sum_{i=1}^{n} l(y_i,\hat{y}{i}^{k-1}+f{k}(x_{i}))$$
回想下梯度下降的形式
- 算法每次迭代生成一颗新的决策树;
- 计算损失函数在每个训练样本点的负梯度生成新的决策树$f_k(x)$;
- 把新生成的决策树$f_k(x)$加入$\hat y_i^k=\hat y_i^{k-1}+ f_{k}\left(x_{i}\right)$
一个好的模型,在偏差和方差上要有一个较好的平衡,损失函数代表了模型的偏差面,最小化损失函数,就相当于最小化模型的偏差,但同时要考虑模型的泛化能力即方差,所以要兼顾模型的方差,所以作者直接在损失函数中加入了正则项抑制模型复杂度的正则项,因此损失函数可以写成: $$\begin{aligned} L^{(k)}&=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}{i}\right)+\sum{k=1}^{K} \Omega\left(f_{k}\right)\ &=\sum_{i=1}^{n} l(y_i,\hat{y}{i}^{k-1}+f{k}(x_{i}))+\Omega(f_{k})+constant\tag 1 \end{aligned}$$ 其中$\Omega$代表了基模型的复杂度,比如树的深度、叶子节点数等等。这里$\Omega\left(f_{k}\right)=\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2}$,
回想一下泰勒公式二阶展开
$$L^{(k)}=\sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y}{i}^{k-1}\right)+g_if{k}\left(x_{i}\right)+\frac{1}{2}h_i f_{k}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{k}\right)+constant \tag4$$
因为上一棵树$\hat{y}{i}^{k-1}$已经算出来了,所以$l\left(y{i}, \hat{y}{i}^{k-1}\right)$为常数,(4)式可以写成: $$\begin{aligned} L^{(k)} &\approx \sum{i=1}^{n}\left[g_{i} f_{k}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{k}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{k}\right) \ &=\sum_{i=1}^{n}\left[g_{i} f_{k}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{k}^{2}\left(x_{i}\right)\right]+\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2} \ &=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T \end{aligned} \tag5$$
其中$G_{j}=\sum_{i \in I_{j}} g_{i}, H_{j}=\sum_{i \in I_{j}} h_{i}$,假设树的结构固定,令函数$L^{(k)}$ 对$w$的导数等于0(凸函数导数为0取极值), 即可求得叶子节点
$$w_{j}^{}=-\frac{G_{j}}{H_{j}+\lambda}$$
把$w_{j}^{}$带进去,此时,损失函数的值为:
- 算法每次迭代生成一颗新的决策树 ;
- 在每次迭代开始之前,计算损失函数在每个训练样本点的一阶导数$g_i$和二阶导数$h_i$;
- 通过贪心策略生成新的决策树,通过式(7) 计算每个叶节点对应的预测值$w$
- 把新生成的决策树$f_k(x)$加入$\hat y_i^k=\hat y_i^{k-1}+\eta f_{k}\left(x_{i}\right)$,$\eta$为学习率
对xgb的优化
- 基于Histogram的决策树算法
- 带深度限制的Leaf-wise的叶子生长策略
- 直方图做差加速
- 支持类别特征
- Cache命中率优化
- 基于直方图的稀疏特征多线程优化
-
XGBoost生成CART树考虑了树的复杂度,GDBT未考虑,GDBT在树的剪枝步骤中考虑了树的复 杂度。
-
XGBoost是拟合上一轮损失函数的二阶导展开,GDBT是拟合上一轮损失函数的一阶导展开,因 此,XGBoost的准确性更高,且满足相同的训练效果,需要的迭代次数更少。
-
XGBoost与GDBT都是逐次迭代来提高模型性能,但是XGBoost在选取最佳切分点时可以开启多 线程进行,大大提高了运行速度。
-
基于残差的gbdt在解决回归问题上不算是一个好的选择,一个比较明显的缺点就是对异常值过于敏感。
-
GBDT的非线性变换比较多,表达能力强,而且不需要做复杂的特征工程和特征变换。
-
GBDT是一个串行过程,不好并行化,而且计算复杂度高,同时不太适合高维稀疏特征;
-
xgb实现了分裂点寻找近似算法。
-
xgb利用了特征的稀疏性。
-
xgb数据事先排序并且以 block 形式存储,有利于并行计算。
-
xgb基于分布式通信框架 rabit,可以运行在 MPI 和 yarn 上。(最新已经不基于 rabit 了)
-
xgb实现做了面向体系结构的优化,针对 cache 和内存做了性能优化。