XGBoost簡介:打開樹模型的黑盒子

做者:字節移動技術——許瑤坤html

首先明確一下,XGBoost是一種訓練決策樹模型的學習方式,準確的說是訓練Tree Ensemble Model的方式,這裏把Tree Ensemble Model翻譯成樹集成模型。前端

舉個例子

img

這裏是一個簡單的數據集,橫座標表示藥物的劑量,綠色表示藥物有效,紅色表示藥物無效。算法

首先給出來訓練後獲得的決策樹模型,緩存

img

這裏每一個非葉子節點是一個判斷條件,由於數據集比較簡單,這裏只有一個特徵的判斷,實際使用過程當中會有多個特徵。每一個葉子節點對應有一個權重,注意這裏設定了初始的預測機率是0.5,也就是剛開始咱們什麼都不知道的時候,認定一種劑量下藥物有效的機率是0.5,而後這棵樹給出來的是相對於最開始猜測的一個偏移量,好比當Dosage<5時,給出的葉子節點權重是-0.5,那麼這個時候咱們預測的機率就是0.5+(-0.5)=0,也就是模型輸出這個劑量下藥物沒有用。markdown

多棵樹就是有多個這樣的決策樹,而後預測結果等於全部決策樹輸出的和。app

大概有了這個簡單的例子,這裏來作一下簡單的數學推導。函數

理論部分

樹集成模型

簡單來講,樹集成模型是由多棵決策樹組成的,對於給定輸入,樹集成模型的預測結果等於每個決策樹預測結果的和。oop

這裏給一下原論文的形式化定義。性能

給定一個數據集 D \mathcal{D} ,數據集中有 n n 個訓練數據,每條數據有 m m 個特徵,學習

D = { ( x i , y i ) } ( D = n , x i R m , y i R ) \mathcal{D} = \{(x_{i}, y_{i})\}(|\mathcal{D}|=n, x_{i}\in\mathbb{R}^{m}, y_{i}\in\mathbb{R})

一個集成了 K K 顆決策樹的樹集成模型能夠被表示爲,

y i ^ = ϕ ( x i ) = k = 1 K f k ( x i ) , f k F \hat{y_{i}} = \phi(x_{i})=\sum_{k=1}^{K}f_{k}(x_{i}), f_{k}\in\mathcal{F}

這裏 f k ( ) f_k(\cdot) 表示一個決策樹對於一個輸入的預測結果, F \mathcal{F} 表示全部可能的決策樹。這裏須要注意,一棵決策樹由兩個方面構成,一是樹的形態,就是樹長成什麼樣,有多少分支,深度有多少;二是樹對應葉子節點的權重。在推理的時候,決策樹把測試樣本劃分到對應的葉子節點裏,這個葉子節點的權值就是測試樣本在該決策樹下的得分,也就是這裏的 f k ( x i ) f_{k}(x_{i}) .

如何訓練樹集成模型

訓練模型就是肯定模型的參數,在XGBoost的語境中,就是肯定決策樹的形態,以及決策樹葉子節點的權值。

損失函數表示了模型預測值和真實值的差距,若是損失函數到了一個比較小的位置,這裏就認爲已經訓練好模型了。訓練模型就是調整模型的參數,使得損失函數愈來愈小的過程。

通常來講,樹集成模型的損失函數定義以下,

L ( ϕ ) = i l ( y i ^ , y i ) + k Ω ( f k ) \mathcal{L}(\phi)=\sum_{i}l(\hat{y_{i}},y_{i})+\sum_{k}\Omega(f_{k})
Ω ( f ) = γ T + 1 2 λ ω 2 \Omega(f)=\gamma T+\frac{1}{2}\lambda||\omega||^{2}

其中 L ( ) \mathcal{L}(\cdot) 被稱爲損失函數,能夠看到,損失函數是 ϕ \phi 的一個函數,注意這裏 ϕ \phi 是樹集成模型。第一項 i l ( y i ^ , y i ) \sum_{i}l(\hat{y_{i}},y_{i}) 表示的是模型的預測值和真實值的差距,第二項 k Ω ( f k ) \sum_{k}\Omega(f_{k}) 表示的是全部樹的複雜程度的累加,在 Ω ( f ) \Omega(f) 的展開式中, T T 表示某棵樹中葉子節點的個數, ω \omega 表示某棵樹中全部葉子節點權值的和。

簡單來講,對於給定的輸入 x i x_{i} ,模型會有對應的輸出 ϕ ( x i ) \phi(x_{i}) 。給定一個損失函數,獲得 L ( ϕ ( x i ) ) \mathcal{L}(\phi(x_{i})) ,使用數學中求極小值的方法能夠獲得對應 ϕ \phi 的參數。

能夠想象,上述的解法試圖同時求解全部決策樹的形態以及權重參數,這樣作比較困難。

Gradient Tree Boosting

這裏把Gradient Tree Boosting翻譯成梯度提高樹方法,這是個訓練決策樹的算法。算法的想法比較簡介,既然同時求解全部的參數比較困難,那就一次只求解一棵樹對應的參數。進一步,若是每一次求解了一棵樹的參數,用後面的樹來修正前面樹的偏差。

