XGBoost原理


XGBoost是Extreme Gradient Boosting的簡稱,Gradient Boosting是論文"Greedy Function Approximation: A Gradient Boosting Machine"中介紹的梯度提升算法。Boosting Tree樹數據挖掘和機器學習中常用算法之一,其對輸入要求不敏感,效果好,在工業界用的較多。

Traing loss + Regularization

XGBoost用於監督學習問題(分類和回歸)。監督學習的常用目標函數是:
通常目標函數包含兩部分:訓練誤差和正則化

\[obj(θ)=L(θ)+Ω(θ) \]

其中L是損失函數,度量模型預測與真實值的誤差。常用的損失函數:
預測問題的平方損失函數:

\[L(\theta) = \Sigma_{i}{(y_i-\hat{y_i})}^2 \]

logistic 損失:

\[L(\theta) = \Sigma_{i}[y_i ln(1+e^{-\hat{y_i}}) +(1-y_i) ln(1+e^{\hat{y_i}})] \]

\(Ω\)是正則化項,度量模型的復雜度,避免過擬合,常用的正則化有L1 和L2正則化。

Tree模型融合(集成)

Boosting Tree 最基本的部分是回歸樹,也即是CART,下圖就是一個CART的例子,CART會把輸入根據屬性分配到各個也子節點上,而每個葉子節點上面會對應一個分數值。下面的例子是預測一個人是否喜歡電腦游戲。將葉子節點表示為分數之后,可以做很多事情,比如概率預測,排序等等。

一個CART往往過於簡單,而無法有效的進行預測,因此更加高效的是使用多個CART進行融合,使用集成的方法提升預測效率:

假設有兩顆回歸樹,則兩棵樹融合后的預測結果如上圖。
用公式表示為:

\[\hat{y_i}=\Sigma_{k=1}^{K}f_k(x_i),f_k\in\mathscr{F} \]

其中, K 是樹的個數, \(f_k(x_i)\)是第k棵樹對於輸入 \(x_i\) 輸出的得分, $f_k $是相應函數, \(\mathscr{F}\) 是相應函數空間。
則目標函數為:

\[\text{obj}(\theta)=\Sigma_i^nl(y_i,\hat{y_i})+\Sigma_{k=1}^K\Omega(f_k) \]

函數\(l\)描述\(y_i\)\(\hat{y_i}\)之間的距離。
常用的模型融合方法是Random Foreast和Boosting Tree,這兩種方法構造樹的方式不同。Tree Ensemble中,模型的參數是什么呢?其實就是指樹的結構和葉子節點上面分數的預測。如何求參數?定義目標函數,通過優化目標函數進行求解。

Tree Boosting

假設每次迭代生成一棵樹,則訓練目標函數可以寫成:

\[\text{obj}(\theta)^{(t)}=\Sigma_i^nl(y_i,\hat{y_i}^{(t)})+\Sigma_{k=1}^t\Omega(f_k) \]

其中第一部分是訓練誤差,第2部分是每棵樹的復雜度。\(\hat{y_i}^{(t)}\)為第t步迭代的預測值,且有一下迭代關系:
\(\hat{y_i}^{(0)}=0\)
\(\hat{y_i}^{(1)}=f_1(x_i) = \hat{y_i}^{(0)}+f_1(x_i)\)
\(\cdots\)
\(\hat{y_i}^{(t)}=\Sigma_{k=1}^t(f_k(x_i))= \hat{y_i}^{(t-1)}+f_t(x_i)\)

則訓練目標函數可以寫成:

\[obj^{(t)} =\Sigma_{i=1}^nl(y_i,\hat{y_i}^{(t)})+\Sigma_{i=1}^t\Omega(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) \]

對於第t步來說$\Sigma_{i=1}^{(t-1)}\Omega(f_i) $是已知的,故,

\[obj^{(t)} =\Sigma_{i=1}^nl(y_i,\hat{y_i}^{(t-1)}+ f_t(x_i))+\Omega(f_t)+ constant \]

如果\(l\)使用平方損失函數,則有

