CART是一种基于二叉树的机器学习算法,其既能处理回归问题,又能处理分类为题。相比于 ID3 和 C4.5 只能用于离散型数据且只能用于分类任务,CART 决策树的适用面要广得多,既可用于离散型数据,又可以处理连续型数据,并且分类和回归任务都能处理。
在分类任务中 CART 算法使用基尼系数作为特征选择的依据,在回归任务中则以均方误差作为特征选择的依据。
信息增益准测对可取值数目较多的特征有所偏好, 增益率准则使用大量的对数计算会非常耗费技术资源。于是引进了基尼系数。基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,特征越好。
基尼系数定义:在分类问题中,假设有K个类别,第k个类别的概率为 P k P_k Pk, 则基尼系数的表达式为:
G i n i ( p ) = ∑ k = 1 K P k ∗ ( 1 − P k ) Gini(p)=\sum_{k=1}^K P_k*(1-P_k) Gini(p)=k=1∑KPk∗(1−Pk)
对于个给定的样本D,假设有K个类别, 第k个类别的数量为 C k C_k Ck,则样本D的基尼系数表达式为:
G i n i ( D ) = 1 − ∑ k = 1 K ( ∣ C K ∣ ∣ D ∣ ) 2 Gini(D)=1-\sum_{k=1}^K(\frac{|C_K|}{|D|})^2 Gini(D)=1−k=1∑K(∣D∣∣CK∣)2
直观的说,Gini(D)反映了从数据集D中随机抽取两个样本,其类别标记不一致的概率。因此,Gini(D)越小,则数据集D的纯度越高。
特别的,对于样本D,如果根据特征A的某个值a,把D分成D1和D2两部分,则在特征A的条件下,D的基尼系数表达式为(特征A的基尼系数):
G i n i ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini(D,A)= \frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2) Gini(D,A)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)
于是,我们在候选特征集合 Ω \Omega Ω中,选择那个使得划分后基尼系数最小的特征作为最优划分特征,即 A = a r g m i n A ∈ Ω G i n i ( D , A ) A=argmin_{A\in\Omega}Gini(D,A) A=argminA∈ΩGini(D,A)。
对于CART分类树连续值的处理问题,其思想和C4.5是相同的,都是将连续的特征离散化。唯一的区别在于在选择划分点时的度量方式不同,C4.5使用的是信息增益比,而CART分类树使用的是基尼系数。
具体的思路如下,比如m个样本的连续特征A有m个,从小到大排列为a1,a2,…,am,则CART算法取相邻两样本值的平均数,一共取得m-1个划分点,其中第i个划分点 T i T_i Ti表示为: T i = a i + a i + 1 2 T_i= \frac{a_i+a_{i+1}}{2} Ti=2ai+ai+1。对于这m-1个点,分别计算以该点作为二元分类点时的基尼系数。选择基尼系数最小的点作为该连续特征的二元离散分类点。如取到的基尼系数最小的点为 a t a_t at,则小于 a t a_t at的值为类别0,大于 a t a_t at 的值为类别1,这样我们就做到了连续特征的离散化。
要注意的是,与ID3或者C4.5处理离散属性不同的是,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程。
对于CART分类树离散值的处理问题,采用的思路是不停的二分离散特征。
回忆下ID3或者C4.5,如果某个特征A被选取建立决策树节点,如果它有A1,A2,A3三种类别,我们会在决策树上建立一个三叉的节点。这样导致决策树是多叉树。但是CART分类树使用的方法不同,他采用的是不停的二分,还是这个例子,CART分类树会考虑把A分成{A1}和{A2,A3}, {A2}和{A1,A3},{A3}和{A1,A2}三种情况,找到基尼系数最小的组合,比如{A2}和{A1,A3},然后建立二叉树节点,一个节点是A2对应的样本,另一个节点是{A1,A3}对应的节点。同时,由于这次没有把特征A的取值完全分开,后面我们还有机会在子节点继续选择到特征A来划分A1和A3。这和ID3或者C4.5不同,在ID3或者C4.5的一棵子树中,离散特征只会参与一次节点的建立。
这里简单介绍一下CART中的回归树。
假设有n个训练样本,损失函数定义为均方误差(注意,这里的损失指的是树中的criterion,类似信息增益,Gini指数;而不是原问题中的loss,原问题既可以是分类也可以是回归)。这n个样本一开始都在根节点,那么此时进入该节点的样本预测值都为该节点的训练均值,所以此时的损失值为:
L = [ ( y 1 − y ˉ ) 2 + . . . + ( y n − y ˉ ) 2 ] n L=\frac{[(y_1-\bar{y})^2+...+(y_n-\bar{y})^2]}{n} L=n[(y1−yˉ)2+...+(yn−yˉ)2]
接下来就是遍历每一个特征,然后在特征中寻找一个划分点,让大于和小于该值的样本分别进入左右两个子节点,使得左右两个节点的损失值之和最小。即:
m i n ( L l e f t + L r i g h t ) = m i n [ ∑ x i ∈ l e f t ( y i − y ˉ l e f t ) 2 + ∑ x i ∈ r i g h r ( y i − y ˉ r i g h t ) 2 ] min(L_{left}+L_{right})=min[\sum_{x_i\in{left}}(y_i- \bar{y} _{left})^2+\sum_{x_i\in{righr}}(y_i-\bar{y}_{right})^2] min(Lleft+Lright)=min[xi∈left∑(yi−yˉleft)2+xi∈righr∑(yi−yˉright)2]
然后按照上述步骤递归下去,直到达到预设的条件为止,比如树的最大深度等。
CART分类树采用叶子节点里概率最大的类别作为当前节点的预测类别。而回归树输出不是类别,它采用的是用最终叶子的均值或者中位数来预测输出结果。
由于决策树算法很容易对训练集过拟合,而导致泛化能力差,为了解决这个问题,我们需要对CART树进行剪枝,来增加决策树的泛化能力。CART采用的办法是后剪枝法,后面的内容主要来分析后剪枝算法。
也就是说,CART树的剪枝算法可以概括为两步,
第一步是从原始决策树生成各种剪枝效果的决策树,
第二步是用交叉验证来检验剪枝后的预测能力,选择泛化预测能力最好的剪枝后的树作为最终的CART树。
那么按照步骤来进行,我们可以分析如下:
对于位于节点t的任意一颗子树 T t T_t Tt,如果没有剪枝,它的损失函数是
C a ( T t ) = C ( T t ) + α ∣ T t ∣ Ca(T _t)=C(T _t )+\alpha∣T _t ∣ Ca(Tt)=C(Tt)+α∣Tt∣
其中 α \alpha α是正则化因子, ∣ T t ∣ ∣T_t∣ ∣Tt∣是子树 T t T_t Tt的结点的个数, C ( T t ) C(T_t) C(Tt)为训练数据的预测误差。
如果将其剪掉,仅仅保留根节点,则损失是
C a ( T t ) = C ( T ) + α Ca(T _t )=C(T)+\alpha Ca(Tt)=C(T)+α
这样分析之后,何时才能确定剪枝呢?
我们可以假设当剪枝前和剪枝后的损失函数相同,即T这个树的结点数更少,可以对 T t T_t Tt这个子树进行剪枝,直接将其变为树T。有了上面的假设,我们可以得到等式如下
C ( T t ) + α ∣ T t ∣ = C ( T ) + α C(T _t )+\alpha|T_t|=C(T)+\alpha C(Tt)+α∣Tt∣=C(T)+α
解得:
α = C ( T ) − C ( T ) ∣ T t ∣ − 1 \alpha=\frac{C(T)-C(T)}{|T_t|-1} α=∣Tt∣−1C(T)−C(T)
那么如何选择出最优的CART分类树呢?我们可以采用交叉验证策略,上面我们计算出了每个子树是否剪枝的阈值 α \alpha α,如果我们把所有的节点是否剪枝的值 α \alpha α都计算出来,然后分别针对不同的 α \alpha α所对应的剪枝后的最优子树做交叉验证。这样就可以选择一个最好的 α \alpha α,有了这个 α \alpha α,我们就可以用对应的最优子树作为最终结果。
参考文章
Boosting是一族可将弱学习器提升为强学习器的算法。这族算法的工作机制类似:先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下一个基学习器;如此重复进行,直至基学习器数目达到事先指定的值M。最终将这M个学习器进行加权结合。
基学习器是CART回归树
提升树算法:
初始化 F 0 ( x ) = 0 F_0(x)=0 F0(x)=0
对 m = 1 , 2 , … , M m=1, 2, …, M m=1,2,…,M
(1)计算残差
r m i = y i − F m − 1 ( x ) , i = 1 , 2 , … , N r_{mi}=y_i-F_{m-1}(x), i=1, 2, …,N rmi=yi−Fm−1(x),i=1,2,…,N
(2) 拟合残差 r m i r_{mi} rmi学习一个回归树,得到 h m ( x ) h_m(x) hm(x)
假设我们前一轮迭代得到的强学习器是 F t − 1 ( x ) F_{t-1}(x) Ft−1(x)
损失函数是 L ( y , F t − 1 ( x ) ) L(y,F_{t-1}(x)) L(y,Ft−1(x))
我们本轮迭代的目标是找到一个弱学习器 h t ( x ) h_t(x) ht(x)
最小化本轮的损失
L ( y , F t ( x ) ) = L ( y , F t − 1 ( x ) + h t ( x ) ) L(y,F_t(x))=L(y,F_{t-1}(x)+h_t(x)) L(y,Ft(x))=L(y,Ft−1(x)+ht(x))
当采用平方损失函数时
L ( y , F t − 1 ( x ) + h t ( x ) ) = ( y − F t − 1 − h t ( x ) ) 2 = ( r − h t ( x ) ) 2 L(y,F_{t-1}(x)+h_t(x))=(y-F_{t-1}-h_t(x))^2=(r-h_t(x))^2 L(y,Ft−1(x)+ht(x))=(y−Ft−1−ht(x))2=(r−ht(x))2
这里 r = y − F t − 1 ( x ) r=y-F_{t-1}(x) r=y−Ft−1(x)是当前模型拟合数据的残差。所以对于提升树来说只需要简单地拟合当前模型的残差。
(3) 更新 F m ( x ) = F m − 1 ( x ) + h m ( x ) F_m(x)=F_{m-1}(x)+h_m(x) Fm(x)=Fm−1(x)+hm(x)
得到回归问题提升树
F M ( x ) = ∑ m = 1 M h m ( x ) F_M(x)=\sum_{m=1}^Mh_m(x) FM(x)=m=1∑Mhm(x)
参考文献
当损失函数是平方损失和指数损失函数时,提升树每一步优化是很简单的,但是对于一般损失函数而言,往往每一步优化起来不那么容易,针对这一问题,Friedman提出了梯度提升算法,这是利用最速下降的近似方法,其关键是利用损失函数的负梯度作为提升树算法中的残差的近似值。
Gradient boosting是一种用于回归和分类问题的机器学习技术,是一种Boosting的方法。
Gradient boosting的主要思想是:迭代产生多个(M个)弱的模型,每一次建立模型是在之前建立模型损失函数的梯度下降方向。然后将每个弱模型的预测结果相加,后面的模型 F m + 1 ( h x ) F_{m+1}(hx) Fm+1(hx)基于前面学习模型的 F m ( x ) F_{m}(x) Fm(x)的效果生成的,关系如下:
F m ( x ) = F m − 1 ( x ) + ρ m h ( x ; α m ) F_{m}(x)=F_{m-1}(x)+\rho_mh (x;\alpha_m) Fm(x)=Fm−1(x)+ρmh(x;αm)
将损失定义为任意函数的boosting就是Gradient Boosting。
算法步骤:
GBDT也是集成学习Boosting家族的成员,但是却和传统的Adaboost有很大的不同。
AdaBoost算法是模型为加法模型,损失函数为指数函数,学习算法为前向分步算法时的分类问题。而GBDT算法是模型为加法模型,学习算法为前向分步算法,基函数为CART树,损失函数为平方损失函数的回归问题,为指数函数的分类问题和为一般损失函数的一般决策问题。在针对基学习器的不足上,AdaBoost算法是通过提升错分数据点的权重来定位模型的不足,而梯度提升算法是通过算梯度来定位模型的不足。
GBDT使用的决策树是CART回归树,无论是处理回归问题还是二分类以及多分类,GBDT使用的决策树通通都是都是CART回归树。为什么不用CART分类树呢?因为GBDT每次迭代要拟合的是梯度值,是连续值所以要用回归树。
GBDT是GB和DT的结合。
算法步骤:
在scikit-learning中,GradientBoostingClassifier对应GBDT的分类算法,GradientBoostingRegressor对应GBDT的回归算法。
具体算法参数情况如下:
GradientBoostingRegressor(loss=’ls’, learning_rate=0.1, n_estimators=100,
subsample=1.0, criterion=’friedman_mse’, min_samples_split=2,
min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_depth=3,
min_impurity_decrease=0.0, min_impurity_split=None, init=None,
random_state=None, max_features=None, alpha=0.9, verbose=0,
max_leaf_nodes=None, warm_start=False, presort=’auto’,
validation_fraction=0.1, n_iter_no_change=None, tol=0.0001)
参数说明:
GBDT几乎可以用于所有回归问题(线性/非线性),相对loigstic regression仅能用于线性回归,GBDT的适用面非常广。亦可用于分类问题。
参考文章
经过前面的学习,我们已经知道,GBDT是一种基于集成思想下的Boosting学习器,并采用梯度提升的方法进行每一轮的迭代最终组建出强学习器,这样的话算法的运行往往要生成一定数量的树才能达到令我们满意的准确率。当数据集大且较为复杂时,运行一次极有可能需要几千次的迭代运算,这将对我们使用算法造成巨大的计算瓶颈。
针对这一问题,华盛顿大学的陈天奇博士开发出了XGBoost(eXtreme Gradient Boosting),它是Gradient Boosting Machine的一个c++实现,并在原有的基础上加以改进,从而极大地提升了模型训练速度和预测精度。可以说,XGBoost是Gradient Boosting的高效实现。
在XGBoost中,目标函数的形式为: O b j ( Θ ) = L ( θ ) + Ω ( Θ ) Obj(\Theta) = L(\theta) + \Omega(\Theta) Obj(Θ)=L(θ)+Ω(Θ)
如果目标函数中的损失函数权重过高,那么模型的预测精度则不尽人意,反之如果正则项的权重过高,所生成的模型则会出现过拟合情况,难以对新的数据集做出有效预测。只有平衡好两者之间的关系,控制好模型复杂度,并在此基础上对参数进行求解,生成的模型才会“简单有效”(这也是机器学习中的偏差方差均衡)。
由于之前已经学习过树的生成及集成方法,这里不再赘述。首先,我们可以把某一次迭代后集成的模型表示为:
y ^ i = ∑ k = 1 K F k ( x i ) , F k ∈ F \hat{y}_i=\sum_{k=1}^KF_k(x_i), F_k\in \mathcal{F} y^i=k=1∑KFk(xi),Fk∈F
y ^ i \hat{y}_i y^i也就是上文中的 F m ( x ) F_m(x) Fm(x)
所对应的目标函数: O b j ( θ ) = L ( y i , y ^ i ) + ∑ k = 1 K ( F k ) Obj(\theta) = L(y_i,\hat{y}_i) + \sum_{k=1}^K(F_k) Obj(θ)=L(yi,y^i)+∑k=1K(Fk)
将这两个公式进行扩展,应用在前t轮的模型迭代中,具体表示为:
y ^ i ( 0 ) = 0 y ^ i ( 1 ) = F 1 ( x i ) = y ^ i ( 0 ) + F 1 ( x i ) y ^ i ( 2 ) = F 1 ( x i ) + F 2 ( x i ) = y ^ i ( 1 ) + F 2 ( x i ) ⋯ y ^ i ( t ) = ∑ k = 1 t F k ( x i ) = y ^ i ( t − 1 ) + F t ( x i ) \begin {aligned} &\hat{y}_i^{(0)}=0\\ &\hat{y}_i^{(1)}=F_1(x_i)=\hat{y}_i^{(0)}+F_1(x_i)\\ &\hat{y}_i^{(2)}=F_1(x_i)+F_2(x_i)=\hat{y}_i^{(1)}+F_2(x_i)\\ &\cdots\\ &\hat{y}_i^{(t)}=\sum_{k=1}^tF_k(x_i)=\hat{y}_i^{(t-1)}+F_t(x_i) \end{aligned} y^i(0)=0y^i(1)=F1(xi)=y^i(0)+F1(xi)y^i(2)=F1(xi)+F2(xi)=y^i(1)+F2(xi)⋯y^i(t)=k=1∑tFk(xi)=y^i(t−1)+Ft(xi)
y i ^ ( t − 1 ) \hat{y_i}^{(t-1)} yi^(t−1)就是前 t − 1 t-1 t−1轮的模型预测, F t ( x i ) F_t(x_i) Ft(xi)为新t轮加入的预测函数。
这里自然就涉及一个问题:如何选择在每一轮中加入的 F ( x i ) F(x_i) F(xi)呢?答案很直接,选取的 f ( x i ) f(x_i) f(xi)必须使得我们的目标函数尽量最大地降低(这里应用到了Boosting的基本思想,即当前的基学习器重点关注以前所有学习器犯错误的那些数据样本,以此来达到提升的效果)。先对目标函数进行改写,表示如下:
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 ) ) + Ω ( F t ) + c o n s t a n t \begin{aligned} Obj^{(t)} & = \sum_{i=1}^nL(y_i, \hat{y}_i^{(t)}) + \sum_{i=1}^t \Omega(F_i) \\ & = \sum_{i=1}^n L(y_i, \hat{y}_i^{(t-1)} + F_t(x_i)) + \Omega(F_t) + constant \end{aligned} Obj(t)=i=1∑nL(yi,y^i(t))+i=1∑tΩ(Fi)=i=1∑nL(yi,y^i(t−1)+Ft(xi))+Ω(Ft)+constant
如果我们考虑使用平方误差作为损失函数,公式可改写为:
O b j ( t ) = ∑ i = 1 n ( y i − ( y ^ i ( t − 1 ) + F t ( x i ) ) ) 2 + ∑ i = 1 t Ω ( F i ) = ∑ 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 \begin{aligned} Obj^{(t)} & = \sum_{i=1}^n (y_i - (\hat{y}_i^{(t-1)} + F_t(x_i)))^2 + \sum_{i=1}^t\Omega(F_i) \\ & = \sum_{i=1}^n [2(\hat{y}_i^{(t-1)} - y_i)F_t(x_i) + F_t(x_i)^2] + \Omega(F_t) + constant \end{aligned} Obj(t)=i=1∑n(yi−(y^i(t−1)+Ft(xi)))2+i=1∑tΩ(Fi)=i=1∑n[2(y^i(t−1)−yi)Ft(xi)+Ft(xi)2]+Ω(Ft)+constant
对于不是平方误差的情况,我们可以采用如下的泰勒展开近似来定义一个近似的目标函数,方便我们进行这一步的计算。
泰勒展开: f ( x + Δ x ) ≃ F ( x ) + F ′ ( x ) Δ x + 1 2 F ′ ′ ( x ) Δ x 2 f(x+\Delta x)\simeq 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
Obj ( 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 o n s t a n t \text{Obj}^{(t)} = \sum_{i=1}^n [L(y_i, \hat{y}_i^{(t-1)}) + g_i F_t(x_i) + \frac{1}{2} h_i F_t^2(x_i)] + \Omega(F_t) + constant Obj(t)=i=1∑n[L(yi,y^i(t−1))+giFt(xi)+21hiFt2(xi)]+Ω(Ft)+constant
其中 g i = ∂ y ^ i ( t − 1 ) L ( y i , y ^ i ( t − 1 ) ) , h i = ∂ y ^ i ( t − 1 ) 2 L ( y i , y ^ i ( t − 1 ) ) g_i= \partial_{\hat{y}_i^{(t-1)}} L(y_i, \hat{y}_i^{(t-1)}),h_i = \partial_{\hat{y}_i^{(t-1)}}^2 L(y_i, \hat{y}_i^{(t-1)}) gi=∂y^i(t−1)L(yi,y^i(t−1)),hi=∂y^i(t−1)2L(yi,y^i(t−1))
如果移除掉常数项,我们会发现这个目标函数有一个非常明显的特点,它只依赖于每个数据点的在误差函数上的一阶导数和二阶导数 ( ∑ i = 1 n [ g i F t ( x i ) + 1 2 h i F t 2 ( x i ) ] + Ω ( F t ) (\sum_{i=1}^n [g_i F_t(x_i) + \frac{1}{2} h_i F_t^2(x_i)] + \Omega(F_t) (∑i=1n[giFt(xi)+21hiFt2(xi)]+Ω(Ft)。
接着来讨论如何定义树的复杂度。我们先对于 F F F的定义做一下细化,把树拆分成结构部分 q q q和叶子权重部分 ω \omega ω。其中结构函数 q q q把输入映射到叶子的索引号上面去,而 ω \omega ω给定了每个索引号对应的叶子分数是什么。
具体公式为: F t ( x ) = ω q ( x ) , ω ∈ R T , q : R d → { 1 , 2 , ⋯ , T } F_t(x) = \omega _{q(x)}, \omega \in R^T, q: R^d\rightarrow \{1,2,\cdots,T\} Ft(x)=ωq(x),ω∈RT,q:Rd→{1,2,⋯,T}
当我们给定上述定义后,那么一棵树的复杂度就为 Ω ( F ) = γ T + 1 2 λ ∑ j = 1 T ω j 2 \Omega(F) = \gamma T + \frac{1}{2}\lambda \sum_{j=1}^T \omega_j^2 Ω(F)=γT+21λ∑j=1Tωj2
这个复杂度包含了一棵树里面节点的个数(左侧),以及每个树叶子节点上面输出分数的 L 2 L_2 L2模平方(右侧)。当然这不是唯一的一种定义方式,不过这一定义方式学习出的树效果一般都比较不错。
简单提及一下 γ \gamma γ和 λ \lambda λ两个系数的作用, γ \gamma γ作为叶子节点的系数,使XGBoost在优化目标函数的同时相当于做了预剪枝; λ \lambda λ作为 L 2 L_2 L2平方模的系数 也是要起到防止过拟合的作用。
接下来就是非常关键的一步,在这种新的定义下,我们可以把目标函数进行如下改写,其中 I I I被定义为每个叶子上面样本集合 I j = { i ∣ q ( x i ) = j } I_j = \{i|q(x_i)=j\} Ij={i∣q(xi)=j}
O b j ( t ) ≈ ∑ i = 1 n [ g i ω q ( x i ) + 1 2 h i ω q ( x i ) 2 ] + γ T + 1 2 λ ∑ j = 1 T ω j 2 = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) ω j + 1 2 ( ∑ i ∈ I j h i + λ ) ω j 2 ] + γ T \begin{aligned} Obj^{(t)} &\approx \sum_{i=1}^n [g_i \omega_{q(x_i)} + \frac{1}{2} h_i \omega_{q(x_i)}^2] + \gamma T + \frac{1}{2}\lambda \sum_{j=1}^T \omega_j^2\\ &= \sum^T_{j=1} [(\sum_{i\in I_j} g_i) \omega_j + \frac{1}{2} (\sum_{i\in I_j} h_i + \lambda) \omega_j^2 ] + \gamma T \end{aligned} Obj(t)≈i=1∑n[giωq(xi)+21hiωq(xi)2]+γT+21λj=1∑Tωj2=j=1∑T[(i∈Ij∑gi)ωj+21(i∈Ij∑hi+λ)ωj2]+γT
分别定义 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,上式简化为
O b j ( t ) = ∑ j = 1 T [ G j ω j + 1 2 ( H j + λ ) ω j 2 ] + γ T {Obj}^{(t)} = \sum^T_{j=1} [G_j\omega_j + \frac{1}{2} (H_j+\lambda) \omega_j^2] +\gamma T Obj(t)=j=1∑T[Gjωj+21(Hj+λ)ωj2]+γT
由此,我们将目标函数转换为一个一元二次方程求最小值的问题(在此式中,变量为 ω j \omega_j ωj,函数本质上是关于 ω j \omega_j ωj的二次函数),略去求解步骤,最终结果如下所示:
ω j ∗ = − G j H j + λ , O b j ∗ = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T \omega _j^\ast = -\frac{G_j}{H_j+\lambda},{Obj}^\ast = -\frac{1}{2} \sum_{j=1}^T \frac{G_j^2}{H_j+\lambda} + \gamma T ωj∗=−Hj+λGj,Obj∗=−21j=1∑THj+λGj2+γT
乍一看目标函数的计算与树的结构函数 q q q没有什么关系,但是如果我们仔细回看目标函数的构成,就会发现其中 G j G_j Gj和 H j H_j Hj的取值都是由第 j j j个树叶上数据样本所决定的。而第 j j j个树叶上所具有的数据样本则是由树结构函数 q q q决定的。也就是说,一旦树的结构 q q q确定,那么相应的目标函数就能够根据上式计算出来。那么树的生成问题也就转换为找到一个最优的树结构 q q q,使得它具有最小的目标函数。
计算求得的 O b j Obj Obj代表了当指定一个树的结构的时候,目标函数上面最多减少多少。所有我们可以把它叫做结构分数(structure score)。
在前面分析的基础上,当寻找到最优的树结构时,我们可以不断地枚举不同树的结构,利用这个打分函数来寻找出一个最优结构的树,加入到我们的模型中,然后再重复这样的操作。不过枚举所有树结构这个操作不太可行,在这里XGBoost采用了常用的贪心法,即每一次尝试去对已有的叶子加入一个分割。对于一个具体的分割方案,我们可以获得的增益可以由如下公式计算得到:
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]−γ
其中 G L 2 H L + λ \frac{G_L^2}{H_L+\lambda} HL+λGL2代表左子树分数, G R 2 H R + λ \frac{G_R^2}{H_R+\lambda} HR+λGR2代表右子树分数, ( G L + G R ) 2 H L + H R + λ \frac{(G_L+G_R)^2}{H_L+H_R+\lambda} HL+HR+λ(GL+GR)2代表不分割时我们可以获得的分数, γ \gamma γ代表加入新叶子节点引入的复杂度代价。
对于每次扩展,我们还是要枚举所有可能的分割方案,那么如何高效地枚举所有的分割呢?假设需要枚举所有 x < a xx<a这样的条件,那么对于某个特定的分割 a a a我们要计算 a a a左边和右边的导数和,
我们可以发现对于所有的 a a a,我们只要做一遍从左到右的扫描就可以枚举出所有分割的梯度与 G L G_L GL和 G R G_R GR。然后用上面的公式计算每个分割方案的分数就可以了。
但需要注意是:引入的分割不一定会使得情况变好,因为在引入分割的同时也引入新叶子的惩罚项。所以通常需要设定一个阈值,如果引入的分割带来的增益小于一个阀值的时候,我们可以剪掉这个分割。此外在XGBoost的具体实践中,通常会设置树的深度来控制树的复杂度,避免单个树过于复杂带来的过拟合问题。
到这里为止,XGBoost的数学推导就简要介绍完毕。
同样是梯度提升,同样是集成学习,那么XGBoost比GBDT要好在哪里呢?
XGBoost的作者把所有的参数分成了三类:
这些参数用来控制XGBoost的宏观功能。
还有两个参数,XGBoost会自动设置,目前你不用管它。接下来咱们一起看booster参数。
尽管有两种booster可供选择,我这里只介绍tree booster,因为它的表现远远胜过linear booster,所以linear booster很少用到。
这个参数用来控制理想的优化目标和每一步结果的度量方法。