XGBoost算法梳理
XGBoost自发布以来就受到了大家的关注,可以发现目前很多预测类竞赛的前几名解决方案中都经常出现XGBoost。很多选手都是在特征工程后直接通过XGBoost和lightGBM进行预测,一般调参后都能取得不错的效果。
目录
1. XGB
2. CART树
3. 算法原理
4. 分裂结点算法
5. 损失函数
6. 正则化
7. 对缺失值处理
8. 优缺点
9. sklearn参数
10. 应用场景
1. XGB
XGBoost自提出以来就受到了广泛的关注,可以发现现在许多竞赛中都使用了XGBoost以及lightGBM作为其预测算法。很多竞赛中前几名的解决方案都是在进行特征工程后直接使用XGBoost进行调参预测,并都取得了不错的效果。
在工业方面,由于XGBoost的分布式版本有广泛的可移植性,支持在YARN, MPI, Sungrid Engine等各个平台上面运行,并且保留了单机并行版本的各种优化,使得它可以很好地解决于工业界规模的问题。
2. CART树
CART作为Boosting Tree 最基本的部分,又名分类回归树,是一棵二叉树,既能是分类树,又能是分类树。当CART作为分类树时,采用GINI值作为节点分裂的依据。当CART作为回归树时,采用样本的最小方差作为节点分裂的依据。
3. 算法原理
假设每次迭代生成一棵树,第t次训练的目标函数可以写成:
obj
(
θ
)
(
t
)
=
Σ
i
n
l
(
y
i
,
y
i
^
(
t
)
)
+
Σ
k
=
1
t
Ω
(
f
k
)
\text{obj}(\theta)^{(t)}=\Sigma_i^nl(y_i,\hat{y_i}^{(t)})+\Sigma_{k=1}^t\Omega(f_k)
obj(θ)(t)=Σinl(yi,yi^(t))+Σk=1tΩ(fk)
其中前半部分是训练误差,后半分是每棵树的复杂度。
y
i
^
(
t
)
\hat{y_i}^{(t)}
yi^(t)为第t步迭代的预测值,且有一下迭代关系:
y
i
^
(
0
)
=
0
\hat{y_i}^{(0)}=0
yi^(0)=0
y
i
^
(
1
)
=
f
1
(
x
i
)
=
y
i
^
(
0
)
+
f
1
(
x
i
)
\hat{y_i}^{(1)}=f_1(x_i) = \hat{y_i}^{(0)}+f_1(x_i)
yi^(1)=f1(xi)=yi^(0)+f1(xi)
⋯
\cdots
⋯
y
i
^
(
t
)
=
Σ
k
=
1
t
(
f
k
(
x
i
)
)
=
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
\hat{y_i}^{(t)}=\Sigma_{k=1}^t(f_k(x_i))= \hat{y_i}^{(t-1)}+f_t(x_i)
yi^(t)=Σk=1t(fk(xi))=yi^(t−1)+ft(xi)
XGBoost通过不断的生成新树,来优化决策,每一棵树都学习之前生成树的结论,不断优化。其基本思想和GBDT是一样的,都是按照损失函数的负梯度方向提升,只是XGBoost进行了泰勒二次展开,加了一些正则项。
4.分裂结点算法
如何来寻找一个最优结构的树,加入到我们的模型中?常用的是贪心法,每一次尝试对已经的叶子加入一个分割,对一个具体的分割方案,我们可以获得分割后的增益为:
G
a
i
n
=
1
2
[
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
−
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
]
−
γ
Gain = \frac{1}{2} \left[\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}\right] - \gamma
Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
如果Gain<0,则此节点不应该split成左右两支。
对于每次扩展,我们还是要枚举所有可能的分割方案,实际应用中,先将 g i g_i gi 从小到大排序,然后进行遍历,看每个结点是否需要分裂。
包括贪心算法陈天奇论文中一共提到了三种分裂算法:
-
贪心算法(exact greedy algorithm)
当数据量过大,由于要遍历每个分割点,十分消耗内存,贪心算法就显得不再适用,所以提出了额外一种近似算法能加快运行时间。 -
近似算法(approximate algorithm)
近似算法根据特征的分布情况,先提出proposal,然后分割点就从候选proposal中选择,该算法大大的提高了效率。这里有两种proposal的方式,global、local的。global的是在建树之前就做proposal然后之后每次分割都要更新一下proposal,local的方法是在每次分裂之后更新proposal。
-
分布式加权直方图算法 (Weighted Quantile Sketch)
近似算法在特征分布然后做proposal的时候,用到了加权分位直方图。将数据投射在一个小的存储空间内作为整个数据的概要,需要分裂节点时查看数据概要,以此来减少计算分裂节点是的用于储存数据的内存。
5. 损失函数
第t次训练的目标函数:
o
b
j
(
t
)
=
Σ
i
=
1
n
l
(
y
i
,
y
i
^
(
t
)
)
+
Σ
i
=
1
t
Ω
(
f
i
)
obj^{(t)} =\Sigma_{i=1}^nl(y_i,\hat{y_i}^{(t)})+\Sigma_{i=1}^t\Omega(f_i)
obj(t)=Σi=1nl(yi,yi^(t))+Σi=1tΩ(fi)
o b j ( t ) = Σ i = 1 n l ( y i , y i ^ ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) + Σ i = 1 ( t − 1 ) Ω ( f i ) obj^{(t)} =\Sigma_{i=1}^nl(y_i,\hat{y_i}^{(t-1)}+ f_t(x_i))+\Omega(f_t)+ \Sigma_{i=1}^{(t-1)}\Omega(f_i) obj(t)=Σi=1nl(yi,yi^(t−1)+ft(xi))+Ω(ft)+Σi=1(t−1)Ω(fi)
对于第t步来说
Σ
i
=
1
(
t
−
1
)
Ω
(
f
i
)
\Sigma_{i=1}^{(t-1)}\Omega(f_i)
Σi=1(t−1)Ω(fi)是已知的,所以可以得到下一步。
o
b
j
(
t
)
=
Σ
i
=
1
n
l
(
y
i
,
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
)
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
obj^{(t)} =\Sigma_{i=1}^nl(y_i,\hat{y_i}^{(t-1)}+ f_t(x_i))+\Omega(f_t)+ constant
obj(t)=Σi=1nl(yi,yi^(t−1)+ft(xi))+Ω(ft)+constant
如果使用平方损失函数,则有
o
b
j
(
t
)
=
Σ
i
=
1
n
[
y
i
−
(
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
]
2
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
obj^{(t)} =\Sigma_{i=1}^n[y_i-(\hat{y_i}^{(t-1)}+ f_t(x_i)]^2+\Omega(f_t)+ constant
obj(t)=Σi=1n[yi−(yi^(t−1)+ft(xi)]2+Ω(ft)+constant
=
Σ
i
=
1
n
[
y
i
2
−
2
y
i
(
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
)
+
(
y
i
^
(
t
−
1
)
+
f
t
(
x
i
)
)
2
]
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
=\Sigma_{i=1}^n[y_i^2-2y_i(\hat{y_i}^{(t-1)}+ f_t(x_i))+(\hat{y_i}^{(t-1)}+ f_t(x_i))^2]+\Omega(f_t)+ constant
=Σi=1n[yi2−2yi(yi^(t−1)+ft(xi))+(yi^(t−1)+ft(xi))2]+Ω(ft)+constant
=
Σ
i
=
1
n
[
y
i
2
−
2
y
i
y
i
^
(
t
−
1
)
−
2
y
i
f
t
(
x
i
)
+
(
y
i
^
(
t
−
1
)
)
2
+
2
y
i
^
(
t
−
1
)
f
t
(
x
i
)
+
(
f
t
(
x
i
)
)
2
]
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
=\Sigma_{i=1}^n[y_i^2-2y_i\hat{y_i}^{(t-1)}- 2y_if_t(x_i)+ (\hat{y_i}^{(t-1)})^2+2\hat{y_i}^{(t-1)}f_t(x_i) +(f_t(x_i))^2]+\Omega(f_t)+ constant
=Σi=1n[yi2−2yiyi^(t−1)−2yift(xi)+(yi^(t−1))2+2yi^(t−1)ft(xi)+(ft(xi))2]+Ω(ft)+constant
=
Σ
i
=
1
n
[
2
(
y
i
^
(
t
−
1
)
−
y
i
)
f
t
(
x
i
)
+
(
f
t
(
x
i
)
)
2
]
+
Σ
i
=
1
n
[
(
y
i
)
2
−
2
y
i
y
i
^
+
(
y
i
^
(
t
−
1
)
)
2
]
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
=\Sigma_{i=1}^n[2{(\hat{y_i}^{(t-1)} - y_i)}f_t{(x_i)} +(f_t(x_i))^2]+ \Sigma_{i=1}^n[{(y_i)}^2-2y_i\hat{y_i} + {(\hat{y_i}^{(t-1)})}^2 ]+\Omega(f_t)+ constan
=Σi=1n[2(yi^(t−1)−yi)ft(xi)+(ft(xi))2]+Σi=1n[(yi)2−2yiyi^+(yi^(t−1))2]+Ω(ft)+constan
其中对于第t步来说,
Σ
i
=
1
n
[
(
y
i
)
2
−
2
y
i
y
i
^
+
(
y
i
^
(
t
−
1
)
)
2
]
\Sigma_{i=1}^n[{(y_i)}^2-2y_i\hat{y_i} + {(\hat{y_i}^{(t-1)})}^2 ]
Σi=1n[(yi)2−2yiyi^+(yi^(t−1))2]
也是常数,所以目标函数优化为:
o
b
j
(
t
)
=
Σ
i
=
1
n
[
2
(
y
i
^
(
t
−
1
)
−
y
i
)
f
t
(
x
i
)
+
(
f
t
(
x
i
)
)
2
]
+
Ω
(
f
t
)
+
c
o
n
s
t
a
n
t
obj^{(t)}=\Sigma_{i=1}^n[2{(\hat{y_i}^{(t-1)} - y_i)}f_t{(x_i)} +(f_t(x_i))^2]+\Omega(f_t)+ constant
obj(t)=Σi=1n[2(yi^(t−1)−yi)ft(xi)+(ft(xi))2]+Ω(ft)+constant
其中 ( y i ^ ( t − 1 ) − y i ) (\hat{y_i}^{(t-1)} - y_i) (yi^(t−1)−yi)一般叫做残差。
在实际的业务场景下,我们往往需要自定义损失函数。损失函数自定义
6. 正则化
xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项中包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。 正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合。
7. 对缺失值处理
XGBoost处理缺失值的方法如下图所示(截取自陈天奇论文),XGBoost把缺失值当做稀疏矩阵来对待,节点分裂时不考虑的缺失值的数值。在训练模型时训练模型时缺失数据会被分到左子树和右子树分别计层损失,选取结果较优的子树。在预测时预测数据中的缺失值,默认分类到右子树。
8.优缺点
① 优点
- XGBoost支持线性分类器。这个时候xgboost相当于带L1和L2正则化项的LR或者线性回归。
- xgboost在代价函数里加入了正则项,用于控制模型的复杂度,有效的防止过拟合。
- xgboost借鉴了随机森林的做法,支持列抽样,在降低过拟合的同时,还能减少计算。
- 可以直接处理有缺失值的数据集。
- XGBoost工具支持并行。XGBoost的并行是在特征粒度上的,并行化各个特征的增益计算。
- 高效地生成候选的分割点,通过采用并行的近似直方图算法。
- 具有很好的灵活性,XGBoost支持用户自定义目标函数和评估函数,只要目标函数二阶可导就行。
- 用剪枝使得算法不容易陷入局部最优,XGBoost 先从顶到底建立所有可以建立的子树,再从底到顶反向进行剪枝。
- 内置交叉验证XGBoost允许在每一轮boosting迭代中使用交叉验证。可以方便地获得最优boosting迭代次数。
② 缺点
-
XGBoost在每轮迭代时,都需要遍历整个训练数据多次。若把整个训练数据装进内存则会消耗大量内存,但是不装进内存,反复地读写训练数据又会消耗大量通信时间。
-
预排序方法导致空间、时间消耗大。因为该算法需要保存数据的特征值,还保存了特征排序的结果需要消耗训练数据两倍的内存,在遍历每一个分割点的时候,都需要进行分裂增益的计算,花费时间多。
-
对cache优化不友好。在预排序后,特征对梯度的访问是一种随机访问,并且不同的特征访问的顺序不一样,无法对cache进行优化。
-
XGBoosting采用level-wise生成决策树,同时分裂同一层的叶子,很多叶子节点的分裂增益较低,带来了不必要的开销。
9. sklearn参数
由于Xgboost的参数过多,此部分只介绍常用的一些参数。由于参数过多网格搜索会很慢,具体调参可参考链接调参地址
- objective [ default=reg:linear ] 定义学习任务及相应的学习目标,可选的目标函数如下:
“reg:linear” 线性回归。
“reg:logistic” 逻辑回归。
“binary:logistic” 二分类的逻辑回归问题,输出为概率。
“binary:logitraw” 二分类的逻辑回归问题,输出的结果为 w T x w^Tx wTx。
“count:poisson” 计数问题的poisson回归,输出结果为poisson分布。 在poisson回归中,max_delta_step的缺省值为0.7。
“multi:softmax” 采用softmax目标函数处理多分类问题,同时需要设置参数num_class(类别个数)
“multi:softprob” 和softmax一样,但是输出的是ndata * nclass的向量,可以将该向量reshape成ndata行nclass列的矩阵。m每行数据表示样本所属于每个类别的概率。
“rank:pairwise” –排序时采用最小化pairwise损失 - ’eval_metric’ 评估指标,可选的参数如下:
“rmse”: 根节点均方误差
“logloss”: 负最大似然
“error”: 二分类误差率,判定预测值大于0.5为positive。
“merror”: 多分类误差率
“mlogloss”: 多分类对数损失
“auc”:ROC面积
“ndcg”:归一化折损累计增益,通常是用来衡量和评价搜索结果算法,计算相对复杂。
“map”:准确率的平均值。 - lambda [default=0] L2 正则的惩罚系数。
- alpha [default=0] L1 正则的惩罚系数。
- lambda_bias [default=0] 在偏置上的L2正则。
- eta [default=0.3] 为了防止过拟合,更新过程中用到的收缩步长。在每次提升计算之后,算法会直接获得新特征的权重。eta通过缩减特征的权重使提升计算过程更加保守。取值范围为:[0,1]
- max_depth [default=6] 数的最大深度。取值范围为:[1, ∞ ∞ ∞]
- min_child_weight [default=1] 孩子节点中最小的样本权重和。如果一个叶子节点的样本权重和小于min_child_weight则拆分过程结束。取值范围为: [0, ∞ ∞ ∞]
10. 应用场景
XGBoost由于其优越性被广泛运用与分类和回归预测中,可以用于二分类也可以用于多分类。同时由于其可以并行话实现也可以用于工业规模的应用中。