xgboost是個准確率很高的集成學習框架,在很多比賽中成績優異。
大多數的集成學習都使用決策樹作為基分類器,主要是因為本身要訓練多個分類器,而決策樹速度很快,總體時間相對較少。
決策樹
在講xgboost之前,先描述一下決策樹,后面要用到這些符號
決策樹是把輸入x映射到一個葉節點中,這個過程我們記為q(x)
葉節點總數記為T,每個葉節點有個標簽(分類)或者預測值(回歸)w,即W=[w1,w2,...wT]
那么決策過程就是 f(x)=W[q(x)],記為wq(x)
決策樹的復雜度
決策樹很容易過擬合,過擬合是因為樹太深,模型過於復雜,限制過擬合主要是避免樹太深,可以限制葉節點的個數不能太多,也可以限制葉節點中樣本數不能太少,當然還有很多方法,
這里我們提出一個概念叫樹的復雜度,可以用葉節點的個數T和葉節點的標簽w來衡量,標簽w可以理解為節點中樣本較多,取平均會比較平滑,類似於 batch,
當然也可以用其他方式來衡量
xgboost通俗理解
本文以xgboost回歸為例進行講解
單個決策樹很難保障准確率,假設單個決策樹預測為y’,真實值為y,於是產生了一個誤差y-y',
xgboost針對這個誤差又建立了一棵決策樹,分析誤差產生的原因,從而彌補這個誤差,新的決策樹又會產生一個誤差,那么繼續建立一棵決策樹,如此迭代下去,這就是xgboost的大致過程。
這個過程好比我們寫代碼,先大致寫個框架,運行一下,看看哪不對,改一下,再運行一下,看看哪不對,再改一下,如此迭代,直至完全正確。
注意我們寫代碼時很少一下從頭寫到尾,因為這樣很不方便調試,如果錯誤太多,還不如重新寫,
對應到決策樹就是樹太深,過擬合,可能需要重新訓練,所以xgboost每一棵樹不能太深,這個例子不太合適,只是幫助理解
假設決策樹的預測為y',真實值為y,我們把誤差記為 l(y, y'),為了約束決策樹的復雜度,xgboost加上了正則項,我們用 Ω(f) 來表示,f代表決策樹
xgboost需要建立多棵決策樹,假設y'初始值為0,即瞎猜,沒有任何預測時為0,那么整個過程如下
i表示樣本,t表示第幾棵樹,yt表示前t棵樹的預測值,ft(x)表示新建的第t棵樹的預測值,
最終的預測值就是
k表示決策樹的個數。
建樹原則
那么問題來了,如何建樹?
普通的決策樹是利用信息增益、信息增益率或者基尼系數來建樹,那么xgboost建樹的指標是什么呢?
樹的損失函數
之前說決策樹有個損失函數
n表示樣本數
根據經驗,我們需要使得損失函數最小,這就是建樹的原則。
那么怎么最小化損失函數呢?正常我們都會有個線性變換、激活函數,這里是一棵樹,怎么辦呢? 可能一時半會真想不到,先把公式變換一下吧
這個 constant 就是前t-1棵樹的復雜度,Ω (ft)是第t棵樹的復雜度。
假設l損失為均方差,上式變為
這里重新回憶一下,xgboost是一棵樹一棵樹的建立,也就是說在建立第t棵樹時,第t-1棵樹的預測值是已知的,也就是上式中 y, yt-1是已知的,故其運算值是常數,所以上式是這樣的
注意xgboost有個特點就是允許自定義損失函數,那么如果我們定義的損失函數不是均方差,那是不是得重新研究一下算法呢? 是的,確實要重新研究,但是xgboost為了避免這種麻煩,采用了損失函數的二階近似
二階Taylor展開
這里簡單介紹下
泰勒級數:把非線性函數f(x)=0在x0處展開成泰勒級數,注意x0是個已知數
然后取前3項作為近似值。
損失函數 l(y,yt-1+ft(x)) 是關於 yt-1+ ft(x) 的方程,yt-1+ ft(x)對應為泰勒展開中的x,yt-1是常數,對應為泰勒展開中的x0
所以損失函數 l(y,yt-1+ft(x)) 二階近似為
注意這里是對 l(y,yt-1+ft(x)) 的展開。
這里需要理解下gi,hi是什么東西?貌似不是很清楚。
我們以均方差為例來看下
可以看到gi和hi就是損失函數對 預測值 的一階導和二階導,
而且gi和hi可以並行計算,並且是根據上一棵樹來計算的,也就是在新建樹時,他們是已知值。
樹損失函數展開為
去掉常數項
樹模型 正則化 Ω(ft)
前面說到決策樹的復雜度,假如用決策樹的葉節點數T和葉節點的預測值w來對樹進行正則化的,當然你也可以自定義其正則化的方式,合理即可,
T盡量少,w盡量平滑,所以
γ λ 是懲罰系數,需要自己定義,γ 越大,表示數結構越簡單,其對較多葉節點的樹(整棵樹的葉節點)懲罰越大,λ 同理。
這種正則方式被作者證明效果很好。
樹損失函數變形
ft(x)=wq(x)
故樹損失函數變為
這樣的式子真的沒法處理,需要進一步變換
先看這張圖
可以看到T個葉節點中包含了所有樣本,也就是說遍歷葉節點可以獲取所有樣本,且同一個葉節點中樣本的預測值即wq(x)相同,
那么上式可以轉化為
Ij表示第j個葉節點
注意,對於一棵確定的樹,G H 都是確定的,λ也是確定的,所有上式是一個關於w的二次函數,方程的解是 -b/2a,解帶入方程就是二次曲線的最小值
如果能使損失最小,那就是最好的樹。
obj 可以理解為樹的錯誤率,錯誤率越小越好。w就是預測值。
這就對應了信息增益等指標,就是分裂的評價指標,選擇obj最小的屬性進行分裂。
大致如圖所示
這里稍微總結一下:
xgboost在上一棵樹的基礎上,新建一棵樹,這棵樹根據上一棵樹的一階導和二階導確定最優的分裂方式。
建樹
回憶一下傳統的決策樹如何建立,以id3為例,先根據一定的原則逐個按屬性分裂,然后計算分裂前后的信息增益,選擇增益最大的屬性進行分裂。
xgboost貌似也是這個邏輯,因為沒有其他好辦法。
逐個按屬性進行分裂,計算分裂前后的的損失(錯誤率),相減,取損失為正/負的,看誰減誰。
在分的屬性中選擇絕對值最大的,這里理解就好,語言表述會有些繞。
分裂節點
那按屬性怎么分裂?方法幾乎無限多,枚舉肯定不現實。
回憶一下傳統id3決策樹,屬性分裂有離散和連續之分,離散按屬性值划分,連續只能二划分,排序,每兩個值之間取個數(如均值)進行划分,
xgboost的屬性是離散還是連續呢? 理論上也是都可以,不過回歸應該是連續的。
xgboost在分裂時會防止過擬合,所以它盡可能的會減少葉節點的數量,也就是每次只進行2分裂,所以xgboost回歸對應的基決策樹是cart決策樹。
實際分裂也大致等同於cart決策樹,每次分裂都會計算損失
精確搜索法
怎么理解呢?大致思路等同於連續值處理
但是當樣本太大時,這種方法也會很慢,於是作者提出了一種加速方法
近似搜索法
思路大致同精確搜索,只是在確定分裂點時,不是逐個探索,而是在對特征進行排序后,找出幾個區間,作為分裂點
如先觀察特征的分布情況,划分區間,再分裂。
具體作者又提出了一些選擇
全局近似:即在訓練前就確定分裂點,后面每棵樹都用這些分裂點,這樣提出候選分裂點的次數少,而每棵樹探索的分裂點多,因為這種方法往往要多設一些分裂點,不然到后面就分不開了
局部近似:即每次建樹時重新確定分裂點,這種方法每次嘗試的少,對層數較深的樹比較合適
作者做過如下嘗試
桶的個數等於 1 / eps, 可以看出:
- 全局切分點的個數夠多的時候,和Exact greedy算法性能相當。
- 局部切分點個數不需要那么多,因為每一次分裂都重新進行了選擇。
======================== xgboost 進階 ========================
步長
xgboost也可以加入步長,這也是防止過擬合的好方法
yt=yt-1+ηft(x)
η通常取0.1
yt-yt-1是殘差,ηft(x)可以理解為在梯度上的學習,逼近目標值
雙隨機
xgboost還借鑒了隨機森林的雙隨機處理方式,進一步防止過擬合,並加速訓練和預測過程
xgboost實例
上文以回歸為例進行原理描述,這里以分類為例進行實例解析
數據集
15個樣本,2個特征
模型描述:基決策樹深度max_depth為3,共2棵樹num_boost_round=2,學習率eta=0.1,正則參數γ=0,λ=1,某損失函數(這不是重點,因為參考文獻這里做的好像不對,所以我沒有用它的)
可以假設
損失函數一階導 gi=yi,pred-y
損失函數二階導 hi=yi,pred*(1-yi,pred)
建第一棵樹
因為在分裂過程中要計算每個節點的G H,而G H 就是節點中樣本的g h 的和,故先把每個樣本的g h求出來。
g h 是上一棵樹的預測結果,這里是第一棵樹,沒有之前的預測結果,所以需要初始化一個結果,這里是01分類,我們初始化全部為0.5,當然其他也行
注意這里我們假設0.5是經過分類激活函數sigmoid后的值,是個概率值,因為經過sigmoid的值才能直接和真實標簽作差,這點很重要,后面會用到,當然你也可以假設0.5是不經過sigmoid的值,這里理解就好,往后面看會理解的
根據一階導二階導公式計算出所有樣本的g h,如下圖
比如計算ID=1的樣本y=0, g=0.5,0=0.5,h=0.5*(1-0.5)=0.25
下面開始分裂,目標是看看按哪個屬性進行分裂增益最大
建造第一層
遍歷屬性,首先是第一個屬性
第一個屬性值排序 [1,2,3,6,7,8,9,10] 共8個值,
以1為閾值分裂,小於1為一邊,不小於1為另一邊,左邊為空,右邊為全部,gain=0
以2為閾值分裂,小於2為一邊,不小於2為另一邊,左邊為[1,4],右邊為[2,3,5,6,7,8...15],GL=0,HL=0.5,GR=-1.5,HR=3.25,gain=0.05572
其余類似,最終結果如下圖
可以看到最大增益為0.615,對應分裂點為x<10
同樣步驟處理第二個特征,得到結果如下圖
可以看到最大增益為0.2186,對應分裂點x<0
明顯特征1的增益大於特征2的增益,所以按特征1進行分裂。
建造第二層
左子節點樣本為[1,2,3,4,5,6,7,8,9,10,11,12,14,15]
右子節點樣本為[13]
右邊只有1個節點,不可再分,那么該節點的預測值為
即 w=-0.4
左子節點按照第一層的方法划分,得到如下兩個表
按特征2進行分裂,分裂點為x<2
第一棵樹建造完畢
這里解釋下第一層右子節點預測值為-0.04,但是上面計算出來是-0.4呀,這里相當於直接乘上了學習率0.1,后面樹累加的時候就不用乘了
注意這里葉子節點上的值是沒有經過sigmoid的值
建第二棵樹
第二棵樹的構造方法與第一棵樹完全相同,只是第一棵樹的基礎值為初始化設定的,而第二棵樹的基礎值為第一棵樹的預測結果,正是這個原因,在分裂計算增益尋找分裂點時兩棵樹才不相同。
那第一棵樹的預測結果是什么呢?
是前1棵樹的結果相加,即 f0+f1,但是f0是經過sigmoid的值,f1是沒經過sigmoid的值,而我們需要的是沒經過sigmoid的值,
所以需要把f0即初始化的0.5還原成沒經過sigmoid的值,即已知函數和函數值,求x,很簡單,x=0,故第一棵樹的預測值為0+wq(x),然后需要經過sigmoid
*************** 注意重中之重 ***************
每棵樹的初始值是經過sigmoid函數的,因為要與真實標簽作差
每棵樹的輸出值是沒有經過sigmoid函數的,這樣才能與之前的輸出值相加
同理得到了第二棵樹
這樣最后一棵樹的結果即為預測值,當然分類要經過sigmoid函數。
至此模型完畢。
參考資料:
https://www.jianshu.com/p/7467e616f227
https://xgboost.readthedocs.io/en/latest/tutorials/model.html
https://blog.csdn.net/qq_22238533/article/details/79477547 手擼實例
https://www.hrwhisper.me/machine-learning-xgboost/ 此文章還有很多我這里沒講到