目标函数
xgboost是由k个基模型组成的一个加法运算式。
模型的预测精度由模型的偏差和方差共同决定,我们追求偏差小方差小的模型。
损失函数代表了模型的偏差,提升模型复杂度可以减小偏差。
但模型过于复杂时容易过拟合,使用正则项抑制模型复杂度。选用抑制模型复杂度的正则项
Ω
\Omega
Ω 来衡量。
增加模型的复杂度通常会显著提升模型的方差并减少偏差。反过来,降低模型的复杂度则会提升模型的偏差并降低方差
构造目标函数
- 目标函数由模型的损失函数
L
L
L 和 抑制模型复杂度的正则项
Ω
\Omega
Ω 组成。
n n n 为样本数, k k k为树的数量,
每个 f f f是在一个函数空间 F F F里的函数, F F F对应了所有CART树的集合。
目标函数优化
- boosting是前向加法,第
t
t
t 步的模型,对第
i
i
i 个样本
x
i
x_i
xi 的预测为:
其中
y i t ^ \hat{y_i^{t}} yit^ 为 t t t 步的模型预测值,
y i t − 1 ^ \hat{y_i^{t-1}} yit−1^ 为 t − 1 t-1 t−1 步的模型预测值,
f t ( x i ) f_t(x_i) ft(xi)为这次需要加入的新模型的预测值。
此时目标函数可写成:
由于在第
t
t
t步时,
y
i
t
−
1
^
\hat{y_i^{t-1}}
yit−1^即上一步的预测值是一个已知的值。
每轮加入什么
f
t
(
x
i
)
f_t(x_i)
ft(xi) 决定了最终目标函数的大小。需要选取使目标函数尽量低的
f
f
f。
- 采用泰勒展开近似来定义一个新的目标函数,方便进一步计算:
(在下方公式中,
将 l ( y i , y i t − 1 ^ ) l(y_i,\hat{y_i^{t-1}}) l(yi,yit−1^) 视作泰勒的 f(x) ,
将 y i t − 1 ^ \hat{y_i^{t-1}} yit−1^ 视作 x x x ,
而第t步的模型 f t ( x i ) f_t(x_i) ft(xi) 视作 Δ \Delta Δ x x x)
(注意泰勒的f(x)和我们目标函数中的 f t ( x i ) f_t(x_i) ft(xi) 不是同一个函数,只是他们恰好都叫f;泰勒展开是为了将常数项剔除掉,最终简化目标函数)
定义 g i g_i gi 为损失函数的一阶导,
定义 h i h_i hi 为损失函数的二阶导,
(导数是对
y i t − 1 ^ \hat{y_i^{t-1}} yit−1^求导)
- 因为
y
i
y_i
yi 和
y
i
t
−
1
^
\hat{y_i^{t-1}}
yit−1^ 都是已知的,所以
l
(
y
i
,
y
i
t
−
1
^
)
l(y_i,\hat{y_i^{t-1}})
l(yi,yit−1^) 为一个常数,其对目标函数的优化不会产生影响,因此目标函数可以改写成:
问题
?为何需要做泰勒展开
因为损失函数为平方误差时,求目标函数最优解还好求,但对于其他损失函数将变得很复杂,通过二阶泰勒展开使求解其他损失函数时的目标函数最优解变得可行。
?为何是二阶不是一阶
https://www.zhihu.com/question/277638585/answer/522272201
损失函数是MSE时, O b j Obj Obj展开是一阶项(残差)+二阶项的形式,而其他的损失函数(如log loss)的展开式没有这样的形式。为了有一个统一的形式,选择二阶展开,就可以把MSE推导的过程直接复用到其他自定义损失函数上(简单说就是为了统一损失函数的形式以支持自定义损失函数)。
二阶信息本身就能让梯度收敛更快更准确。这一点在优化算法里的牛顿法里已经证实了。可以简单认为一阶导指引梯度方向,二阶导指引梯度方向如何变化。这是从二阶导本身的性质,也就是为什么要用泰勒二阶展开的角度来说的
?为什么要在形式上与MSE统一
因为MSE是最普遍常用的损失函数,且求导最容易。
?xgboost和GBDT的区别
- GBDT在决策树构建完成后再进行剪枝,而xgboost在决策树构建阶段就加入了正则项。
- GBDT在模型训练时只使用了代价函数的一阶导数信息,xgboost对代价函数进行二阶泰勒展开。同时用到了一阶和二阶导数。
- 传统的GBDT采用CART作为基模型,xgboost支持多种类型的基模型,比如线性分类器。
基于决策树的目标函数
- 可以将决策树定义为
x x x为某一样本
q ( x ) q(x) q(x)代表了该样本在哪个节点上(叶子的索引)
w q w_q wq代表了叶子节点的取值(即每个叶子的分数)
w q ( x ) w_{q(x)} wq(x)代表了每个样本的取值
- 决策树的复杂度包含两个部分:
1)叶子数 T T T 衡量模型复杂度,叶子节点越少模型越简单。
2)叶子节点的得分 w w w 的L2模平方(对 w w w进行L2正则化,即每个叶子节点的得分加L2平滑,目的是为了避免过拟合)。
需提前了解L2正则化
叶子节点不该含有过高的权重
w
w
w 怎么理解叶子节点的权重?叶子节点的权重和该叶子上样本的取值的关系?
基模型为决策树的xgboost的目标函数中的正则项可定义为:
这种复杂度的定义不是唯一的一种定义方式,只是这种方式简单并且有效。
- 设
I
j
I_j
Ij 为第
j
j
j个叶子节点的样本集合,
目标函数就可以写成:
上式中,第二步到第三步解释:
第二步是遍历所有的样本后求每个样本的损失函数
但样本最终是落在叶子节点上的,所以可以遍历叶子节点,然后获取叶子节点上的样本集合,最后再求损失函数。
外层循环:从第
j
=
1
j=1
j=1 个叶子节点遍历至第
T
T
T 个叶子节点。
内层循环:
为属于第j个叶子节点上所有样本的
g
g
g (一阶导)求和。
w
j
w_j
wj为第
j
j
j个叶子节点取值。
- 为简化表达式,定义
则目标函数为:
G G G 和 H H H 是前一步t-1步得到的结果,可以视为常数,只有最后一棵树(第t步加入的基模型)的各叶子节点的值 w j w_j wj 不确定。
- 将目标函数对
w
j
w_j
wj 求一阶导,另其等于0,即可求得使目标函数达到极值时的
w
j
w_j
wj:
将
w
j
w_j
wj 代入后
O
b
j
Obj
Obj 化简为
上图给出目标函数计算的例子,求每个节点每个样本的一阶导数 g 和二阶导数 h ,然后针对每个节点对所含样本求和得到的 G 和 H ,最后遍历决策树的节点即可得到目标函数。
O b j Obj Obj 表示在某个确定的树结构下的结构分数(structure score)
选择分裂节点策略
1、贪心算法
- 分割方法
a.从深度为0的树开始,每个叶节点枚举所有的可用特征;
b.对每个特征,把属于该节点的训练样本根据该特征值进行升序排序,通过线性枚举,决定该特征的最佳分裂点;
c.选择增益最大的特征作为分裂特征(增益:分裂前的目标函数值-分裂后的目标函数值)
(因为随着不断分裂,我们希望目标函数越小越好),用该特征的最佳分裂点作为分裂位置。
在该节点上分裂出左右两个新的叶子节点,并为每个新节点关联对应的样本集。
d.回到第a步,递归执行到满足特定条件为止。
上图为论文原图,外层循环为从第1个特征遍历到第m个特征;内层循环为遍历 I I I(即当前叶子节点的样本集合,且样本集合是按该特征值进行排序后的)
- 计算每个特征的增益
在某一节点分裂前的目标函数可写为:
分裂后的目标函数为:
对于目标函数来说,分裂后的增益为(分裂后-分裂前):
该特征增益Gain也可作为特征重要性的依据。
引入分割不一定会使结果更好, 当引入的分割带来的增益大于该设定的阈值时,才进行分裂。
(这种阈值限制是为了限制树深度,因为新增一次分割,模型的复杂度就会增大)
设定的阈值:
xgboost的切分操作和普通的决策树切分过程是不一样的。普通的决策树在切分的时候并不考虑树的复杂度,所以才有了后续的剪枝操作。而xgboost在切分的时候就已经考虑了树的复杂度(obj里面看到那个了吗)。所以,它不需要进行单独的剪枝操作。https://mp.weixin.qq.com/s/NC9CwR4cfDUJ26WpHsvkPQ
遍历完所有特征后,我们就可以确定应该在哪个特征的哪个点进行切分。对切分出来的两个节点,递归地调用这个过程,我们就能获得一个相对较好的树结构, 有了树结构就比较容易找最优的叶子节点,这样就能对上面的样本进行预测了。当然,特征与特征之间的收益计算是互不影响的,所以这个遍历特征的过程其实可以并行运行。
2、近似算法
贪婪算法可以得到最优解,但当数据量太大时,无法完全读入内存,贪婪算法的效率就很低;或分布式场景下想应用此算法不好应用。
近似算法:对于每个特征,只考察分位点,可以减少计算复杂度。
在选取候选切分点时有两种策略:
- Globa:学习每棵树前就确定候选切分点,并在每次分裂时都采用这种分割。
- Local:每次分裂前将重新选出候选切分点。
local策略需要更多的计算步骤,而globa策略因为节点没有划分所以需要更多的候选点。
两种近似策略以及贪婪算法的 AUC曲线:
图中横坐标为迭代次数;纵坐标为测试集AUC;eps为近似算法的精度,其倒数为桶的数量(候选点的数量)。
Globa策略在候选点多时(eps小),可以和Local策略在候选点少时(eps大)具有相似的准确度。
在eps取值合理时,近似算法能取得和贪婪算法(exact greedy)相同的AUC。几种evaluation metric的区别
![](https://i-blog.csdnimg.cn/blog_migrate/c008f108bb376cdd4fca8e2c4c36f85e.png)
上方论文原图,第一个循环,遍历特征,根据每个特征的分布找到候选切分点集合
S
k
S_k
Sk。
第二个循环,遍历每个特征,将样本映射到上一步根据对应切分点划分出的分桶区间中。并对每个桶统计G、H,根据统计量选择最佳分裂点。
3、加权分位数
实际上xgboost不是简单地按样本个数分位,而是以二阶导数值
h
i
h_i
hi 作为样本的权重进行分位。
1/3分位点 :(0.1×6)/(0.1×6+0.4+0.2+0.6)=1/3
https://www.youtube.com/watch?v=oRrKeUCEbq8
纯分位数分桶:
加权分位数分桶:
xgboost only uses the 【Approximate Greedy Algorithm】,【Parallel Learning】 and the 【Weighted Quantile Sketch】 when the Training Dataset is huge;
When the Training Datasetes are small ,xgboost just uses a noemal,every day 【Greedy Algorithm】。
为什么用 $h_i$ 作为样本权重?
目的是找使本次分裂的增益最大的分裂点;
增益最大 意味着 目标函数减小得最多,目标函数由损失函数、抑制复杂度组成,即损失函数下降得更多,那加权分位数相较于 直接单纯的分位数,优点在哪呢,我看到有文章说能加速这个找最佳分裂点的过程?怎么加速的呢?(待解决)
稀疏感知算法
xgboost在应对数据缺失时的分裂策略
xgboost在构建树的节点过程中只考虑非缺失值的数据遍历,但为每个节点增加了一个缺省方向,当样本相应的特征值缺失时,分别将该样本归为左右分支,计算归为两边各自的增益,选增益最大的那一边作为最优缺省方向。
注意:xgboost在构建树的过程中只考虑了特征未缺失的样本的遍历,而特征值缺失的样本直接被分配到左右节点。
因此遍历的样本量减少,稀疏感知算法比basic算法速度快得多。
树何时停止分裂?
1、当本次分裂引入的增益小于设定阈值时,本次分裂不进行。
2、设定超参数max_depth,即当树的深度大于该值时停止分裂。
3、设定超参数min_child_weight,当某个叶子节点的权重
H
j
H_j
Hj(该叶子上所有样本的权重
h
i
h_i
hi ,
i
i
i∈
I
j
I_j
Ij 之和)小于该值时停止分裂。