本文从互联网搬运,只用于本人学习记录。
决策树之XGBoost
XGBoost使大规模并行boosting tree的工具。
1. 数学原理
1.1. 目标函数
XGBoost使由
k
k
k个基模型组成的一个加法运算式:
y
i
^
=
∑
t
=
1
k
f
t
(
x
i
)
\hat{y_i} = \sum^k_{t = 1}f_t(x_i)
yi^=t=1∑kft(xi)
其中
f
k
f_k
fk为第
k
k
k个基模型,
y
i
^
\hat{y_i}
yi^为第
i
i
i个样本的预测值。
损失函数由预测值
y
i
^
\hat{y_i}
yi^与真实值
y
i
y_i
yi进行表示:
L
=
∑
i
=
1
n
l
(
y
i
,
y
i
^
)
L = \sum^n_{i = 1}l(y_i, \hat{y_i})
L=i=1∑nl(yi,yi^)
其中
n
n
n为样本数量。
目标函数由模型的损失函数
L
L
L与抑制模型复杂度的正则项
Ω
\Omega
Ω组成,所以:
O
b
j
=
∑
i
=
1
n
l
(
y
i
^
,
y
i
)
+
∑
t
=
1
k
Ω
(
f
t
)
Obj = \sum^n_{i = 1}l(\hat{y_i}, y_i) + \sum^k_{t = 1}\Omega(f_t)
Obj=i=1∑nl(yi^,yi)+t=1∑kΩ(ft)
XGBoost 支持决策树也支持线性模型
Boosing模型是前向算法,以第
t
t
t步的模型为例,模型对第
i
i
i个样本
x
i
x_i
xi的预测为:
y
^
i
t
=
y
^
i
t
−
1
+
f
t
(
x
i
)
\hat y_i^t = \hat y^{t - 1}_i + f_t(x_i)
y^it=y^it−1+ft(xi)
因此目标函数可以写成:
O
b
j
(
t
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
)
+
∑
i
=
1
t
Ω
(
f
i
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
−
1
+
f
t
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
i
)
\begin{aligned} Obj^{(t)} &= \sum^n_{i = 1}l(y_i, \hat y^t_i) + \sum^t_{i = 1}\Omega(f_i) \\ &= \sum^n_{i = 1}l(y_i, \hat y^{t - 1}_i + f_t(x_i)) + \sum^t_{i = 1}\Omega(f_i) \end{aligned}
Obj(t)=i=1∑nl(yi,y^it)+i=1∑tΩ(fi)=i=1∑nl(yi,y^it−1+ft(xi))+i=1∑tΩ(fi)
此时求最优解,相当于求解
f
t
(
x
i
)
f_t(x_i)
ft(xi)
根据泰勒公式:
将
y
^
i
t
−
1
\hat y^{t -1}_i
y^it−1视为
x
x
x,
f
t
(
x
i
)
f_t(x_i)
ft(xi)视为
Δ
x
\Delta x
Δx,故可将目标函数写为:
其中
g
i
g_i
gi为损失函数的一阶导,
h
i
h_i
hi为损失函数的二阶导,注意这里的导是对
y
^
i
t
−
1
\hat y^{t -1}_i
y^it−1求导。
于是目标函数可以写成:
1.2. 基于决策树的目标函数
Xgboost 的基模型不仅支持决策树,还支持线性模型。
将决策树定义为 f t ( x ) = ω q ( x ) f_t(x) = \omega_{q(x)} ft(x)=ωq(x), x x x为某一样本,这里的 q ( x ) q(x) q(x)代表了该样本在哪个叶子节点上,二 ω q \omega_q ωq代表了叶子节点取值 ω \omega ω,所以 ω q ( x ) \omega_{q(x)} ωq(x)就代表了每个样本的取值 ω \omega ω(预测值)。
决策树的复杂度可由叶子数
T
T
T组成,叶子节点越少模型越简单,同时叶子节点不应含有过高权重
ω
\omega
ω,故目标函数正则项定义为:
定义
G
j
=
∑
i
∈
I
j
g
i
G_j = \sum_{i \in I_j}g_i
Gj=∑i∈Ijgi,
H
j
=
∑
i
∈
I
j
h
i
H_j = \sum_{i \in I_j}h_i
Hj=∑i∈Ijhi,则目标函数为:
最优化得到:
目标函数化简为:
1.3. 最优决策点划分算法
决策树生长过程中的关键问题是如何找到叶子的节点的最优切分点。
1) 贪心算法:
- 从深度为0的树开始,对每个叶节点枚举所有的可用特征;
- 针对每个特征,把属于该节点的训练样本根据该特征值进行升序排列,通过线性扫描的方式来决定该特征的最佳分裂点,并记录该特征的分裂收益;
- 选择收益最大的特征作为分裂特征,用该特征的最佳分裂点作为分裂位置,在该节点上分裂出左右两个新的叶节点,并为每个新节点关联对应的样本集;
- 回到第 1 步,递归执行到满足特定条件为止。
观察分裂后的收益,我们会发现节点划分不一定会使得结果变好,因为我们有一个引入新叶子的惩罚项,也就是说引入的分割带来的增益如果小于一个阀值的时候,我们可以剪掉这个分割。
2) 近似算法:
贪婪算法可以的到最优解,但当数据量太大时则无法读入内存进行计算,近似算法主要针对贪婪算法这一缺点给出了近似最优解。
对于每个特征,只考察分位点可以减少计算复杂度。
该算法会首先根据特征分布的分位数提出候选划分点,然后将连续型特征映射到由这些候选点划分的桶中,然后聚合统计信息找到所有区间的最佳分裂点。
在提出候选切分点时有两种策略:
- Global:学习前就提出候选切分点,并在每次分裂时都采用这种分割;
- Local:每次分裂前将重新提出候选切分点。
直观上来看,Local 策略需要更多的计算步骤,而 Global 策略因为节点没有划分所以需要更多的候选点。
1.4. 稀疏感知算法
XGBoost 在构建树的节点过程中只考虑非缺失值的数据遍历,而为每个节点增加了一个缺省方向,当样本相应的特征值缺失时,可以被归类到缺省方向上,最优的缺省方向可以从数据中学到。
特征值缺失的样本无需遍历只需直接分配到左右节点,故算法所需遍历的样本量减少,速度更快。
2. 工程实现
2.1. 块结构设计
决策树的学习最耗时的一个步骤就是在每次寻找最佳分裂点是都需要对特征的值进行排序。而 XGBoost 在训练之前对根据特征对数据进行了排序,然后保存到块结构中,并在每个块结构中都采用了稀疏矩阵存储格式(Compressed Sparse Columns Format,CSC)进行存储,后面的训练过程中会重复地使用块结构,可以大大减小计算量。
Xgboost 能够实现分布式或者多线程计算的原因:
这种块结构存储的特征之间相互独立,方便计算机进行并行计算。在对节点进行分裂时需要选择增益最大的特征作为分裂,这时各个特征的增益计算可以同时进行。
2.2. 缓存访问优化算法
为每个线程分配一个连续的缓存区,将需要的梯度信息存放在缓冲区中,这样就是实现了非连续空间到连续空间的转换,提高了算法效率。
2.3. “核外”块计算
当数据量过大时,先将无法加载到内存中的数据暂存到硬盘中,直到需要时再进行加载计算,这种操作必然涉及到因内存与硬盘速度不同而造成的资源浪费和性能瓶颈。。
XGBoost 独立一个线程专门用于从硬盘读入数据,以实现处理数据和读入数据同时进行。
降低硬盘读写开销的方法:
- 块压缩:对 Block 进行按列压缩,并在读取时进行解压;
- 块拆分:将每个块存储到不同的磁盘中,从多个磁盘读取可以增加吞吐量。
3. 优缺点
优点:
- 精度更高:GBDT 只用到一阶泰勒展开,而 XGBoost 对损失函数进行了二阶泰勒展开。XGBoost 引入二阶导一方面是为了增加精度,另一方面也是为了能够自定义损失函数,二阶泰勒展开可以近似大量损失函数;
- 灵活性更强:GBDT 以 CART 作为基分类器,XGBoost 不仅支持 CART 还支持线性分类器,(使用线性分类器的 XGBoost 相当于带 L1 和 L2 正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题))。此外,XGBoost 工具支持自定义损失函数,只需函数支持一阶和二阶求导;
- 正则化:XGBoost 在目标函数中加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、叶子节点权重的 L2 范式。正则项降低了模型的方差,使学习出来的模型更加简单,有助于防止过拟合;
- Shrinkage(缩减):相当于学习速率。XGBoost 在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间;
- 列抽样:XGBoost 借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算;
- 缺失值处理:XGBoost 采用的稀疏感知算法极大的加快了节点分裂的速度;
- 可以并行化操作:块结构可以很好的支持并行计算。
缺点:
- 虽然利用预排序和近似算法可以降低寻找最佳分裂点的计算量,但在节点分裂过程中仍需要遍历数据集;
- 预排序过程的空间复杂度过高,不仅需要存储特征值,还需要存储特征对应样本的梯度统计值的索引,相当于消耗了两倍的内存。
4. 调参
‘n_estimators’:则是非常重要的要调的参数,它关系到我们XGBoost模型的复杂度,因为它代表了我们决策树弱学习器的个数。
‘booster’:gbtree 和 gblinear,使用树模型还是线性模型。
‘objective’: 这个参数定义需要被最小化的损失函数。最常用的值有"multi:softprob" --输出维度为ndata * nclass的概率矩阵。
‘eval_metric’:用于验证集的评估指标。比如‘auc’。
‘gamma’:为了对树的叶子节点做进一步的分割而必须设置的损失减少的最小值,该值越大,算法越保守。
‘max_depth [default=6]’:用于设置树的最大深度。
‘min_child_weight’:表示子树观测权重之和的最小值,如果树的生长时的某一步所生成的叶子结点,其观测权重之和小于min_child_weight,那么可以放弃该步生长,在线性回归模式中,这仅仅与每个结点所需的最小观测数相对应。该值越大,算法越保守。
‘eta’:学习率
‘subsample’:表示观测的子样本的比率,将其设置为0.5意味着xgboost将随机抽取一半观测用于数的生长,这将有助于防止过拟合现象。
‘lambda’:L2 权重的L2正则化项。