$obj^{(t)} =\Sigma_{i=1}^n[y_i-(\hat{y_i}^{(t-1)}+ f_t(x_i)]^2+\Omega(f_t)+ constant \( \)=\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\( \)=\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\( \)=\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)+ constant$

其中對於第t步來說,\(\Sigma_{i=1}^n[{(y_i)}^2-2y_i\hat{y_i} + {(\hat{y_i}^{(t-1)})}^2 ]\)也是常數,所以 目標函數優化為:

\[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 \]

其中\((\hat{y_i}^{(t-1)} - y_i)\)一般叫做殘差。

當使用平方損失函數時,擬合殘差的步驟就是上次分享中的Adaboost算法。更加一般地,對於不是平方損失函數(當對於一般損失函數時,可以用一階梯度擬合殘差,對應的就是GBDT方法。

GBDT回顧

提升樹利用加法模型和前向分步算法實現學習的優化過程。當損失函數時平方損失和指數損失函數時,每一步的優化很簡單,如平方損失函數學習殘差回歸樹。
當為一般的損失函數時,GBDT利用最速下降的近似方法,即利用損失函數的負梯度在當前模型的值,作為回歸問題中提升樹算法的殘差的近似值,擬合一個回歸樹。

提升樹利用加法模型和前向分步算法實現學習的優化過程。當損失函數時平方損失和指數損失函數時,每一步的優化很簡單,如平方損失函數學習殘差回歸樹。
當為一般的損失函數時,GBDT利用最速下降的近似方法,即利用損失函數的負梯度在當前模型的值,作為回歸問題中提升樹算法的殘差的近似值,擬合一個回歸樹。

XGBoost

更加一般地,對於一般損失函數,XGBoost會使用泰勒展開的形式進而用到二階導數。

目標函數:$$obj^{(t)} =\Sigma_{i=1}^nl(y_i,\hat{y_i}^{(t-1)}+ f_t(x_i)+\Omega(f_t)+ constant $$
用泰勒展開來近似目標函數:
(在GBDT中,使用梯度,只是使用了一階導數的形式)

\(f_t(x_i)\)相當於)
則有:

其中\(g_i\)\(h_i\)分別為:

移除常量:\(l(y_i,\hat{y_i}^{(t-1)})\)

有了這個更新后的目標函數后,可以看出這個目標函數僅僅依賴一階似然的一階和二階導數。對於Adaboost,通常只能用用平方損失函數和通過擬合殘差的方式來求解。GBDT在ADAboost基礎上做了一部優化,用釋然函數在每一個樹處的梯度來近似殘差。
有了上述通用的形式后,就可以使用任意(可以求1階和2階)形式的損失函數。

樹的復雜度

當目前為止,討論了模型中訓練誤差的部分。下面來探討模型復雜度\(\Omega(f_t)\)的表示方式。
重新定義每棵樹,將樹f拆分成樹結構q和葉子權重部分w。結構函數q把輸入映射到葉子的索引上。而w給定了每個索引號對應葉子的分數。

當給定了如上圖的書定義后,沒顆樹的復雜度可以定義為下面公式,這個公式里定義了樹中葉子節點的個數和每個樹葉子節點的輸出分數的L2正則項。

在這種定義下,我們可以把目標函數改寫為:

其中,I被定義為每個葉子上面樣本的集合:\(I_j= \left( i|q(x_i)=j \right)\)。這個目標函數包含了T個獨立的單變量二次函數。

上述目標函數對\(w_j\)求導並令導數為0,可以求得:

計算舉例:
Obj代表了當我們指定一個樹的結構的時候,我們在目標上面最多減少多少。我們可以把它叫做結構分數(structure score)。你可以認為這個就是類似吉尼系數一樣更加一般的對於樹結構進行打分的函數。下面是一個具體的打分函數計算的例子

所以目標很明確,不斷的枚舉樹結構,然后利用上述打分函數來尋找一個最優結構的樹,加入到我們的模型中,再重復這個操作。枚舉樹結構計算消耗太大,常用的是貪心法,每一次嘗試對已經的葉子加入一個分割,對一個具體的分割方案,我們可以獲得分割后的增益為:

\[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<0,則此節點不應該split成左右兩支。

對於每次擴展,我們還是要枚舉所有可能的分割方案,如何高效地枚舉所有的分割呢?我假設我們要枚舉所有 x<ax<a 這樣的條件,對於某個特定的分割aa我們要計算左邊和右邊的導數和。
實際應用中,先將 \(g_i\) 從小到大排序,然后進行遍歷,看每個結點是否需要split。

我們可以發現對於所有的aa,我們只要做一遍從左到右的掃描就可以枚舉出所有分割的梯度和GL和GR。然后用上面的公式計算每個分割方案的分數就可以了。

其他優化方法

Shrinkage and Column Subsampling

除了在目標函數中添加正則化外,還有兩種防止過擬合的方法:Shrinkage and Column Subsampling
Shrinkage : 在每一步生成boosting樹時添加一個刪減參數:η ,通SGD中的學習率類似,這個參數可以減少單顆樹的作用
Column(Feature) Subsampling:這個技術在Random Foreast構建子樹時使用(RF決策樹的每個節點,隨機在屬性集合中選擇k個屬性子集用做划分屬性)。XGBoost在構建子樹時也引入了相似的優化方法。

SPLIT FINDING ALGORITHM

xgboost相比傳統gbdt有何不同?xgboost為什么快?xgboost如何支持並行?

  • 傳統GBDT以CART作為基分類器,xgboost還支持線性分類器,這個時候xgboost相當於帶L1和L2正則化項的邏輯斯蒂回歸(分類問題)或者線性回歸(回歸問題)。
  • 傳統GBDT在優化時只用到一階導數信息,xgboost則對代價函數進行了二階泰勒展開,同時用到了一階和二階導數。順便提一下,xgboost工具支持自定義代價函數,只要函數可一階和二階求導。
  • xgboost在代價函數里加入了正則項,用於控制模型的復雜度。正則項里包含了樹的葉子節點個數、每個葉子節點上輸出的score的L2模的平方和。從Bias-variance tradeoff角度來講,正則項降低了模型的variance,使學習出來的模型更加簡單,防止過擬合,這也是xgboost優於傳統GBDT的一個特性。
  • Shrinkage(縮減),相當於學習速率(xgboost中的eta)。xgboost在進行完一次迭代后,會將葉子節點的權重乘上該系數,主要是為了削弱每棵樹的影響,讓后面有更大的學習空間。實際應用中,一般把eta設置得小一點,然后迭代次數設置得大一點。(補充:傳統GBDT的實現也有學習速率)
  • 列抽樣(column subsampling)。xgboost借鑒了隨機森林的做法,支持列抽樣,不僅能降低過擬合,還能減少計算,這也是xgboost異於傳統gbdt的一個特性。
  • 對缺失值的處理。對於特征的值有缺失的樣本,xgboost可以自動學習出它的分裂方向。XGBoost對於確實值能預先學習一個默認的分裂方向
  • xgboost工具支持並行。boosting不是一種串行的結構嗎?怎么並行的?注意xgboost的並行不是tree粒度的並行,xgboost也是一次迭代完才能進行下一次迭代的(第t次迭代的代價函數里包含了前面t-1次迭代的預測值)。xgboost的並行是在特征粒度上的。我們知道,決策樹的學習最耗時的一個步驟就是對特征的值進行排序(因為要確定最佳分割點),xgboost在訓練之前,預先對數據進行了排序,然后保存為block結構,后面的迭代中重復地使用這個結構,大大減小計算量。這個block結構也使得並行成為了可能,在進行節點的分裂時,需要計算每個特征的增益,最終選增益最大的那個特征去做分裂,那么各個特征的增益計算就可以開多線程進行。
  • 可並行的近似直方圖算法。樹節點在進行分裂時,我們需要計算每個特征的每個分割點對應的增益,即用貪心法枚舉所有可能的分割點。當數據無法一次載入內存或者在分布式情況下,貪心算法效率就會變得很低,所以xgboost還提出了一種可並行的近似直方圖算法,用於高效地生成候選的分割點

機器學習算法中GBDT和XGBOOST的區別有哪些?

XFBoost實例和調參

接下來:微軟LightGBM

todo

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM