1. 程式人生 > >機器學習系列--GBDT演算法總結與原始碼分析

機器學習系列--GBDT演算法總結與原始碼分析

最近在看Kaggle2014年的一個比賽–Display Advertising Challenge。三個臺灣人得了比賽的第一名,他們使用的是FFM演算法(這個後面再做總結),在他們比賽的程式碼中,使用了GBDT演算法進行了特徵的處理。他們沒有使用scikit-learn中封裝好的演算法,而是自己手擼了一個GBDT的實現。下面就GBDT的一些原理和原始碼進行分析總結。

1. GBDT演算法總結

  梯度提升決策樹GBDT(Gradient Boosting Decision Tree)最早由Friedman文章“Greedy Function Approximation: A Gradient Boosting Machine”提出這個概念。GBDT中的樹用的是CART迴歸樹(不是分類樹),GBDT用來做 迴歸預測,調整後也可以用於分類。由於GBDT中的CART樹,在模型訓練的時候,需要逐個訓練樣本進行計算,模型的訓練時間相當之長。因此,這個也決定了GBDT不適合實時的線上訓練,更加適用於離散的場景。
  GBDT的思想使其具有天然優勢可以發現多種有區分性的特徵以及特徵組合。Facebook(Practical Lessons From Predicting Clicks on Ads at Facebook)使用其來自動發現有效的特徵、特徵組合,來作為LR模型中的特徵,以提高 CTR預估(Click-Through Rate Prediction)的準確性。GBDT在萬能的淘寶搜尋及預測業務上也發揮了重要作用。

1.1-Bagging和Boosting

  要想理解清楚GBDT,首先要明白Bagging和Boosting的區別與聯絡。Bagging和Boosting都是將已有的分類或迴歸演算法通過一定方式組合起來,形成一個性能更加強大的分類器,更準確的說這是一種分類演算法的組裝方法。即將弱分類器組裝成強分類器的方法。

Bagging即套袋法,其演算法過程如下:
A)從原始樣本集中抽取訓練集。每輪從原始樣本集中使用Bootstraping的方法抽取n個訓練樣本(在訓練集中,有些樣本可能被多次抽取到,而有些樣本可能一次都沒有被抽中)。共進行k輪抽取,得到k個訓練集。(k個訓練集之間是相互獨立的)
B)每次使用一個訓練集得到一個模型,k個訓練集共得到k個模型。(注:這裡並沒有具體的分類演算法或迴歸方法,我們可以根據具體問題採用不同的分類或迴歸方法,如決策樹、感知器等)
C)對分類問題:將上步得到的k個模型採用投票的方式得到分類結果;對迴歸問題,計算上述模型的均值作為最後的結果。(所有模型的重要性相同)

關於Boosting的兩個核心問題:
A)在每一輪如何改變訓練資料的權值或概率分佈?
通過提高那些在前一輪被弱分類器分錯樣例的權值,減小前一輪分對樣例的權值,來使得分類器對誤分的資料有較好的效果。
B)通過什麼方式來組合弱分類器?
通過加法模型將弱分類器進行線性組合,比如AdaBoost通過加權多數表決的方式,即增大錯誤率小的分類器的權值,同時減小錯誤率較大的分類器的權值。而提升樹通過擬合殘差的方式逐步減小殘差,將每一步生成的模型疊加得到最終模型。
  總的來說,Bagging的訓練樣本在每次訓練的時候,是通過抽樣採取;而Boosting的核心是每次訓練樣本都是一樣的,但是訓練時候的訓練樣本的權重不一樣。
1)Bagging + 決策樹 = 隨機森林
2)AdaBoost + 決策樹 = 提升樹
3)Gradient Boosting + 決策樹 = GBDT

