XGBoost算法原理小結

在兩年半以前做過梯度提高樹(GBDT)原理小結,可是對GBDT的算法庫XGBoost沒有單獨拿出來分析。雖然XGBoost是GBDT的一種高效實現,可是裏面也加入了不少獨有的思路和方法,值得單獨講一講。所以討論的時候,我會重點分析和GBDT不一樣的地方。html

    本文主要參考了XGBoost的論文和陳天奇的PPT算法

1、從GBDT到XGBoost

    做爲GBDT的高效實現,XGBoost是一個上限特別高的算法,所以在算法競賽中比較受歡迎。簡單來講,對比原算法GBDT,XGBoost主要從下面三個方面作了優化:緩存

    一是算法自己的優化:在算法的弱學習器模型選擇上,對比GBDT只支持決策樹,還能夠直接不少其餘的弱學習器。在算法的損失函數上,除了自己的損失,還加上了正則化部分。在算法的優化方式上,GBDT的損失函數只對偏差部分作負梯度(一階泰勒)展開,而XGBoost損失函數對偏差部分作二階泰勒展開,更加準確。算法自己的優化是咱們後面討論的重點。微信

    二是算法運行效率的優化:對每一個弱學習器,好比決策樹創建的過程作並行選擇,找到合適的子樹分裂特徵和特徵值。在並行選擇以前,先對全部的特徵的值進行排序分組,方便前面說的並行選擇。對分組的特徵,選擇合適的分組大小,使用CPU緩存進行讀取加速。將各個分組保存到多個硬盤以提升IO速度。app

    三是算法健壯性的優化:對於缺失值的特徵,經過枚舉全部缺失值在當前節點是進入左子樹仍是右子樹來決定缺失值的處理方式。算法自己加入了L1和L2正則化項,能夠防止過擬合,泛化能力更強。函數

    在上面三方面的優化中,第一部分算法自己的優化是重點也是難點。如今咱們就來看看算法自己的優化內容。post

2、XGBoost損失函數

    在看XGBoost自己的優化內容前,咱們先回顧下GBDT的迴歸算法迭代的流程,詳細算法流程見梯度提高樹(GBDT)原理小結第三節,對於GBDT的第t顆決策樹,主要是走下面4步:學習

    1)對樣本i=1,2,...m,計算負梯度
\[ r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{t-1}\;\; (x)} \]優化

    2)利用\((x_i,r_{ti})\;\; (i=1,2,..m)\), 擬合一顆CART迴歸樹,獲得第t顆迴歸樹,其對應的葉子節點區域爲\(R_{tj}, j =1,2,..., J\)。其中J爲迴歸樹t的葉子節點的個數。url

    3) 對葉子區域j =1,2,..J,計算最佳擬合值
\[ c_{tj} = \underbrace{arg\; min}_{c}\sum\limits_{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c) \]

    4) 更新強學習器
\[ f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})  \]

    上面第一步是獲得負梯度,或者是泰勒展開式的一階導數。第二步是第一個優化求解,即基於殘差擬合一顆CART迴歸樹,獲得J個葉子節點區域。第三步是第二個優化求解,在第二步優化求解的結果上,對每一個節點區域再作一次線性搜索,獲得每一個葉子節點區域的最優取值。最終獲得當前輪的強學習器。

    從上面能夠看出,咱們要求解這個問題,須要求解當前決策樹最優的全部J個葉子節點區域和每一個葉子節點區域的最優解\(c_{tj}\)。GBDT採樣的方法是分兩步走,先求出最優的全部J個葉子節點區域,再求出每一個葉子節點區域的最優解。

    對於XGBoost,它指望把第2步和第3步合併在一塊兒作,即一次求解出決策樹最優的全部J個葉子節點區域和每一個葉子節點區域的最優解\(c_{tj}\)。在討論如何求解前,咱們先看看XGBoost的損失函數的形式。

    在GBDT損失函數\(L(y, f_{t-1}(x)+ h_t(x))\)的基礎上,咱們加入正則化項以下:
\[ \Omega(h_t) = \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \]

    這裏的\(J\)是葉子節點的個數,而\(w_{tj}\)是第j個葉子節點的最優值。這裏的\(w_{tj}\)和咱們GBDT裏使用的\(c_{tj}\)是一個意思,只是XGBoost的論文裏用的是\(w\)表示葉子區域的值,所以這裏和論文保持一致。

    最終XGBoost的損失函數能夠表達爲:
