這篇應該是你見過的講xgboost的文章裏最細的。
簡單介紹
首先需要介紹GBDT,它是一種基於boosting增強策略的加法模型,訓練的時候採用前向分佈算法進行貪婪的學習,每次迭代都學習一棵CART樹來擬合之前 t-1 棵樹的預測結果與訓練樣本真實值的殘差。
在覈心思想不變的情況下,XGBoost對GBDT進行了一系列優化,主要是損失函數進行了二階泰勒展開,另外還有目標函數加入正則項、支持並行和默認缺失值處理等,在可擴展性和訓練速度上有了巨大的提升。
模型核心思想
- Boosting提升方法
- 使用二階泰勒展開來近似擬合殘差:相對於GBDT的一階導數,XGBoost採用二階泰勒展開,可以更爲精準的逼近真實的損失函數
- 貪婪方法分裂節點
模型前提設定
訓練數據集D={(xi,yi)}i=1n,其中xi∈Rm,yi∈R。又有實例x。
01 - 單棵決策樹模型表示方法
假設某棵決策樹有T個葉子節點,則單棵決策樹模型可記爲:
f(x)=wq(x)
其中q:Rm→{1,…,T}是由輸入x向葉子節點編號的映射,其本質是樹的分支結構;而w∈RT是葉子權重向量。
例如實例 x 落在了決策樹的第 j 個節點,則其輸出值即爲 wj 的值。而葉子節點向量即爲
w=(w1,⋯,wj,⋯,wT)
02 - 加法模型結構
使用Boosting思想,對於實例x,總模型的預測輸出爲
y^=ϕ(x)=k=1∑Kfk(x)
其中,fk(x) 爲第 k 棵決策樹。
核心生成原理
03 - XGBoost的正則化目標函數及其變量解釋
XGBoost的正則化目標函數由損失函數和正則化項兩部分組成,定義如下:
L=i=1∑nl(yi,y^i)+k=1∑KΩ(fk)
以下逐一解釋各個參數:
-
l表示損失函數。由於模型將使用二階泰勒展開,因此要求損失函數一階和二階可導。常見的損失函數有:
- 用於迴歸的平方損失函數:l(y,y^)=(y−y^)2
- 用於分類的邏輯迴歸損失函數:l(y,y^)=yln(1+e−y^)+(1−y)ln(1+ey^)
-
y^i表示第i個樣本xi的預測值。作爲加法模型,預測得分是每棵樹打分的累加之和:
y^i=k=1∑Kfk(xi),fk∈F
如上式,此模型中共有 k 棵樹,fk 爲第 k 棵樹的函數(模型)。
-
Ω(f)爲單棵數的複雜度。模型將全部 k 棵樹的複雜度進行求和,添加到目標函數中作爲正則化項,以防止模型過擬合:
k=1∑KΩ(fk)
04 - 學習第 t 棵樹
假設在第 t 輪,也是生成第 t 棵樹時,要訓練的樹模型爲 ft ,則在第 t 輪迭代後,實例 x 的預測結果 y^(t) 爲:
y^(t)=k=1∑tfk(x)=y^(t−1)+ft(x)
公式中 y^(t−1) 爲前 t−1 棵樹的預測結果。
注意,在第 t 輪迭代時,前 t−1 棵樹已知,因此 y^(t−1) 也是一個已知常量。
將上式代入模型的基本目標函數,得到第 t 輪的目標函數爲
L(t)=i=1∑nl(yi,y^i(t))+k=1∑tΩ(fk)=i=1∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)+ constant
後半部分同理。因爲在第 t 輪迭代時,前 t−1 棵樹已知,因此前 t−1 棵樹的複雜度都是已知常量,可以記爲 constant:
k=1∑tΩ(fk)=Ω(ft)+k=1∑t−1Ω(fk)=Ω(ft)+ constant
而yi爲已知標籤,因此在第 t 輪的目標函數中,只有唯一的變量,即第 t 棵樹 ft 。
因爲常量不影響目標函數的優化,我們去除常量,得到第 t 輪的目標函數爲
L(t)=i=1∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)
05 - 泰勒公式及目標函數的泰勒展開
泰勒公式是XGBoost模型的核心要點之一,也是核心亮點。相比較於GBDT模型,二階泰勒展開逼近更加有效。
泰勒公式是將一個在x=x0處具有n階導數的函數f(x)利用(x−x0)的n次多項式來逼近函數的方法。
泰勒公式形式衆多,從本文的實用角度出發,採用以下形式的泰勒公式的二階展開形式:
f(x+Δx)≃f(x)+f′(x)Δx+21f′′(x)Δx2
第 t 輪目標函數 L(t) 在 yi^(t−1) 處的二階泰勒展開爲
L(t)=i=1∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)≃i=1∑n[l(yi,yi^(t−1))+∂yi^(t−1)l(yi,yi^(t−1))ft(xi)+21∂yi^(t−1)2l(yi,yi^(t−1))ft2(xi)]+Ω(ft)=i=1∑n[l(yi,yi^(t−1))+gift(xi)+21hift2(xi)]+Ω(ft)
其中 gi=∂yi^(t−1)l(yi,yi^(t−1)),hi=∂yi^(t−1)2l(yi,yi^(t−1))
以下逐步解釋:
-
形式。比較我們選擇的泰勒公式的形式與損失函數的形式
l(y,y^(t−1)+ft(x))
上節已經說過,yi 和 y^(t−1) 爲都爲常數,相當於 x;而第 t 棵樹 ft 不確定,相當於 Δx。
-
偏導。求和中的每一項都對本項的 y^i(t−1) 求偏導,如果使用以下記號來標記損失函數 l 關於 y^i(t−1) 的一階偏導和二階偏導
gi=∂y^(t−1)l(yi,y^(t−1)),hi=∂y^(t−1)2l(yi,y^(t−1))
那麼,將每一項的損失函數在 y^i(t−1) 處泰勒二階展開,則得下式:
l(yi,y^i(t−1)+ft(xi))≃l(yi,y^i(t−1))+gift(xi)+21hift2(xi)
-
常數。l(yi,y^i(t−1))是前 t−1 棵樹對此項帶來的損失,因此是常數; 在這一輪中,因爲gi 和 hi 已經是上一輪的產物,因此也是常數。
如果再次去掉所有常數項,將得到
L(t)≃i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)
其中 gi=∂yi^(t−1)l(yi,yi^(t−1)),hi=∂yi^(t−1)2l(yi,yi^(t−1)) 。
06 - 定義樹的複雜度
我們使用兩個部分來定義一棵樹 ft 的複雜度:
- 葉子節點的數量 T
- 葉子節點權重向量 w 的 L2 範數
即:
Ω(ft)=γT+21λj=1∑Twj2其中 γ 和 λ 都是超參數。
07 - 根據葉子節點重組累加
設屬於第j個葉子節點的所有樣本下標的集合爲
Ij={i∣q(xi)=j}
使用此種表示方法,參考模型前提設定,可將目標函數改換爲按照葉子節點累加的形式
L(t)≃i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)=i=1∑n[giwq(xi)+21hiwq(xi)2]+λ21j=1∑Twj2+γT=j=1∑T⎣⎡⎝⎛i∈Ij∑gi⎠⎞wj+21⎝⎛i∈Ij∑hi+λ⎠⎞wj2⎦⎤+γT=j=1∑T[Gjwj+21(Hj+λ)wj2]+γT
其中
Gj=i∈Ij∑giHj=i∈Ij∑hi
到此,對目標函數的化簡結束。
以下逐行解釋:
- 第二行將正則化項引入,同時,將ft(xi)改寫爲wq(xi),以便於下一行改換爲按照節點累加的形式。
- 第三行將按照樣本累加改換爲按照節點累加。
- 首先,累加的都是下標。原來按照樣本下標累加,改換爲按照葉子節點下標累加。將 ft(xi) 改寫爲 wq(xi) ,也是爲了適應葉子節點下標的累加寫法以改寫成wj。
- 其次,如果按照葉子節點累加,那麼在同一個葉子節點 j 中的樣本的輸出值都是一樣的,也就是其在權重向量 w 中對應的權重值 wj ,因此原來的 wq(xi) 也可直接改寫成 wj,xi 徹底消失。
- 再次,因爲每個樣本的 gi 和 hi 都是不同的,而且外層的累加使用葉子節點使用下標標記 i ,因此這裏需要使用 Ij 來再次疊加。
- 最後,如果中括號中的 wq(xi) 都直接改寫成 wj ,則可將其與樹複雜度的第一項組合起來,得到中括號中的第二項。
- 第四行引入新的記號。Gj 和 Hj 分別是葉子節點 j 中樣本的一階偏導和二階偏導累加之和,都是常量。
- 目標函數中只有權重向量 w 中的各個權重值爲未知量。
08 - 參數學習
將目標函數作爲各個 wj 的二次凸函數來求解 wj∗
wj∗=wjargminL(t)
令 L(t) 對 wj 求偏導,並令其等於零
∂wj∂L(t)=Gj+(Hj+λ)wj=0
解得每個葉子節點的權重值爲
wj∗=−Hj+λGj
代入即得最優化的目標函數值
L(t)∗=−21j=1∑THj+λGj2+γT
也可使用二次函數求頂點座標的方法求得。
核心生長細節
09 - 如何分裂節點
在實際訓練的第 t 輪,模型都使用貪心法進行樹節點的分裂:
-
對樹中的每個葉子結點嘗試進行分裂;
-
每次分裂後,原來的一個葉子結點繼續分裂爲左右兩個子葉子結點,原葉子結點中的樣本集將根據該結點的判斷規則分散到左右兩個葉子結點中;
-
新分裂一個結點後,我們需要檢測這次分裂是否會給損失函數帶來增益,增益的定義如下:
Lsplit=LL+R−(LL+LR)=[−21HL+HR+λ(GL+GR)2+γ]−[−21(HL+λGL2+HR+λGR2)+2γ]=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
如果Lsplit>0,或者大於設定的閾值,即分裂後目標函數值下降了,那麼可以考慮此次分裂的結果。
10 - 如何尋找最佳分裂點
但是一般都是有多個候選分割點,尋找最佳分割點的理論最佳步驟如下:
- 遍歷每個結點的每個特徵;
- 對每個特徵,按特徵值大小將特徵值排序;
- 線性掃描,找出每個特徵的最佳分裂特徵值;
- 在所有特徵中找出最好的分裂點,即分裂後增益最大的特徵及特徵值
以上爲全局掃描法,是一種貪心的方法,每次進行分裂嘗試都要遍歷一遍全部候選分割點,但不適用數據量過大的條件,如內存無法一次載入或者分佈式的情況。
基於此,XGBoost提出了一系列加快尋找最佳分裂點的方案:
- 特徵預排序+緩存:XGBoost在訓練之前,預先對每個特徵按照特徵值大小進行排序,然後保存爲block結構,後面的迭代中會重複地使用這個結構,使計算量大大減小。
- 分位點近似法:對每個特徵按照特徵值排序後,採用類似分位點選取的方式,僅僅選出常數個特徵值作爲該特徵的候選分割點,在尋找該特徵的最佳分割點時,從候選分割點中選出最優的一個。
- 並行查找:由於各個特性已預先存儲爲block結構,XGBoost支持利用多個線程並行地計算每個特徵的最佳分割點,這不僅大大提升了結點的分裂速度,也極利於大規模訓練集的適應性擴展。
11 - 如何停止生長
一棵樹不會一直生長下去,除了決策樹基礎的下面是一些常見的限制條件。
- 設定分裂閾值,當新引入的一次分裂所帶來的增益 Lsplit 小於閾值時,放棄當前的分裂。
- 當樹達到最大深度時,停止建樹,因爲樹的深度太深容易出現過擬合,這裏需要設置一個超參數 max_depth
- 當引入一次分裂後,重新計算新生成的左、右兩個葉子結點的樣本權重和。如果存在一個葉子結點的樣本權重低於某一個閾值,也會放棄此次分裂。這涉及到一個超參數:最小樣本權重和,是指如果一個葉子節點包含的樣本數量太少也會放棄分裂,防止樹分的太細,這也是過擬合的一種措施。
外圍技術簡介
12 - XGBoost爲什麼可以並行訓練
XGBoost的並行,並不是說每棵樹可以並行訓練,XGB本質上仍然採用boosting思想,每棵樹訓練前需要等前面的樹訓練完成才能開始訓練。XGBoost的並行,指的是特徵維度的並行:在訓練之前,每個特徵按特徵值對樣本進行預排序,並存儲爲Block結構,在後面查找特徵分割點時可以重複使用,而且特徵已經被存儲爲一個個block結構,那麼在尋找每個特徵的最佳分割點時,可以利用多線程對每個block並行計算。
13 - XGBoost爲什麼快
- 分塊並行:訓練前每個特徵按特徵值進行排序並存儲爲Block結構,後面查找特徵分割點時重複使用,並且支持並行查找每個特徵的分割點
- 候選分位點:每個特徵採用常數個分位點作爲候選分割點
- CPU cache 命中優化: 使用緩存預取的方法,對每個線程分配一個連續的buffer,讀取每個block中樣本的梯度信息並存入連續的Buffer中。
- Block 處理優化:Block預先放入內存;Block按列進行解壓縮;將Block劃分到不同硬盤來提高吞吐
14 - XGBoost防止過擬合的方法
- 目標函數添加正則項:葉子節點個數+葉子節點權重的L2正則化
- 列抽樣:訓練的時候只用一部分特徵(不考慮剩餘的block塊即可)
- 子採樣:每輪計算可以不使用全部樣本,使算法更加保守
- shrinkage: 可以叫學習率或步長,爲了給後面的訓練留出更多的學習空間
15 - XGBoost如何處理缺失值
- 在特徵k上尋找最佳 split point 時,不會對該列特徵 missing 的樣本進行遍歷,而只對該列特徵值爲 non-missing 的樣本上對應的特徵值進行遍歷,通過這個技巧來減少了爲稀疏離散特徵尋找 split point 的時間開銷。
- 在邏輯實現上,爲了保證完備性,會將該特徵值missing的樣本分別分配到左葉子結點和右葉子結點,兩種情形都計算一遍後,選擇分裂後增益最大的那個方向(左分支或是右分支),作爲預測時特徵值缺失樣本的默認分支方向。
- 如果在訓練中沒有缺失值而在預測中出現缺失,那麼會自動將缺失值的劃分方向放到右子結點。
XGBoost如何處理不平衡數據
參考:
https://mp.weixin.qq.com/s/wLE9yb7MtE208IVLFlZNkw