1.2-CART迴歸樹

  在機器學習演算法中,決策樹的種類有很多。最早使用的是ID3演算法,之後又陸續的提出C4.5演算法和CART演算法,這是三個比較常用的決策樹演算法。ID3演算法十分簡單,核心是根據“最大資訊熵增益”原則選擇劃分當前資料集的最好特徵。ID3採用的資訊增益度量存在一個缺點,它一般會優先選擇有較多屬性值的Feature,因為屬性值多的Feature會有相對較大的資訊增益?(資訊增益反映的給定一個條件以後不確定性減少的程度,必然是分得越細的資料集確定性更高,也就是條件熵越小,資訊增益越大).為了避免這個不足C4.5中是用資訊增益比率(gain ratio)來作為選擇分支的準則。CART樹是二叉樹,既可以用於分類,也可以用於迴歸問題,最先由 Breiman 等提出,分類樹的輸出是樣本的類別, 迴歸樹的輸出是一個實數。GBDT中使用的是CART迴歸樹,這裡我們詳細分析一下回歸樹的演算法,其他演算法感興趣的同學可以檢視相關的文獻。

迴歸樹的生成
具體的推導過程可以參考李航博士的統計學習方法。這個演算法應該說是很easy的,稍做說明的就是輸出值選擇該節點樣本點的平均值可以通過求導輕鬆得到相應的結論。由於決策樹很容易產生過擬合的現象,在生成CART樹後,還需要進行剪枝操作,生成一系列的迴歸樹,之後通過交叉驗證,選擇效果相對較好的決策樹。
迴歸樹演算法描述

1.3-GBDT演算法

  有了前面的CART迴歸樹,就可以正式進入GBDT的演算法了。GBDT演算法是通過逐輪的迭代生成一系列的樹,最終的結果是這一系列的樹的加權求和。假設我們前一輪迭代得到的強學習器是Fm1(x),損失函式是L(y,Fm1(x))。我們本輪迭代的目標是找到一個CART迴歸樹模型的弱學習器h(x,am)(其中am是CART樹的引數),使得L(y,Fm(x))=L(y,Fm1(x)+h(x,am))最小,也就是說,本輪迭代找到決策樹,要讓樣本的損失儘量變得更小。
  GBDT的思想可以用一個通俗的例子解釋,假如有個人30歲,我們首先用20歲去擬合,發現損失有10歲,這時我們用6歲去擬合剩下的損失,發現差距還有4歲,第三輪我們用3歲擬合剩下的差距,差距就只有一歲了。如果我們的迭代輪數還沒有完,可以繼續迭代下面,每一輪迭代,擬合的歲數誤差都會減小。
  那麼現在的難點是在GBDT中如何去量我們每一輪的損失啊。大牛Freidman提出了用損失函式的負梯度來擬合本輪損失的近似值,進而擬合一個CART迴歸樹。
我自己的理解(一家之言,僅供參考)是可以從損失函式泰勒展開的角度來理解Freidman大牛的做法。將損失函式進行泰勒展開,得到如下結果:

L(y,Fm(x))=L(y,Fm1(x)+h(x,am))=L(y,Fm1(x))+L(y,Fm1(x))Fm1h(x,am)
要想保證等號左邊的取值小於等號的右邊,即L(y,Fm1(x))Fm1h(x,am)<0恆成立,又因為h(x,am)是將要計算的CART樹,這個是未知,只有梯度是已知的,因此不妨假設要擬合的CART樹就是梯度的負方向,即h(x,am)=L(y,Fm1(x))Fm1這個就可以保證上面的等式恆成立。

GBDT中使用的損失函式是L(y,F)=log(1+exp(2yF),y{1,1},有關損失函式,可以參考新浪微博的趙志勇總結的機器學習中損失函式。GBDT中損失函式的梯度的負方向為:

yi~=rim=[L(yi,F(xi)))F(xi)]F(x)=Fm1(x)=2yi(1+exp(2yiFm1(x)))
利用(xi,rim)(i=1,2,..N),我們可以擬合一顆CART迴歸樹,得到了第m顆迴歸樹。其對應的葉節點區域γjm,j=1,2,...,J。其中J為葉子節點的個數。針對每一個葉子節點裡的樣本,我們求出使損失函式最小,也就是擬合葉子節點最好的的輸出值γjm如下:
γjm=