1 简述
XGBoost(eXtreme Gradient Boosting,极端梯度提升算法)是Gradient Boosting框架下的一种高效能实现。为了提升性能,XGBoost算法主要在以下几个方面做了提升:
- 在目标函数上,XGBoost会考虑模型复杂度,这样可以使学习出来的模型更简单,防止过拟合。此时,XGBoost算法中每一步迭代时的目标函数为: J ( f t ) = ∑ i = 1 N L ( y i , y ^ i t − 1 + f t ( x i ) ) + Ω ( f t ) + C J(f_{t})=\sum_{i=1}^{N}L(y_{i},\widehat y_{i}^{t-1}+f_{t}(x_{i}))+\Omega(f_{t})+C J(ft)=i=1∑NL(yi,y it−1+ft(xi))+Ω(ft)+C其中 C C C为常数项, Ω ( f t ) \Omega(f_{t}) Ω(ft)为模型复杂度, N N N为训练样本个数。
- 另外在梯度提升方法求解加法模型时,只考虑了一阶导数(负梯度方向)。为了能让梯度下降的更快更准,可以考虑同时使用一阶和二阶导数。依据泰勒展开公式: f ( x + Δ x ) ≈ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+\Delta x)\approx f(x)+f^{'}(x)\Delta x+\frac{1}{2}f^{''}(x)\Delta x^{2} f(x+Δx)≈f(x)+f′(x)Δx+21f′′(x)Δx2可以将上述目标公式改为如下: J ( f t ) = ∑ i = 1 N [ L ( y i , y ^ i t − 1 ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + C J(f_{t})=\sum_{i=1}^{N}[L(y_{i},\widehat y_{i}^{t-1})+g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})+C J(ft)=i=1∑N[L(yi,y it−1)+gift(xi)+21hift2(xi)]+Ω(ft)+C其中 g i g_{i} gi为损失函数的一阶导数, h i h_{i} hi为损失函数的二阶导数,其公式如下: g i = ∂ L ( y i , y ^ i t − 1 ) ∂ y ^ i t − 1 , h i = ∂ 2 L ( y i , y ^ i t − 1 ) ∂ y ^ i t − 1 g_{i}=\frac{\partial L(y_{i},\widehat y_{i}^{t-1})}{\partial \widehat y_{i}^{t-1}},h_{i}=\frac{\partial^{2}L(y_{i},\widehat y_{i}^{t-1})}{\partial \widehat y_{i}^{t-1}} gi=∂y it−1∂L(yi,y it−1),hi=∂y it−1∂2L(yi,y it−1)
- 当XGBoost算法使用CART作为基模型时,为了加快寻找特征的最佳分割点,XGBoost算法在训练之前预先将样本按照特征进行排序,并将结果保存在block结构中。同时,XGBoost算法中使用多线程同时计算多个特征的分裂增益。
2 XGBoost算法
XGBoost算法中不仅可以使用CART作为基模型,还可以使用线性模型。下面以使用CART作为基模型的XGBoos为例对其进行详细介绍。
2.1 目标函数化简
当使用CART作为基模型时,用叶结点数与叶结点权值表示模型复杂度。其公式如下: Ω ( f t ) = γ T + λ 2 ∑ j = 1 T w j 2 \Omega(f_{t})=\gamma T+\frac{\lambda}{2}\sum_{j=1}^{T}w_{j}^{2} Ω(ft)=γT+2λj=1∑Twj2其中, T T T为决策树叶子结点数, w j w_{j} wj为叶子结点的权值。结合泰勒公式,对其目标函数进行化简优化,具体如下: J ( f t ) = ∑ i = 1 N [ L ( y i , y ^ i t − 1 + f t ( x i ) ) ] + Ω ( f t ) + C = ∑ i = 1 N [ L ( y i , y ^ i t − 1 ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + C = ∑ i = 1 N [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + C = ∑ i = 1 N [ g i w q ( x i ) + 1 2 h i w q ( x i ) 2 ] + γ T + λ 2 ∑ j = 1 T w j 2 + C = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ i ∈ I j h i ) w j 2 ] + γ T + λ 2 ∑ j = 1 T w j 2 + C = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ i ∈ I j h i + λ ) w j 2 ] + γ T + C \begin{aligned}J(f_{t})&=\sum_{i=1}^{N}[L(y_{i},\widehat y_{i}^{t-1}+f_{t}(x_{i}))]+\Omega(f_{t})+C\\ &=\sum_{i=1}^{N}[L(y_{i},\widehat y_{i}^{t-1})+g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})+C \\ &=\sum_{i=1}^{N}[g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})+C\\&=\sum_{i=1}^{N}[g_{i}w_{q(x_{i})}+\frac{1}{2}h_{i}w_{q(x_{i})}^{2}]+\gamma T+\frac{\lambda}{2}\sum_{j=1}^{T}w_{j}^{2}+C\\&=\sum_{j=1}^{T}[(\sum_{i\in I_{j}}g_{i})w_{j}+\frac{1}{2}(\sum_{i\in I_{j}}h_{i})w_{j}^{2}]+\gamma T+\frac{\lambda}{2}\sum_{j=1}^{T}w_{j}^{2}+C\\&=\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+C\end{aligned} J(ft)=i=1∑N[L(yi,y it−1+ft(xi))]+Ω(ft)+C=i=1∑N[L(yi,y it−1)+gift(xi)+21hift2(xi)]+Ω(ft)+C=i=1∑N[gift(xi)+21hift2(xi)]+Ω(ft)+C=i=1∑N[giwq(xi)+21hiwq(xi)2]+γT+2λj=1∑Twj2+C=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi)wj2]+γT+2λj=1∑Twj2+C=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2]+γT+C令 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。则: J ( f t ) = ∑ j = 1 T [ G j w j + 1 2 ( H j + λ ) w j 2 ] + γ T + C J(f_{t})=\sum_{j=1}^{T}[G_{j}w_{j}+\frac{1}{2}(H_{j}+\lambda)w_{j}^{2}]+\gamma T+C J(ft)=j=1∑T[Gjwj+21(Hj+λ)wj2]+γT+C对 w w w求偏导数,得到 ∂ J ( f t ) ∂ w j = G j + ( H j + λ ) w j \frac{\partial J(f_{t})}{\partial w_{j}}=G_{j}+(H_{j}+\lambda)w_{j} ∂wj∂J(ft)=Gj+(Hj+λ)wj令上式等于0,则可以得到: w j = − G j H j + λ w_{j}=-\frac{G_{j}}{H_{j}+\lambda} wj=−Hj+λGj将得到的 w j w_{j} wj带入的目标公式中,得到: J ( f t ) = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T J(f_{t})=-\frac{1}{2}\sum_{j=1}^{T}\frac{G_{j}^{2}}{H_{j}+\lambda}+\gamma T J(ft)=−21j=1∑THj+λGj2+γT
2.2 决策树生长策略
由于决策树的树结构是无穷的,不可能枚举所有可能树结构并计算出该树结构所对应的 J ( f t ) J(f_{t}) J(ft)的值。XGBoost算法使用贪心策略构建每一棵决策树。其步骤如下:
- 从深度为0的树开始,对每个叶节点枚举所有的可用特征。
- 针对每个特征,把属于该节点的训练样本根据该特征值升序排列,通过线性扫描的方式来决定该特征的最佳分裂点,并记录该特征的最大收益(为了降低计算复杂度,XGBoost中使用block保存每个特征的排序结果,降低计算复杂度)。假设当前节点为 C C C,其分裂之后得到的左右子树分别为 L L L和 R R R,则该节点的分裂增益为当前节点的目标函数值减去左右子树目标函数值之和,即: 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}[\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}]-\gamma Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
- 选择收益最大的特征作为分裂特征,用该特征的最佳分裂点作为分裂位置,把该节点生长出左右两个新的叶节点,并为每个新节点关联对应的样本集。
- 回到第1步,递归执行到满足特定条件为止。
2.3 防止过拟合
XGBoost算法中防止过拟合的策略主要有两个方面:(1)shrinkage(衰减): GBDT算法中使用的也是该策略,不在赘述。(2).列采样:类似于随机森林的处理方法,既能防止过拟合,又能减少训练时间。
2.4 优缺点
与GBDT相比,XGBoost在以下方面有提升:
- GBDT使用CART作为基模型,XGBoost不近可以使用CART作为基模型,还可以使用线性分类器,此时相当于引入 L 1 L1 L1和 L 2 L2 L2正则化项的逻辑回归和线性回归。
- GBDT在优化时仅使用了一阶导数,而XGBoost对目标函数做了泰勒展开,使用了一阶导数和二阶导数。使用了二阶导数之后,梯度下降的更快更准。同时,可以在不选定损失函数具体形式的情况下,仅仅依靠输入数据的值就可以进行叶子分裂优化计算,本质上也就把损失函数的选取和模型算法优化/参数选择分开了。
- 当样本存在缺失值时,XGBoost能自动学习分裂方向。
- XGBoost支持列抽样。
- XGBoost中引入了正则化项,控制了模型的复杂度,防止模型过拟合。
- XGBoost在每次迭代之后,为叶子结点分配学习速率,降低每棵树的权重,减少每棵树的影响,为后面提供更好的学习空间;
- XGBoost支持特征粒度上的并行。XGBoost已经预先对数据进行了排序。各个特征的增益计算可以并行进行。
- 可并行的近似直方图算法。
XGBoost的缺点主要有以下两个方面:
- XGBoost需要保存各个特征预先排序的结果,占用内存。
- XGBoost采用level-wise生成决策树,同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合,但很多叶子节点的分裂增益较低,没必要进行跟进一步的分裂,这就带来了不必要的开销;
3 实现
下面以xgboost中的scikit-learning接口中的XGBClassifier和XGBRegresso为例,介绍其主要参数及其用法(所有参数的介绍,可以查询参考资料3):
- n_estimators: 基学习器数量。
- booster: 指定基模型。常用的有‘gblinear’、‘gbtree’、‘dart’。其中,‘gblinear’为线性模型。
- eta: 或者为learning_rate,学习率参数。其作用即为上文提到的衰减。
- gamma:或者为min_split_loss。只有损失函数的减少值超过该参数指定值时,才会进行下一步的分裂。
- max_depth:树的深度。
- subsample:训练样本抽样比例。主要是为了防止过拟合。
- sampling_method: 训练样本抽样方法。
- colsample_bytree, colsample_bylevel, colsample_bynode:控制列抽样的三个参数。colsample_bytree指定构造每一棵树时的列抽样比例;colsample_bylevel指定树的每一层的列抽样比例;colsample_bynode指定每一次分裂时的列抽样比例。
- lambda: L 2 L2 L2正则化系数。
- alpha: L 1 L1 L1正则化系数。
- tree_method: XGBoost中的树生成算法。
- scale_pos_weight:指定正负样本比例。
- missing: 指定空值。
- objective: 目标类型。常用的参数如下:
- eval_metrics: 评估指标。
import pandas as pd
import numpy as np
from xgboost import XGBRegressor
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
boston=load_boston()
X=pd.DataFrame(boston.data,columns=boston.feature_names)
y=pd.Series(boston.target)
x_train,x_test,y_train,y_test=train_test_split(X,y,test_size=0.2,
random_state=0)
xgbr=XGBRegressor(booster='gbtree',n_estimators=100,max_depth=3)
xgbr.fit(x_train, y_train,
eval_metric='rmse',
verbose=1)
y_pred=xgbr.predict(x_test)
score=np.sqrt(mean_squared_error(y_test, y_pred))
print("RMSE:{:.3f}".format(score))
#获取树结构
xgbr.get_booster().dump_model('a.txt')
参考资料