XGB算法梳理
XGBoosting是Boosting算法中的一种。
CART树
CART即可用于分类也可用于回归,两者使用的分裂因子不同。因为XGB算法用到的是CART回归树,故如下只讨论CART回归树。
CART回归树是基于二叉树的基础上,对特征进行不断的分裂得到的。根节点包含所有的特征,如下假设当前树结点是基于第j个特征值进行分裂的,设该特征值小于s的样本划分为左子树,大于s的样本划分为右子树,
R
1
(
j
,
s
)
=
{
x
∣
x
(
j
)
≤
s
}
a
n
d
R
2
(
j
,
s
)
=
{
x
∣
x
(
j
)
>
s
}
(
1
)
R_{1}(j,s)=\left\{x|x^{(j)}\leq s \right \}\ and\ R_{2}(j,s)=\left \{x|x^{(j)} > s \right\} \ \ \ \ (1)
R1(j,s)={x∣x(j)≤s} and R2(j,s)={x∣x(j)>s} (1)
本质为在该特征维度进行空间划分,而这种空间划分的优化是一种NP难问题,因此,在决策树模型中是使用启发式方法解决。典型CART回归树产生的目标函数为:
L
=
∑
x
i
∈
R
m
(
y
i
−
f
(
x
i
)
)
2
(
2
)
L=\sum_{x_{i}\in R_{m}}(y_{i}-f(x_{i}))^{2}\ \ \ \ (2)
L=xi∈Rm∑(yi−f(xi))2 (2)
结合(1)式可将目标函数转化为:
L
=
min
⎵
j
,
s
[
min
⎵
c
1
∑
x
i
∈
R
1
(
j
,
s
)
(
y
i
−
c
1
)
2
+
min
⎵
c
2
∑
x
i
∈
R
2
(
j
,
s
)
(
y
i
−
c
2
)
2
]
(
3
)
L=\underbrace{\min}_{j,s} \left[\underbrace{\min}_{c_{1}} \sum_{x_{i} \in R_{1}(j,s)}(y_{i}-c_{1})^{2}+\underbrace{\min}_{c_{2}} \sum_{x_{i} \in R_{2}(j,s)}(y_{i}-c_{2})^{2}\right]\ \ \ \ (3)
L=j,s
min⎣⎡c1
minxi∈R1(j,s)∑(yi−c1)2+c2
minxi∈R2(j,s)∑(yi−c2)2⎦⎤ (3)
其中
c
1
=
1
N
1
∑
x
i
∈
R
1
f
(
x
i
)
;
c
2
=
1
N
2
∑
x
i
∈
R
2
f
(
x
i
)
(
4
)
c_{1}=\frac{1}{N_{1}}\sum_{x_{i}\in R_{1}}f(x_{i})\ \ ; \ c_{2}=\frac{1}{N_{2}}\sum_{x_{i}\in R_{2}}f(x_{i})\ \ \ \ (4)
c1=N11xi∈R1∑f(xi) ; c2=N21xi∈R2∑f(xi) (4)
这样固定
j
j
j后只需要找到最优的
s
s
s,然后通过遍历所有的变量,可以得到最优对
(
j
,
s
)
(j,s)
(j,s),确定两个区间。
算法描述:
- 输入:训练样本数据
- 输出:回归树 f ( x ) f(x) f(x)
- 步骤:在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树
- a.选择特征变量 j j j与切分点 s s s,求解(3)式,遍历变量 j j j,对固定的特征变量 j j j扫描切分点 s s s,选择使(3)式达到最小值的最优切分对 ( j , s ) (j,s) (j,s)
- b.用选定的对 ( j , s ) (j,s) (j,s)划分区域(1)式,并决定相应的输出值(4)式
- c.继续对两个子区域调用以上两个步骤,直至满足停止条件
- d.将输入空间划分为
M
M
M个区域
R
1
,
R
2
,
.
.
.
.
,
R
M
R_{1},R_{2},....,R_{M}
R1,R2,....,RM,生成决策树:
f ( x ) = ∑ m = 1 M c m I ( x ∈ R m ) ( 5 ) f(x)=\sum_{m=1}^{M}c_{m}I(x \in R_{m})\ \ \ \ (5) f(x)=m=1∑McmI(x∈Rm) (5)
XGB算法原理
泰勒公式
- 定义:泰勒公式是一个用函数在某点的信息描述其附近取值的公式(局部有效性)
- 基本形式
f ( x ) = ∑ n = 0 ∞ f ( n ) ( x o ) n ! ( x − x o ) n ( 6 ) f(x)=\sum_{n=0}^{\infty}\frac{f^{(n)}(x_{o})}{n!}(x-x_{o})^n\ \ \ \ (6) f(x)=n=0∑∞n!f(n)(xo)(x−xo)n (6)- 一阶泰勒展开: f ( x ) ≈ f ( x o ) + f ′ ( x o ) ( x − x o ) ( 7 ) f(x)\approx f(x_{o})+f{}'(x_{o})(x-x_{o})\ \ \ \ (7) f(x)≈f(xo)+f′(xo)(x−xo) (7)
- 二阶泰勒展开: f ( x ) ≈ f ( x o ) + f ′ ( x o ) ( x − x o ) + f ′ ′ ( x o ) ( x − x o ) 2 2 ( 8 ) f(x)\approx f(x_{o})+f{}'(x_{o})(x-x_{o})+f{}''(x_{o})\frac{(x-x_{o})^2}{2}\ \ \ \ (8) f(x)≈f(xo)+f′(xo)(x−xo)+f′′(xo)2(x−xo)2 (8)
- 迭代形式:假设
x
t
=
x
t
−
1
+
Δ
x
x^t=x^{t-1}+\Delta x
xt=xt−1+Δx,将
f
(
x
)
f(x)
f(x)在
x
t
−
1
x^{t-1}
xt−1处进行泰勒展开
f ( x ) = f ( x t − 1 + Δ x ) = f ( x t − 1 ) + f ′ ( x t − 1 ) ( Δ x ) + f ′ ′ ( x t − 1 ) ( Δ x ) 2 2 ( 9 ) f(x)=f(x^{t-1}+\Delta x)=f(x^{t-1})+f{}'(x^{t-1})(\Delta x)+f{}''(x^{t-1})\frac{(\Delta x)^2}{2}\ \ \ \ (9) f(x)=f(xt−1+Δx)=f(xt−1)+f′(xt−1)(Δx)+f′′(xt−1)2(Δx)2 (9)
XGB算法思想
该算法思想与GBDT思想部分类似,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数,去拟合上次预测的残差。当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数,最后只需要将每棵树对应的分数加起来就是该样本的预测值。
y
^
=
∑
k
=
1
K
f
k
(
x
i
)
(
10
)
\hat{y}=\sum_{k=1}^{K}f_{k}(x_{i})\ \ \ \ (10)
y^=k=1∑Kfk(xi) (10)
如下图例子,训练出了2棵决策树,小孩的预测分数就是两棵树中小孩所落到的结点的分数相加。爷爷的预测分数同理。
XGB算法原理
XGBoost目标函数定义为:
O
b
j
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
)
+
∑
k
=
1
K
Ω
(
f
k
)
(
11
)
Obj=\sum_{i=1}^{n}l(y_{i},\hat{y}_{i})+\sum_{k=1}^{K}\Omega(f_{k})\ \ \ \ (11)
Obj=i=1∑nl(yi,y^i)+k=1∑KΩ(fk) (11)
w
h
e
r
e
Ω
(
f
)
=
γ
T
+
1
2
λ
∣
∣
w
∣
∣
2
where\ \Omega(f)=\gamma T+\frac{1}{2}\lambda||w||^2
where Ω(f)=γT+21λ∣∣w∣∣2
这个目标函数同样包含两部分,第一部分就是损失函数,可以是均方损失、log损失等,衡量预测值与真实值的差距,第二部分就是正则项,L1,L2均可,正则化项同样包含两部分,
T
T
T表示叶子结点的个数,
w
w
w表示叶子节点的分数。
γ
\gamma
γ可以控制叶子结点的个数,
λ
\lambda
λ可以控制叶子节点的分数不会过大,防止过拟合。
依据算法思想,新生成的树要拟合上次预测的残差,即当生成
t
t
t棵树后,预测分数可以写成:
y
^
t
=
y
^
t
−
1
+
f
t
(
x
i
)
(
12
)
\hat{y}^{t}=\hat{y}^{t-1}+f^{t}(x_{i})\ \ \ \ (12)
y^t=y^t−1+ft(xi) (12)
将上式代入11式得到新目标函数:
O
b
j
=
∑
i
=
1
n
l
(
y
i
,
y
^
t
−
1
+
f
t
(
x
i
)
)
+
∑
k
=
1
K
Ω
(
f
k
)
(
13
)
Obj=\sum_{i=1}^{n}l(y_{i},\hat{y}^{t-1}+f^{t}(x_{i}))+\sum_{k=1}^{K}\Omega(f_{k})\ \ \ \ (13)
Obj=i=1∑nl(yi,y^t−1+ft(xi))+k=1∑KΩ(fk) (13)
接下来就是要去找到一个
f
t
f^{t}
ft能够最小化目标函数。XGBoost的想法是利用其在
f
t
f^{t}
ft在0处的泰勒二阶展开近似它。所以,目标函数近似为
y
^
t
=
y
^
t
−
1
+
f
t
(
x
i
)
\hat{y}^{t}=\hat{y}^{t-1}+f^{t}(x_{i})
y^t=y^t−1+ft(xi)
h
(
y
^
t
−
1
)
=
l
(
y
^
,
y
^
t
−
1
+
f
t
(
x
i
)
)
h(\hat{y}^{t-1})=l(\hat{y},\hat{y}^{t-1}+f^{t}(x_{i}))
h(y^t−1)=l(y^,y^t−1+ft(xi))
L
t
≈
∑
i
=
1
n
l
[
(
y
i
,
y
^
t
−
1
)
+
g
i
f
t
(
x
i
)
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
∑
k
=
1
K
Ω
(
f
k
)
L^{t} \approx\sum_{i=1}^{n}l[(y_{i},\hat{y}^{t-1})+g_{i}f^{t}(x_{i}))+\frac{1}{2}h_{i}f_{t}^2(x_i)]+\sum_{k=1}^{K}\Omega(f_{k})
Lt≈i=1∑nl[(yi,y^t−1)+gift(xi))+21hift2(xi)]+k=1∑KΩ(fk)
其中
g
i
g_i
gi为一阶导数,
h
i
h_i
hi为二阶导数
由于
t
−
1
t-1
t−1棵树的预测分数与
y
y
y的残差对目标函数优化不影响,可以直接去掉。简化目标函数为:
L
t
=
∑
i
=
1
n
[
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
Ω
(
f
t
)
L^{t}=\sum_{i =1}^{n}[g _if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)]+\Omega (f_t)
Lt=i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)
上式是将每个样本的损失函数值加起来,我们知道,每个样本都最终会落到一个叶子结点中,所以我们可以将所以同一个叶子结点的样本重组起来
L
t
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
i
)
w
j
+
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
w
j
2
]
+
γ
T
L^t=\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
Lt=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2]+γT
因此通过上式的改写,我们可以将目标函数改写成关于叶子结点分数w的一个一元二次函数,求解最优的w和目标函数值就变得很简单了,直接使用顶点公式即可。因此,最优的w和目标函数公式为
w
j
∗
=
−
G
j
H
j
+
λ
;
O
b
j
=
−
1
2
∑
j
=
1
T
G
j
2
H
j
+
λ
+
γ
T
w_j^*=-\frac{G_j}{H_j+\lambda}\ \ ; \ Obj=-\frac{1}{2}\sum_{j=1}^{T}\frac{G_j^2}{H_j+\lambda}+\gamma T
wj∗=−Hj+λGj ; Obj=−21j=1∑THj+λGj2+γT
分裂节点算法
在上面的推导中,我们知道了如果我们一棵树的结构确定了,如何求得每个叶子结点的分数。但我们还没介绍如何确定树结构,即每次特征分裂怎么寻找最佳特征,怎么寻找最佳分裂点。
正如上文说到,基于空间切分去构造一颗决策树是一个NP难问题,我们不可能去遍历所有树结构,因此,XGBoost使用了和CART回归树一样的想法,利用贪婪算法,遍历所有特征的所有特征划分点,不同的是使用上式目标函数值作为评价函数。具体做法就是分裂后的目标函数值比单子叶子节点的目标函数的增益,同时为了限制树生长过深,还加了个阈值,只有当增益大于该阈值才进行分裂。
同时可以设置树的最大深度、当样本权重和小于设定阈值时停止生长去防止过拟合。
对缺失值处理
xgboost模型能够处理缺失值,模型允许缺失值存在。
原始论文中关于缺失值的处理将其看与稀疏矩阵的处理看作一样。在寻找split point的时候,不会对该特征为missing的样本进行遍历统计,只对该列特征值为non-missing的样本上对应的特征值进行遍历,通过这个技巧来减少了为稀疏离散特征寻找split point的时间开销。在逻辑实现上,为了保证完备性,会分别处理将missing该特征值的样本分配到左叶子结点和右叶子结点的两种情形,计算增益后选择增益大的方向进行分裂即可。可以为缺失值或者指定的值指定分支的默认方向,这能大大提升算法的效率。如果在训练中没有缺失值而在预测中出现缺失,那么会自动将缺失值的划分方向放到右子树。
优缺点
优点
- xgBoosting支持线性分类器,相当于引入L1和L2正则化项的逻辑回归(分类问题)和线性回归(回归问题)。
- xgBoosting对代价函数做了二阶Talor展开,引入了一阶导数和二阶导数。
- 当样本存在缺失值是,xgBoosting能自动学习分裂方向。
- xgBoosting借鉴RF的做法,支持列抽样,这样不仅能防止过拟合,还能降低计算。
- xgBoosting的代价函数引入正则化项,控制了模型的复杂度,正则化项包含全部叶子节点的个数,每个叶子节点输出的score的L2模的平方和。从贝叶斯方差角度考虑,正则项降低了模型的方差,防止模型过拟合。
- xgBoosting在每次迭代之后,为叶子结点分配学习速率,降低每棵树的权重,减少每棵树的影响,为后面提供更好的学习空间。
- xgBoosting工具支持并行,但并不是tree粒度上的,而是特征粒度,决策树最耗时的步骤是对特征的值排序,xgBoosting在迭代之前,先进行预排序,存为block结构,每次迭代,重复使用该结构,降低了模型的计算;block结构也为模型提供了并行可能,在进行结点的分裂时,计算每个特征的增益,选增益最大的特征进行下一步分裂,那么各个特征的增益可以开多线程进行。
- 可并行的近似直方图算法,树结点在进行分裂时,需要计算每个节点的增益,若数据量较大,对所有节点的特征进行排序,遍历的得到最优分割点,这种贪心法异常耗时,这时引进近似直方图算法,用于生成高效的分割点,即用分裂后的某种值减去分裂前的某种值,获得增益,为了限制树的增长,引入阈值,当增益大于阈值时,进行分裂;
缺点
- xgBoosting采用预排序,在迭代之前,对结点的特征做预排序,遍历选择最优分割点,数据量大时,贪心法耗时,LightGBM方法采用histogram算法,占用的内存低,数据分割的复杂度更低;
- xgBoosting采用level-wise生成决策树,同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合,但很多叶子节点的分裂增益较低,没必要进行跟进一步的分裂,这就带来了不必要的开销;LightGBM采用深度优化,leaf-wise生长策略,每次从当前叶子中选择增益最大的结点进行分裂,循环迭代,但会生长出更深的决策树,产生过拟合,因此引入了一个阈值进行限制,防止过拟合.
sklearn参数
- eta [默认 0.3]
和 GBM 中的 learning rate 参数类似。 通过减少每一步的权重,可以提高模型的稳定性。 典型值为 0.01-0.2。 - min_child_weight [默认 1]
决定最小叶子节点样本权重和。和 GBM 的 min_child_leaf 参数类似,但不完全一样。XGBoost 的这个参数是最小样本权重的和,而 GBM 参数是最小样本总数。这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。但是如果这个值过高,会导致欠拟合。这个参数需要使用 CV 来调整。 - max_depth [默认 6]
和 GBM 中的参数相同,这个值为树的最大深度。这个值也是用来避免过拟合的。max_depth 越大,模型会学到更具体更局部的样本。需要使用 CV 函数来进行调优。 典型值:3-10 - max_leaf_nodes
树上最大的节点或叶子的数量。 可以替代 max_depth 的作用。因为如果生成的是二叉树,一个深度为 n 的树最多生成 n2 个叶子。 如果定义了这个参数,GBM 会忽略 max_depth 参数。 - gamma [默认 0]
在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma 指定了节点分裂所需的最小损失函数下降值。 这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关,所以是需要调整的。 - max_delta_step[默认 0]
这参数限制每棵树权重改变的最大步长。如果这个参数的值为 0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。 通常,这个参数不需要设置。但是当各类别的样本十分不平衡时,它对逻辑回归是很有帮助的。 这个参数一般用不到,但是你可以挖掘出来它更多的用处。 - subsample [默认 1]
和 GBM 中的 subsample 参数一模一样。这个参数控制对于每棵树,随机采样的比例。 减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。 典型值:0.5-1 - colsample_bytree [默认 1]
和 GBM 里面的 max_features 参数类似。用来控制每棵随机采样的列数的占比 (每一列是一个特征)。 典型值:0.5-1 - colsample_bylevel [默认 1]
用来控制树的每一级的每一次分裂,对列数的采样的占比。 我个人一般不太用这个参数,因为 subsample 参数和 colsample_bytree 参数可以起到相同的作用。但是如果感兴趣,可以挖掘这个参数更多的用处。 - lambda [默认 1]
权重的 L2 正则化项。(和 Ridge regression 类似)。 这个参数是用来控制 XGBoost 的正则化部分的。虽然大部分数据科学家很少用到这个参数,但是这个参数在减少过拟合上还是可以挖掘出更多用处的。 - alpha [默认 1]
权重的 L1 正则化项。(和 Lasso regression 类似)。 可以应用在很高维度的情况下,使得算法的速度更快。 - scale_pos_weight [默认 1]
在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。