給出梯度提高樹算法的損失函數,

L ( t ) = i = 1 n l ( y i , y i ^ ( t 1 ) + f t ( x i ) ) + Ω ( f t ) \mathcal{L}^{(t)} = \sum_{i=1}^{n}l(y_{i}, \hat{y_{i}}^{(t-1)}+f_{t}(x_{i}))+\Omega(f_{t})

這裏的損失函數是一個遞推公式,和上一節描述的損失函數差異在於,以前的預測值是 y i ^ \hat{y_{i}} ,目標優化的參數是全部決策樹的參數,而這裏的預測值是 y i ^ ( t 1 ) + f t ( x i ) \hat{y_{i}}^{(t-1)}+f_{t}(x_{i}) ,即前t-1棵樹的預測結果加上第t棵樹的預測結果,目標優化的參數是第t棵樹的參數。

梯度提高樹方法的大概訓練流程能夠總結爲,依次求解每棵樹的參數(主要是肯定這棵樹的結構,以及樹的葉子的權重)。在訓練的時候會預先設定一個閾值,例如樹的最大深度,或者樹再往下分裂以後獲得的損失減小到小於某個閾值,則中止訓練這棵樹,進行下一棵樹的訓練。

XGBoost

明確正常的梯度提高樹以後,XGBoost利用泰勒展開對遞推的損失函數進行了近似,而後求解樹集成模型的參數。

L ( t ) \mathcal{L}^{(t)} 是相對於 f t ( x i ) f_{t}(x_{i}) 的一個函數,在這裏對 L ( t ) \mathcal{L}^{(t)} 進行泰勒展開獲得,

L ( t ) i = 1 n [ l ( y i , y ^ ( t 1 ) ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) \mathcal{L}^{(t)} \simeq{\sum_{i=1}^{n}[l(y_{i},\hat{y}^{(t-1)})+g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})}

