樹算法系列之四:XGBoost

1.CART樹回顧

在正式講Xgboost之前,我們先回顧一下CART樹。
CART樹的生成過程,就是遞歸構建二叉樹的過程,本質上是在某個特徵維度對樣本空間進行劃分。這種空間劃分是一種NP Hard問題,因此一般都是用啓發式的方式去求解,即求某個特徵j的且分點s,使得
minj,s[minc1xiR1(j,s)(yic1)2+minc2xiR2(j,s)(yic2)2]\min_ { j , s } \left[ \min _ { c _ { 1 } } \sum _ { x _ { i } \in R _ { 1 } ( j , s ) } \left( y _ { i } - c _ { 1 } \right) ^ { 2 } + \min _ { c _ { 2 } } \sum _ { x _ { i } \in R _ { 2 } ( j , s ) } \left( y _ { i } - c _ { 2 } \right) ^ { 2 } \right]

2.XGBoost葉子節點取值推導

XGBoost的大體思路跟GBDT一樣,也是通過不斷進行特徵分裂添加樹,來學習一個新的弱學習器,來擬合上次預測結果的殘差。

首先來看一下XGBoost的損失函數
L(ϕ)=il(yi,y^i)+kΩ(fk),Ω(fk)=γT+12λw2L ( \phi ) = \sum _ { i } l \left( y _ { i } , \hat { y } _ { i } \right) + \sum _ { k } \Omega \left( f _ { k } \right) ,\quad 其中 \quad \Omega \left( f _ { k } \right) = \gamma T + \frac { 1 } { 2 } \lambda | | w | | ^ { 2 }

很容易可以看出來
il(yi,y^i)\sum\limits_i l \left( y _ { i } , \hat { y } _ { i } \right)是代表預測值與真實值的偏差,而kΩ(fk)\sum\limits_k \Omega \left( f _ { k } \right)爲正則項。

其中,yiy_i爲樣本的真實值,y^i\hat y_ i爲預測值。fkf_k表示第k棵樹,γ\gamma爲葉子樹的正則項,起到剪枝的作用,T爲樹的葉子節點個數,ww爲葉子節點的分數,λ\lambda爲葉子節點分數的正則項,起到防止過擬合的作用。

前面樹系列的文章提到過很多次,boost系列算法的核心思想是用新生成的樹擬合上次預測的殘差(對於GBDT來說是上次預測的負梯度方向)。生成第t棵樹後,預測值可以寫爲
y^i=y^it1+ft(x)\hat y_i = \hat y_i ^{t-1} + f_t(x)

此時,損失函數可以表示爲

Lt=i=1l(yit,y^it1)+ft(xi)+Ω(ft)L^t = \sum _{i=1} l ( y _i^t , \hat y_i ^{t-1}) + f_t(x_i) + \Omega (f _t)

於是我們要找一個ftf_t讓損失函數最小。對上面的式子進行泰勒展開
L(t)i=1n[l(yi,y^(t1))+gift(xi)+12hift2(xi)]+Ω(ft)\mathcal { L } ^ { ( t ) } \simeq \sum _ { i = 1 } ^ { n } \left[ l \left( y _ { i } , \hat { y } ^ { ( t - 1 ) } \right) + g _ { i } f _ { t } \left( \mathbf { x } _ { i } \right) + \frac { 1 } { 2 } h _ { i } f _ { t } ^ { 2 } \left( \mathbf { x } _ { i } \right) \right] + \Omega \left( f _ { t } \right)

其中,
gi=l(yi,y^it1)y^it1g_i = \frac{\partial l(y_i, \hat y_i ^{t-1})} {\partial \hat y_i ^{t-1}}
hi=2l(yi,y^it1)(y^it1)2h_i = \frac{\partial ^ 2 l(y_i, \hat y_i ^{t-1})} {\partial (\hat y_i ^{t-1})^2}

