本打算將GBDT和XGBoost放在一起,但由於涉及內容較多,且兩個都是比較重要的演算法,這裡主要先看GBDT演算法,XGBoost是GBDT演算法的優化和變種,等熟悉GBDT後再去理解XGBoost就會容易的多
GBDT演算法原理
GBDT(Gradient Boosting Decision Tree)演算法
前面說到,提升樹是每次訓練將上一次訓練的殘差作為本次訓練的樣本,找出最優的決策樹的過程,然後將所有模型進行疊加的過程。同樣,GBDT也是一種前向加法演算法模型,不同於提升樹的是,GBDT每次將殘差的負梯度(後邊具體解釋)作為當前一輪的訓練樣本。GBDT中的樹都是迴歸樹,即使GBDT經過處理可以用於分類演算法,每棵樹依然是迴歸樹。那麼為什麼選擇殘差的負梯度作為訓練樣本呢?這是因為在提升樹中計算損失的時候:
當使得L最小時,我們採用梯度下降的方法,求取L的負梯度:
當f(x)為上一輪所得的強分類器,那麼:
因此我們只要沿著殘差值不斷減小的方向優化就可以了。也正因為損失函式使用了平方損失,在進行優化的時候才可以將殘差的值作為新的樹的訓練集。
而在實際中,存在很多損失函式,比如在分類時使用的cros_entropy或者絕對值損失等,當對這些損失損失函式進行求導時,求得的導數將不再是殘差,因此,針對這一個問題,Freidman提出了梯度提升的方法,即用損失函式的負梯度在當前模型的值,作為迴歸問題中提升樹的殘差的近似值,意思也就是用這個負梯度去代替殘差,只有當損失函式是平方損失的時候,GBDT演算法就等同於提升樹(我是這麼理解的)。到這裡,GBDT的演算法就很清晰了,只需要將提升樹中的殘差替換為損失函式的負梯度就可以了,演算法流程如下:
“”“
輸入:訓練資料集{(x1,y1),(x2,y2),...,(xN,yN)},損失函式L,迭代次數M
輸出:迴歸樹
- 初始化弱分類器(不同於提升樹中,因為提升樹中是直接求均值即可,直接進入迭代獲得殘差):
- 對於迭代次數1~M:
- 計算樣本的負梯度:
-
- 然後根據樣本的負梯度的值,訓練出一個新的決策樹,對於每個葉節點區域Rmj,搜尋葉節點區域的值,使損失函式最小:
-
- 然後更新強分類器:
- 迭代完成,獲得最終分類器。
“”“
上面就是GBDT的演算法流程,當損失為平方損失時,該過程與提升樹的過程一致,每個葉節點的取值取殘差的均值即可,如果是其他損失,則需要具體求解,對損失進行求導,令其為0來求取。下面舉個小例子對GBDT的過程進行描述:
有如下一組訓練資料:
編號 | 年齡 | 體重 | 身高 |
1 | 5 | 20 | 1.1 |
2 | 7 | 30 | 1.3 |
3 | 21 | 70 | 1.7 |
4 | 30 | 60 | 1.8 |
特徵為年齡、體重,標籤(輸出)為身高,那麼根據這組資料建立梯度提升樹,樹的個數為5,每棵樹的深度為3,我們依然先採用平方損失去建立每一棵樹:
首先初始化分類器:
對其進行求導,並令其等於0.即可求得c值即為均值:
那麼可得初始化的弱分類器f0(x)=c=1.475。
接下來進入第一次迭代,以損失的負梯度(這裡負梯度就是殘差)作為訓練資料,開始訓練迴歸樹, 訓練資料變為:
編號 | 年齡 | 體重 | 負梯度 |
1 | 5 | 20 | -0.375 |
2 | 7 | 30 | -0.175 |
3 | 21 | 70 | 0.225 |
4 | 30 | 60 | 0.325 |
開始對這個資料訓練決策樹,根據特徵取值,年齡取值分別為5、7、21、30,體重取值為20、30、60、70,分別計算按每個值劃分後葉子節點平方損失和(這裡就只是CART決策樹建立時的平方損失的和,每個節點的輸出就是平均值,與損失函式是什麼無關,我是這麼理解的不知道對不對)最小的,比如當取年齡7時,樹被劃分為:
那麼損失為(0.375-0.375)2+(0.375+0.175)2+(0.375-0.225)2+(0.375-0.325)2=0.140,依次類推,得到如下一張表:
劃分點 | 左節點 | 右節點 | SEL | SER | SE(sum) |
年齡5 | 0 | 1,2,3,4 | 0 | 0.327 | 0.327 |
年齡7 | 1 | 2,3,4 | 0 | 0.14 | 0.14 |
年齡14 | 1,2 | 3,4 | 0.02 | 0.005 | 0.025 |
年齡15.5 | 1,2,3 | 4 | 0.187 | 0 | 0.187 |
體重20 | 0 | 1,2,3,4 | 0 | 0.327 | 0.327 |
體重30 | 1 | 2,3,4 | 0 |
0.14 |
0.14 |
體重60 | 1,2 | 3,4 | 0.02 | 0.005 | 0.025 |
體重70 | 1,2,4 | 3 | 0.26 | 0 | 0.26 |
從上面的損失可以看出,選擇年齡21或體重60是最好的劃分值,因此選取一個特徵進行劃分,這裡選取年齡,得到劃分後的樹為:
這裡設定的樹的最大深度是3,那麼需要再進行一次劃分,按照上面的方法,分別對左右節點進行劃分,最終得到最優的決策樹如下:
這時我們需要獲得這一輪決策樹每個葉子節點輸出值,即最佳的負梯度(這裡是殘差),給每個節點賦予一個引數γ:
由於選取的是平方損失,這個輸出就是每個葉子節點的均值,如果損失函式不是平方損失,那麼就需要對L進行求導,求出對應的γ,本次迭代算是完成,得到的樹如下:
此時更新強學習器,需要用到引數的學習率(後邊會敘述為什麼要用學習率),learning_rate=0.1,用lr表示:
接下來重複上述步驟,進行第二輪迭代,依次得到接下來的四棵樹:
最終得到強學習器:
上述過程可以看出,每棵樹的殘差是在想著不斷減小的方向進行的。此外,上面提到了在進行疊加弱分類器的時候用到了learning_rate,就是利用了Shrinkage(縮減)的思想,其可以認為是梯度下降中的學習率的意思,這樣每次增加一個衰減可以避免一次走很大步導致過擬合,通過衰減每次走一小步逐步逼近結果,即它不完全信任每一個棵殘差樹,它認為每棵樹只學到了真理的一小部分,累加的時候只累加一小部分,通過多學幾棵樹彌補不足。本質上,Shrinkage為每棵樹設定了一個weight,累加時要乘以這個weight,但和Gradient並沒有關係。這個weight就是step。就像Adaboost一樣,Shrinkage能減少過擬合發生也是經驗證明的,目前還沒有看到從理論的證明。
GBDT處理分類問題
那麼為什麼GBDT不採用分類樹,而是採用迴歸樹呢,對於分類問題又是如何處理呢?
首先之所以不採用分類樹,主要是GBDT本質上是通過擬合殘差逐步逼近最優模型的,對於離散資料在疊加就會變得沒有意義,比如貓和狗疊加就不知道是什麼了,那麼對於分類問題,GBDT又是怎麼做的呢,通常處理分類問題有兩種方式:
- 採用指數函式作為損失函式,此時GBDT又退回到AdaBoost的演算法了,因為AdaBoost所採用的實際上就是對數損失
- 另一種就是類似於LogisticRegression的思想,通過預測類別的概率與真實值之間的差值來擬合殘差。
首先看一下GBDT對於二元分類的處理:
邏輯迴歸中的對數似然損失函式(這裡y的取值為{-1,1}損失函式是這樣的形式):
那麼這是負梯度的誤差為:
對於生成決策樹時,對於各葉子節點最佳的負梯度擬合值為:
由於該式子比較難優化,一般採用近似值代替:
除了上面兩個在求解負梯度的值和葉子節點最佳負梯度擬合的線性搜尋中,二元分類的GBDT與GBDT迴歸演算法一致。
GBDT的多分類
對於多分類情況,情況要比二元分類複雜一些,假設類別為K,則此時我們的對數似然損失函式為:
其中如果樣本輸出類別為k,則yk=1。第k類的概率pk(x)的表示式為:
集合上面兩個式子,我們可以計算出第t輪的第i個樣本對應類別l的負梯度誤差為:
從上式可以看出,這裡的負梯度誤差就是樣本所屬類別的真是概率(也就是1)與第t-1輪的預測概率的差值。
然後就是生成決策樹後,線性搜尋對應的葉子結點的負梯度最佳擬合值:
同樣,上式的優化求解比較困難,我們採用近似值代替:
其他步驟與二元分類和GBDT迴歸演算法的步驟一致。
GBDT中的損失函式
這裡我們總結幾種GBDT中的常用的損失函式
關於正則化
關於GBDT的正則化,上面提到了一種方法使用learning_rate的方法,還有一種是不放回抽樣,也就是通過子取樣比例,比例在(0,1]之間,這裡取樣不同於Bagging中取樣,這裡是不放回的抽樣,當取值為1時,相當於不取樣,當小於1時,選擇一部分樣本建立GBDT決策樹,這樣可以減小方差,防止過擬合,但同時會增大偏差,因此不能取值太小,推薦為0.5~0.8。
有關GBDT的演算法就先到這裡了,因為這是機器學習中一個比較重要的方法,其本身其實並不複雜,主要涉及內容較多,其中原理和推導還是比較繁瑣的,這裡卡的時間比較久,後面會再對這一部進行回顧並反覆檢視,每一次對同一個演算法進行回顧都會有不同的見解和問題,到後邊會對這裡的內容進一步補充,接下來通過資料集來對GBDT的Python實現和調參進行學習。