這裏 g i = l ( y i , y ^ ( t 1 ) ) y ^ ( t 1 ) g_{i}=\frac{\partial l(y_{i},\hat{y}^{(t-1)})}{\partial \hat{y}^{(t-1)}} h i = 2 l ( y i , y ^ ( t 1 ) ) y ^ ( t 1 ) 2 h_{i}=\frac{\partial ^{2}l(y_{i},\hat{y}^{(t-1)})}{\partial \hat{y}^{(t-1)^{2}}} ,即 l ( y i , y ^ ( t 1 ) l(y_{i},\hat{y}^{(t-1)}) y ^ ( t 1 ) \hat{y}^{(t-1)} 的一階導數和二階導數。利用泰勒展開式,這裏將損失函數轉化成了一個二次函數,並且這裏二次項的係數爲正,能夠很方便的求得函數的最小值。

再次說明一下 f t ( x i ) f_{t}(x_{i}) 的含義是第 i i 個輸入在第 t t 棵樹上的輸出,這裏的求和符號的上限 n n 表示的是訓練數據集總的樣本個數。

到這個地方,咱們目標求解的是 f t ( ) f_t(\cdot) ,在上式中以變量的形式出現,在二次函數中,在對稱軸取到極值。爲了簡單起見,把常數項也就是與 f t ( ) f_t(\cdot) 無關的項刪掉。

L ( t ) i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) \mathcal{L}^{(t)} \simeq{\sum_{i=1}^{n}[g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega(f_{t})}

上文提到過,決定 f t ( ) f_t(\cdot) 的主要是兩個緯度,一是這顆樹的形態,二是樹的葉子節點的權重。這裏用數學表達式來表示就是,

i = 1 n f t ( x i ) = j = 1 T i I j ω j \sum_{i=1}^{n}f_t(x_i) = \sum_{j=1}^{T}\sum_{i\in{I_j}}\omega_{j}

等式左邊的意義很明顯,就是全部樣本在第 t t 棵樹上輸出的和,等式右邊用另外一種方式表達了這個值,第一個求和符號表示全部的葉子節點,第二個求和符號表示被分到每一個葉子節點的樣本集合, { i i I j } \{i|i\in{I_j}\} 表示被分到第 j j 個葉子節點的樣本集合。 ω j \omega_{j} 表明第 j j 個葉子節點的權重。能夠這麼理解,樣本分到哪個葉子節點上表示了樹的結構。

把這個細化的表達式帶入到泰勒展開近似的損失函數中獲得,

L t j = 1 T [ ( i I j g i ) w j + 1 2 ( i I j h i + λ ) w j 2 ] + γ T \mathcal{L}^{t} \simeq{\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

這裏給定樹的結構,咱們就能夠推斷出每一個葉子節點權重的在近似條件下的最優解,這裏給定樹的結構隱含在了 { i i I j } \{i|i\in{I_j}\} 集合中,由於在上式計算的時候須要固定這個集合能進行下一步計算。

最優解記爲,

w j = i I j g i i I j h i + λ w^{\ast}_{j}=-\frac{\sum_{i\in{I_{j}}}g_{i}}{\sum_{i\in{I_{j}}}h_{i}+\lambda}

至此,已經推導獲得了樹參數權重的解。有了這個解,回代到損失函數裏就獲得了最小的損失值。

那麼怎麼肯定樹的結構呢?

One of the key problems in tree learning is to find the best split. In order to do so, a split finding algorithm enumerates over all the possible splits on all the features.

答案是枚舉,在一個節點那裏想要作分裂節點的操做,哪些樣本要分到左邊,哪些節點要分到右邊,XGBoost就把全部的樣本按某個特徵排序,而後切分,以此肯定樹的結構,枚舉下來算出最小損失值,就做爲最優結構。

訓練的例子

把文章開始的圖用表格表示,

Drug Dosage Effectiveness g i g_{i} h i h_{i}
4 0 -0.5 0.75
9 1 0.5 0.25
11 1 0.5 0.25
17 0 -0.5 0.75

首先明確,對於全部的樣本初始預測值是0.5,對應上文公式中的 y ^ ( t 1 ) \hat{y}^{(t-1)} ,負樣本的標籤是0,正樣本的標籤是1,對應上文公式中的 y i y_{i} ,在訓練的最開始,全部的樣本都在葉子節點上。

圖示給出剛開始訓練時葉子節點的狀態,全部訓練樣本都在葉子結點上,葉子結點以其特徵(Drug Dosage)表示:

img

把數據代入到計算 w j w^{\ast}_{j} 的公式中計算出葉子節點的權重,再帶入對應的 L t \mathcal{L}^{t} 公式中求得這時的損失,爲了方便計算把正則向去掉,即 γ = 0 \gamma =0 ,獲得 L t = 0 \mathcal{L}^{t}=0 .

咱們首先嚐試把特徵從劑量小於15開始分割,因而獲得下面這棵樹,

img

此時這棵樹的損失等於左子樹的損失加上右子樹的損失,代入數據獲得 L t = 1.33 \mathcal{L}^{t}=-1.33 ,損失減小了,咱們認定這種節點的分裂方式優於全部的節點都在根結點上。

而後依次枚舉,求Dosage < 10以及Dosage < 5的狀況下損失的減小幅度,取損失減小最多的狀況做爲節點分裂的方式。而後遞歸分裂每個子樹,直到每一個葉子節點都只有一個樣本,或者樹的深度到達了事先設定的限制,而後訓練中止,決策樹訓練完畢。

剩下的樹訓練的方式相同,可是最初的猜想再也不是0.5,而是0.5加上全部以前的樹的預測的和。

Note

  1. 文中公式大部分來自原論文,可是下述公式是筆者加的,但願對公式的推導和理解有所幫助
i = 1 n f t ( x i ) = j = 1 T i I j ω j \sum_{i=1}^{n}f_t(x_i) = \sum_{j=1}^{T}\sum_{i\in{I_j}}\omega_{j}
  1. 文中省略了分類問題損失函數一階導和二階導的求解,有興趣能夠看這裏
  2. 除了利用泰勒展開式的二階展開求極值以外,XGBoost在實現方面有不少算法和硬件層面的優化,列舉以下:
  • 對每一個特徵的分割決策使用並行策略:首先把每一個特徵都排序,由於對特徵在不一樣的位置進行分割是獨立的(例如上文例子中的Dosage<15和Dosage<10這兩個分割點),因此可使用並行的線程進行計算,從而加速訓練的速度。

  • 梯度數據緩存策略:在原始的數據中,樣本不是按照特徵值順序存儲的,或者對於不一樣的特徵值,樣本的存儲不是連續的。那麼在計算損失函數的時候,取對應的一階導數和二階導數時,對內存的訪問就不是連續的。XGBoost在實現時會把須要的梯度數據放到一個額外的內存裏,使用預取和緩存的方式來提升緩存的命中率,從而提高數據IO的速度。

  • 去中心化內存策略:爲了實現去中心化的計算,例若有時候數據量太大沒法在一臺機器上運行,這裏將數據分割成不一樣的塊,而後將全部塊存儲在磁盤上。在計算過程當中,利用一個單獨的線程來預取磁盤中的數據,保證運算和取數據能夠同時發生。

Reference

  1. 原論文
  2. 例子來源

關於字節移動平臺團隊

字節跳動移動平臺團隊(Client Infrastructure)是大前端基礎技術行業領軍者,負責整個字節跳動的中國區大前端基礎設施建設,提高公司全產品線的性能、穩定性和工程效率,支持的產品包括但不限於抖音、今日頭條、西瓜視頻、火山小視頻等,在移動端、Web、Desktop等各終端都有深刻研究。

就是如今!客戶端/前端/服務端/端智能算法/測試開發 面向全球範圍招聘!一塊兒來用技術改變世界,感興趣能夠聯繫郵箱 chenxuwei.cxw@bytedance.com,郵件主題 簡歷-姓名-求職意向-指望城市-電話

相關文章
相關標籤/搜索