其中gi,hig_i, h_i的含義可以按如下方式理解
假設目前已經有t-1棵數,這t-1棵樹組成的模型對第i個訓練樣本有一個預測值y^i\hat y_iy^i\hat y_i與真實值yiy_i肯定有差距,這個差距可以用l(yi,y^i)l(y_i, \hat y_i)這個損失函數來衡量,所以此處的gig_ihih_i就是對於該損失函數的一階導和二階導。(參考文獻1)

搞定了前面的損失函數部分,接下來再觀察一下正則項
Ω(ft)=γT+12λj=1Twj2 \Omega (f_t) = \gamma T + \frac{1}{2} \lambda \sum_{j=1}^T w_j^2

對於上面的正則項,我們最簡單的理解方式爲:要使模型儘可能簡單,那就是葉子節點的個數T要小,同時葉子節點上的值也儘可能小。

因爲前t-1棵樹的預測分數與y的殘差對目標函數優化不影響, 可以直接去掉,所以損失函數可以寫成

L(t)i=1n[gift(xi)+12hift2(xi)]+Ω(ft)\mathcal { L } ^ { ( t ) } \simeq \sum _ { i = 1 } ^ { n } \left[ g _ { i } f _ { t } \left( \mathbf { x } _ { i } \right) + \frac {1} {2} h_i f _ { t } ^ { 2 } \left( \mathbf { x } _ { i } \right) \right] + \Omega \left( f _ { t } \right)

上面式子的含義是將每個樣本的損失函數相加,而每個樣本最終都會落到一個葉子節點上,所以我們可以將所有同一個葉子結點的樣本重組起來
L^(t)=i=1n[gift(xi)+12htft2(xi)]+Ω(ft)=i=1n[gift(xi)+12htft2(xi)]+γT+12λj=1Twj2=j=1T[(iIjgi)wj+12(iIjhi+λ)wj2]+γT=12j=1T(Hj+λ)(wj+GjHj+λ)2+γT12j=1TGj2Hj+λ \begin{aligned} \hat{ \mathcal { L }}^{(t)} & =\sum_{i=1}^n [g_if_t(x_i) + \frac12h_tf_t^2(x_i)] + \Omega(f_t) \\ & =\sum_{i=1}^n [g_if_t(x_i) + \frac12h_tf_t^2(x_i)] + \gamma T+\frac{1}{2}\lambda\sum\limits_{j=1}^{T}w_j^2 \\ & = \sum\limits_{j=1}^{T} [(\sum\limits_{i\in I_j}g_i)w_j+\frac{1}{2}(\sum\limits_{i\in I_j}h_i+\lambda) w_j^2]+\gamma T \\ & = \frac{1}{2}\sum\limits_{j=1}^{T} (H_j+\lambda)(w_j + \frac{G_j}{H_j+\lambda})^2+\gamma T -\frac{1}{2}\sum\limits_{j=1}^{T}\frac{G_j^2}{H_j+\lambda} \end{aligned}

其中,Gj=iIjgiG_j = \sum _{i \in I_j} g_i是落入葉子節點i所有樣本一階梯度的總和,而Hj=iIjhiH_j = \sum _{i \in I_j} h_i是落入葉子節點i所有樣本二階梯度的總和。

基於中學數學的原理,我們可以得知:

wj=GjHj+λw_j^* = -\frac{G_j}{H_j+\lambda}
時,損失函數最小。此時最終的損失函數大小爲
L^=12j=1TGj2Hj+λ+γT\hat{\mathcal { L }}^{*} =-\frac{1}{2}\sum\limits_{j=1}^{T}\frac{G_j^2}{H_j+\lambda}+\gamma T

3.與牛頓法的關係

上面我們推導過程中,用到了二階導數。而常見的基於二階導數的優化方法就是牛頓法,下面我們看看與牛頓法的關係。
假設有一個函數f(x)f(x)二階可微,我們對其進行泰勒展開到二階,有
f(x)=f(xk)+f(xk)(xxk)+f(xk)(xxk)2f(x) = f(x_k) + f'(x_k)(x-x_k) + f''(x_k)(x-x_k)^2
上面的式子對x求導,並令導數等於0
f(xk)+f(xk)(xxk)=0f'(x_k) + f''(x_k)(x-x_k) = 0