\[ L_t=\sum\limits_{i=1}^mL(y_i, f_{t-1}(x_i)+ h_t(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2  \]

     最終咱們要極小化上面這個損失函數,獲得第t個決策樹最優的全部J個葉子節點區域和每一個葉子節點區域的最優解\(w_{tj}\)。XGBoost沒有和GBDT同樣去擬合泰勒展開式的一階導數,而是指望直接基於損失函數的二階泰勒展開式來求解。如今咱們來看看這個損失函數的二階泰勒展開式:

\[ \begin{align} L_t & = \sum\limits_{i=1}^mL(y_i, f_{t-1}(x_i)+ h_t(x_i)) + \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & \approx   \sum\limits_{i=1}^m( L(y_i, f_{t-1}(x_i)) + \frac{\partial L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}(x_i)}h_t(x_i) + \frac{1}{2}\frac{\partial^2 L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}^2(x_i)} h_t^2(x_i)) +  \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2  \end{align} \]
    

    爲了方便,咱們把第i個樣本在第t個弱學習器的一階和二階導數分別記爲
\[ g_{ti} = \frac{\partial L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}(x_i)}, \; h_{ti} = \frac{\partial^2 L(y_i, f_{t-1}(x_i) }{\partial f_{t-1}^2(x_i)} \]

    則咱們的損失函數如今能夠表達爲:
\[ L_t \approx \sum\limits_{i=1}^m( L(y_i, f_{t-1}(x_i)) + g_{ti}h_t(x_i) + \frac{1}{2} h_{ti} h_t^2(x_i)) +  \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \]

    損失函數裏面\(L(y_i, f_{t-1}(x_i))\)是常數,對最小化無影響,能夠去掉,同時因爲每一個決策樹的第j個葉子節點的取值最終會是同一個值\(w_{tj}\),所以咱們的損失函數能夠繼續化簡。

\[ \begin{align} L_t & \approx \sum\limits_{i=1}^m g_{ti}h_t(x_i) + \frac{1}{2} h_{ti} h_t^2(x_i)) +  \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2  \\ & = \sum\limits_{j=1}^J (\sum\limits_{x_i \in R_{tj}}g_{ti}w_{tj} +  \frac{1}{2} \sum\limits_{x_i \in R_{tj}}h_{ti} w_{tj}^2) +  \gamma J + \frac{\lambda}{2}\sum\limits_{j=1}^Jw_{tj}^2 \\ & =  \sum\limits_{j=1}^J [(\sum\limits_{x_i \in R_{tj}}g_{ti})w_{tj} + \frac{1}{2}( \sum\limits_{x_i \in R_{tj}}h_{ti}+ \lambda) w_{tj}^2] + \gamma J   \end{align} \]

    咱們把每一個葉子節點區域樣本的一階和二階導數的和單獨表示以下:
\[ G_{tj} = \sum\limits_{x_i \in R_{tj}}g_{ti},\; H_{tj} =  \sum\limits_{x_i \in R_{tj}}h_{ti} \]

    最終損失函數的形式能夠表示爲:
\[ L_t  =  \sum\limits_{j=1}^J [G_{tj}w_{tj} + \frac{1}{2}(H_{tj}+\lambda)w_{tj}^2] + \gamma J    \]

    如今咱們獲得了最終的損失函數,那麼回到前面講到的問題,咱們如何一次求解出決策樹最優的全部J個葉子節點區域和每一個葉子節點區域的最優解\(w_{tj}\)呢?

3、3. XGBoost損失函數的優化求解

    關於如何一次求解出決策樹最優的全部J個葉子節點區域和每一個葉子節點區域的最優解\(w_{tj}\),咱們能夠把它拆分紅2個問題:

    1) 若是咱們已經求出了第t個決策樹的J個最優的葉子節點區域,如何求出每一個葉子節點區域的最優解\(w_{tj}\)

    2) 對當前決策樹作子樹分裂決策時,應該如何選擇哪一個特徵和特徵值進行分裂,使最終咱們的損失函數\(L_t\)最小?

    對於第一個問題,實際上是比較簡單的,咱們直接基於損失函數對\(w_{tj}\)求導並令導數爲0便可。這樣咱們獲得葉子節點區域的最優解\(w_{tj}\)表達式爲:
