XGBoost
XGBoost相对于GBDT
XGBoost与GBDT树的原理相似,都是使用加法模型,通过梯度上升的方式求出,其主要的不同点在
- XGBoost使用的是泰勒展式的二阶导数,而GBDT采用的是一阶导,XGBoost的拟合效果更好
- XGBoost中带有正则项(L2正则项),减少了模型的过拟合。
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点。(补充:传统GBDT的实现也有学习速率)
- 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
- 对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。
- xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
XGBoost的公式推导
XGboost由两个部分组成,一个部分为损失函数,一个部分为树的复杂度(相当于一个L2正则项)
与GBDT相同,XGboost也是基于boost的思想,由多个弱分类器组成一个强分类器。因此上面的目标函数展开后可以得到如下的公式:
对于上式来说其中唯一的变量其实是
f
(
x
i
)
f(x_i)
f(xi),之前的t-1个树已经得到因此可以将其看做为一个常数项。
泰勒的二级展开为:
对于上式来说,
f
(
x
)
f(x)
f(x) 对应于我们的损失函数
L
L
L ,x 对应于前 t-1 棵树的预测值,
Δ
x
Δx
Δx 对应于我们正在训练的第 t 棵树。
根据泰勒的二级展开带入上一个公式可以得到:
∑ i = 1 N L ( y i , f t − 1 ( x i ) + f t ( x i ) ) = ∑ i = 1 N L ( y , f t − 1 ( x ) ) + ∂ L ( y , f t − 1 ( x ) ) ∂ f t − 1 ( x ) f t ( x ) + ∂ 2 L ( y , f t − 1 ( x ) ) ∂ f t − 1 ( x ) 2 f t 2 ( x ) + Ω + c o n s t \sum_{i=1}^N{L(y_i,f_{t-1}(x_i)+f_t(x_i))}=\sum_{i=1}^N{L(y,f_{t-1}(x))+\frac{\partial L(y,f_{t-1}(x))}{\partial f_{t-1}(x)}f_t(x)+\frac{\partial^2L(y,f_{t-1}(x))}{\partial f_{t-1}{(x)}^2}f_t^2(x)}+\Omega+const i=1∑NL(yi,ft−1(xi)+ft(xi))=i=1∑NL(y,ft−1(x))+∂ft−1(x)∂L(y,ft−1(x))ft(x)+∂ft−1(x)2∂2L(y,ft−1(x))ft2(x)+Ω+const
我们将
∑
i
=
1
N
L
(
y
,
f
t
−
1
(
x
)
)
\sum_{i=1}^N{L(y,f_{t-1}(x))}
∑i=1NL(y,ft−1(x))视为一个常数,将其一阶偏导视设为
h
(
x
i
)
h(x_i)
h(xi),将其二阶偏导视设为
g
(
x
i
)
g(x_i)
g(xi)可以将上式简化为:
去掉所有的常数项后,得到
对于决策树来说其基函数为
f
(
x
)
f(x)
f(x)可以看做每个样本点的集合。因此可以表示为
w
q
(
x
i
)
w_q(x_i)
wq(xi)表示对于每个样本点,落入叶子节点的集合。在此基础上我们可以根据树中的每一个叶子将样本进行分类,得到如下的公式:
I
j
I_j
Ij表示样本点
x
i
x_i
xi落在第j个叶子节点上的集合。将上式带入后得到的结果如下所示:
为进一步简化该式,我们进行如下定义:
含义如下:
Gj :叶子结点 j 所包含样本的一阶偏导数累加之和,是一个常量;
Hj :叶子结点 j 所包含样本的二阶偏导数累加之和,是一个常量;
将 Gj 和 Hj 带入目标式Obj,得到我们最终的目标函数(注意,此时式中的变量只剩下第t棵树的权重向量W):
对于已经得到的目标函数是一个累加和,如果我们想得到最小的目标函数(损失函数)则需要得到每个节点中损失函数最小。
因此我们需要损失函数为:
可以轻易得出这个一元二次方程的解如下所示,并将其带回上市可以得出最小化后的目标函数
XGBoost精确贪心分割算法
我们已经通过上面的推导得到了相应的目标函数并且得知其和每个节点是相关的。那么我们如何在之前的基础上得到一个新的树呢?
其实XGBoost分裂的原则和ID3,C4.5这些比较相似。XGBoost同样是通过计算分裂两点时的增益。一半的XGBoost使用的是遍历所有的,已经排列好的特征值,并计算其中每个特征值的增益。选择最大的增益,这种方法我们将其称作为贪心算法,因为其每次都选取增益最大的。
当然我们不能一直分裂下去这样会造成过拟合,一般我们会设置一些限制
- 增益小于零
- 最大的深度
- 最多的叶子个数等
对于这种方法我们每次计算都要重新寻找一遍所有的样本点。所以除了单纯的每次遍历之外,我们还有其他的方式帮助我们加速进行点的分割:
- 特征预排序+缓存: XGBoost在训练之前,预先对每个特征按照特征值大小进行排序,然后保存为block结构,后面的迭代中会重复地使用这个结构,使计算量大大减小。
- 分位点近似法: 对每个特征按照特征值排序后,采用类似分位点选取的方式,仅仅选出常数个特征值作为该特征的候选分割点,在寻找该特征的最佳分割点时,从候选分割点中选出最优的一个。
- 并行查找: 由于各个特性已预先存储为block结构,XGBoost支持利用多个线程并行地计算每个特征的最佳分割点,这不仅大大提升了结点的分裂速度,也极利于大规模训练集的适应性扩展。
其他分割算法
除了精确贪心算法外,论文作者还有阐述其他分割算法:
- Approximate Algorithm for Split Finding 近似分割算法
- Sparsity-aware Split Finding 稀疏特征处理
具体的参考文献:XGBoost学习-分裂点的选择