可以得到x的迭代公式爲
x=xkf(xk)f(xk)x = x_k - \frac{f'(x_k)}{f''(x_k)}
這就是牛頓法的迭代公式。

對比一下葉子節點的取值方式
wj=GjHj+λw_j^* = -\frac{G_j}{H_j+\lambda}
與牛頓法唯一的區別在於,分母上二階導多了一個正則項λ\lambda,形式上完全一致,就是一階導除以二階導,與牛頓法的形式完全一致。

4.XGBoost的樹分裂方式

上面推導了這麼一大串,都是在分析葉子節點怎麼取值。提升樹系列的算法,還有很重要的一點是樹的分裂方式。
首先我們回顧一下其他算法比如GBDT是怎麼進行分裂的。標準的做法是選擇MSE作爲損失函數,採用啓發式的方式來選擇某個特徵j的切分點s進行分裂。這個分裂的過程,並不一定總與損失函數相關。比如在分類樹中,我們一般都會用對數損失函數(交叉熵)來評估模型的最終效果,這個時候樹分裂的結果與損失函數就沒有直接的關係。

XGBoost的樹分裂是直接與損失函數相關的。在分裂的時候,會計算損失函數的增益Gain
Gain=12[GL2HL+λ+GR2HR+λ(GL+GR)2HL+HR+λ]γ 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

上面這個式子其實很容易理解
(GL+GR)2HL+HR+λ\frac{(G_L + G_R)^2}{H_L + H_R+\lambda}是分裂前最終的損失函數值,而
GL2HL+λ,GR2HR+λ\frac{G_L^2}{H_L+\lambda}, \frac{G_R^2}{H_R+\lambda}
分別爲分裂後的左右子樹的損失函數值

那樹進行分裂的時候,自然希望損失函數減少得越多越好。
前面我們推導出了損失函數最終的表達式

L^=12j=1TGj2Hj+λ+γT\hat{\mathcal { L }}^{*} =-\frac{1}{2}\sum\limits_{j=1}^{T}\frac{G_j^2}{H_j+\lambda}+\gamma T

那我們想要的結果就是分列前的損失函數減去分裂後的損失函數,差值最大,注意前面的負號,最後的分裂準則爲
max(GL2HL+λ+GR2HR+λ(GL+GR)2HL+HR+λ)max\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)

5.XGBoost的各種Tricks總結

防止過擬合方法:
1.加入了正則項,對葉子節點數量,葉子節點分數加入了正則懲罰項。
2.加入了學習率,減少了單棵樹影響,給後面樹的優化留了更多空間。
3.列採樣(特徵採樣),與隨機森林類似,不僅來帶來更好的效果,還可以提高運行速度。

缺失值的處理
在別的算法中,一般會使用中位數,均值或者兩者進行融合計算的方式去處理缺失值。但是xgboost能處理缺失值,模型允許缺失值存在。
在尋找分裂點的時候,不對該特徵缺失的樣本進行遍歷,只遍歷該特徵有的樣。具體實現時,將該特徵值缺失的樣本分別分配到左葉子節點與右葉子節點,分別計算增益,然後選擇增益較大的方向進行分裂即可。如果訓練樣本中沒有缺失值,而預測時有缺失,默認將缺失值劃分到右子節點。

分裂點的選擇
並不把所有特徵值間的間隔點作爲候選分裂點,而是用分位數的方法將其作爲分裂的候選集。

並行處理
XGBoost中雖然樹之間是串行關係,但是同層級的節點可以並行。對於某個節點,節點內選擇最佳分裂點與計算分裂點的增益是可以並行的。

基學習器(弱學習器)
傳統的GBDT與CART樹作爲基分類器,XGBoost不僅支持CART樹,還支持線性分類器,這個時候xgboost就相當於帶L1與L2正則項的邏輯斯蒂迴歸(分類問題)或者線性迴歸(迴歸問題)。

6.XGBoost 與GBDT的對比

主要的區別其實就是上面的Tricks。

參考文獻

1.https://www.jianshu.com/p/ac1c12f3fba1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章