\[ w_{tj} = - \frac{G_{tj}}{H_{tj} + \lambda} \]

    這個葉子節點的表達式不是XGBoost獨創,實際上在GBDT的分類算法裏,已經在使用了。你們在梯度提高樹(GBDT)原理小結第4.1節中葉子節點區域值的近似解表達式爲:
\[ c_{tj} = \sum\limits_{x_i \in R_{tj}}r_{ti}\bigg /  \sum\limits_{x_i \in R_{tj}}|r_{ti}|(1-|r_{ti}|) \]

    它其實就是使用了上式來計算最終的\(c_{tj}\)。注意到二元分類的損失函數是:
\[ L(y, f(x)) = log(1+ exp(-yf(x))) \]

    其每一個樣本的一階導數爲:
\[ g_i=-r_i= -y_i/(1+exp(y_if(x_i))) \]

    其每一個樣本的二階導數爲:
\[ h_i =\frac{exp(y_if(x_i)}{(1+exp(y_if(x_i))^2} = |g_i|(1-|g_i|) \]

    因爲沒有正則化項,則$c_{tj} = -\frac{g_i}{h_i} $,便可獲得GBDT二分類葉子節點區域的近似值。

    如今咱們回到XGBoost,咱們已經解決了第一個問題。如今來看XGBoost優化拆分出的第二個問題:如何選擇哪一個特徵和特徵值進行分裂,使最終咱們的損失函數\(L_t\)最小?

    在GBDT裏面,咱們是直接擬合的CART迴歸樹,因此樹節點分裂使用的是均方偏差。XGBoost這裏不使用均方偏差,而是使用貪心法,即每次分裂都指望最小化咱們的損失函數的偏差。

    注意到在咱們\(w_{tj}\)取最優解的時候,原損失函數對應的表達式爲:
\[ L_t = -\frac{1}{2}\sum\limits_{j=1}^J\frac{G_{tj}^2}{H_{tj} + \lambda} +\gamma J \]

    若是咱們每次作左右子樹分裂時,能夠最大程度的減小損失函數的損失就最好了。也就是說,假設當前節點左右子樹的一階二階導數和爲\(G_L,H_L,G_R,H_L\), 則咱們指望最大化下式:
\[ -\frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+ \lambda} +\gamma J  -(  -\frac{1}{2}\frac{G_L^2}{H_L + \lambda}  -\frac{1}{2}\frac{G_{tj}^2}{H_{tj+\lambda} + \lambda}+ \gamma (J+1) ) \]
 

    整理下上式後,咱們指望最大化的是:
\[ \max \frac{1}{2}\frac{G_L^2}{H_L + \lambda} + \frac{1}{2}\frac{G_R^2}{H_R+\lambda}  - \frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+ \lambda} - \gamma \]
 

    也就是說,咱們的決策樹分裂標準再也不使用CART迴歸樹的均方偏差,而是上式了。

    具體如何分裂呢?舉個簡單的年齡特徵的例子以下,假設咱們選擇年齡這個 特徵的值a做爲決策樹的分裂標準,則能夠獲得左子樹2我的,右子樹三我的,這樣能夠分別計算出左右子樹的一階和二階導數和,進而求出最終的上式的值。

    而後咱們使用其餘的不是值a的劃分標準,能夠獲得其餘組合的一階和二階導數和,進而求出上式的值。最終咱們找出可使上式最大的組合,以它對應的特徵值來分裂子樹。

    至此,咱們解決了XGBoost的2個優化子問題的求解方法。

4、XGBoost算法主流程

    這裏咱們總結下XGBoost的算法主流程,基於決策樹弱分類器。不涉及運行效率的優化和健壯性優化的內容。

    輸入是訓練集樣本\(I=\{(x_,y_1),(x_2,y_2), ...(x_m,y_m)\}\), 最大迭代次數T, 損失函數L, 正則化係數\(\lambda,\gamma\)

    輸出是強學習器f(x)

    對迭代輪數t=1,2,...T有:

    1) 計算第i個樣本(i-1,2,..m)在當前輪損失函數L基於\(f_{t-1}(x_i)\)的一階導數\(g_{ti}\),二階導數\(h_{ti}\),計算全部樣本的一階導數和\(G_t = \sum\limits_{i=1}^mg_{ti}\),二階導數和\(H_t = \sum\limits_{i=1}^mh_{ti}\)

    2) 基於當前節點嘗試分裂決策樹,默認分數score=0

        對特徵序號 k=1,2...K:

        a) \(G_L=0, H_L=0\)

        b.1) 將樣本按特徵k從小到大排列,依次取出第i個樣本,依次計算當前樣本放入左子樹後,左右子樹一階和二階導數和:
\[ G_L = G_L+ g_{ti}, G_R=G-G_L \]
\[ H_L = H_L+ h_{ti}, H_R=H-H_L \]

        b.2) 嘗試更新最大的分數:
\[ score = max(score, \frac{1}{2}\frac{G_L^2}{H_L + \lambda} + \frac{1}{2}\frac{G_R^2}{H_R+\lambda}  - \frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+ \lambda} ) \]

    3) 基於最大score對應的劃分特徵和特徵值分裂子樹。

    4) 若是最大score爲0,則當前決策樹創建完畢,計算全部葉子區域的\(w_{tj}\), 獲得弱學習器\(h_t(x)\),更新強學習器\(f_t(x)\),進入下一輪弱學習器迭代.若是最大score不是0,則轉到第2)步繼續嘗試分裂決策樹。

5、5.XGBoost算法運行效率的優化

    在第2,3,4節咱們重點討論了XGBoost算法自己的優化,在這裏咱們再來看看XGBoost算法運行效率的優化。

    你們知道,Boosting算法的弱學習器是無法並行迭代的,可是單個弱學習器裏面最耗時的是決策樹的分裂過程,XGBoost針對這個分裂作了比較大的並行優化。對於不一樣的特徵的特徵劃分點,XGBoost分別在不一樣的線程中並行選擇分裂的最大增益。

    同時,對訓練的每一個特徵排序而且以塊的的結構存儲在內存中,方便後面迭代重複使用,減小計算量。計算量的減小參見上面第4節的算法流程,首先默認全部的樣本都在右子樹,而後從小到大迭代,依次放入左子樹,並尋找最優的分裂點。這樣作能夠減小不少沒必要要的比較。

    具體的過程以下圖所示:

 

    此外,經過設置合理的分塊的大小,充分利用了CPU緩存進行讀取加速(cache-aware access)。使得數據讀取的速度更快。另外,經過將分塊進行壓縮(block compressoin)並存儲到硬盤上,而且經過將分塊分區到多個硬盤上實現了更大的IO。

6、6.XGBoost算法健壯性的優化

    最後咱們再來看看XGBoost在算法健壯性的優化,除了上面講到的正則化項提升算法的泛化能力外,XGBoost還對特徵的缺失值作了處理。

    XGBoost沒有假設缺失值必定進入左子樹仍是右子樹,則是嘗試經過枚舉全部缺失值在當前節點是進入左子樹,仍是進入右子樹更優來決定一個處理缺失值默認的方向,這樣處理起來更加的靈活和合理。

    也就是說,上面第4節的算法的步驟a),b.1)和b.2)會執行2次,第一次假設特徵k全部有缺失值的樣本都走左子樹,第二次假設特徵k全部缺失值的樣本都走右子樹。而後每次都是針對沒有缺失值的特徵k的樣本走上述流程,而不是全部的的樣本。

    若是是全部的缺失值走右子樹,使用上面第4節的a),b.1)和b.2)便可。若是是全部的樣本走左子樹,則上面第4節的a)步要變成:
\[ G_R=0, H_R=0 \]

    b.1)步要更新爲:
\[ G_R = G_R+g_{ti}, G_L=G-G_R \]
\[ H_R = H_R+h_{ti}, H_L=H-H_R \]

7、XGBoost小結

    不考慮深度學習,則XGBoost是算法競賽中最熱門的算法,它將GBDT的優化走向了一個極致。固然,後續微軟又出了LightGBM,在內存佔用和運行速度上又作了很多優化,可是從算法自己來講,優化點則並無XGBoost多。

    什麼時候使用XGBoost,什麼時候使用LightGBM呢?我的建議是優先選擇XGBoost,畢竟調優經驗比較多一些,能夠參考的資料也多一些。若是你使用XGBoost遇到的內存佔用或者運行速度問題,那麼嘗試LightGBM是個不錯的選擇。

 

(歡迎轉載,轉載請註明出處。歡迎溝通交流: 微信:nickchen121)

相關文章
相關標籤/搜索