GBDT(MART) 迭代決策樹演算法 深入淺出

TangowL發表於2016-02-29

本文轉載自:蘇冉旭的部落格 http://blog.csdn.net/suranxu007/



GBDT(Gradient Boosting Decision Tree) 又叫 MARTMultiple Additive Regression Tree),是一種用於迴歸的迭代決策樹演算法,該演算法由多棵決策樹組成,所有樹的結論累加起來做最終答案。當把目標函式做變換後,該演算法亦可用於分類或排序。它在被提出之初就和SVM一起被認為是泛化能力較強的演算法。近些年更因為被用於搜尋排序的機器學習模型而引起大家關注。

 

1節:GBDT演算法中的DT

2節:GBDT演算法中的GB

3節:GBDT演算法的應用例項

4節:GBDT演算法中的Shrinkage

5節:它可以用於解決哪些問題

6節:它又是怎樣應用於搜尋排序的呢

第7節:從高層明確幾個GBDT概念,主要講GBDT的兩個版本以及GBDT是什麼不是什麼

 

在此先給出我比較推薦的兩篇英文文獻,喜歡英文原版的同學可直接閱讀:

1Boosting Decision Tree入門教程 http://www.schonlau.net/publication/05stata_boosting.pdf

2LambdaMART用於搜尋排序入門教程 http://research.microsoft.com/pubs/132652/MSR-TR-2010-82.pdf

 

GBDT主要由三個概念組成:Gradient Boosting(即GB)Regression Decistion Tree(即DT)Shrinkage (演算法的一個重要演進分枝,目前大部分原始碼都按該版本實現)。搞定這三個概念後就能明白GBDT是如何工作的,要繼續理解它如何用於搜尋排序則需要額外理解RankNet概念,之後便功德圓滿。下文將逐個碎片介紹,最終把整張圖拼出來。

 

一、 DT:迴歸樹 Regression Decision Tree


提起決策樹(DT, Decision Tree) 絕大部分人首先想到的就是C4.5分類決策樹。但如果一開始就把GBDT中的樹想成分類樹,那就是一條歪路走到黑,一路各種坑,最終摔得都要咯血了還是一頭霧水說的就是LZ自己啊有木有。咳嗯,所以說千萬不要以為GBDT是很多棵分類樹。決策樹分為兩大類,迴歸樹和分類樹。前者用於預測實數值,如明天的溫度、使用者的年齡、網頁的相關程度;後者用於分類標籤值,如晴天/陰天//雨、使用者性別、網頁是否是垃圾頁面。這裡要強調的是,前者的結果加減是有意義的,如10+5-3=12歲,後者則無意義,如男++=到底是男是女? GBDT的核心在於累加所有樹的結果作為最終結果,就像前面對年齡的累加(-3是加負3),而分類樹的結果顯然是沒辦法累加的,所以GBDT中的樹都是迴歸樹,不是分類樹,這點對理解GBDT相當重要(儘管GBDT調整後也可用於分類但不代表GBDT的樹是分類樹)。那麼迴歸樹是如何工作的呢?

 

下面我們以對人的性別判別/年齡預測為例來說明,每個instance都是一個我們已知性別/年齡的人,而feature則包括這個人上網的時長、上網的時段、網購所花的金額等。

 

作為對比,先說分類樹,我們知道C4.5分類樹在每次分枝時,是窮舉每一個feature的每一個閾值,找到使得按照feature<=閾值,和feature>閾值分成的兩個分枝的熵最大的feature和閾值(熵最大的概念可理解成儘可能每個分枝的男女比例都遠離1:1),按照該標準分枝得到兩個新節點,用同樣方法繼續分枝直到所有人都被分入性別唯一的葉子節點,或達到預設的終止條件,若最終葉子節點中的性別不唯一,則以多數人的性別作為該葉子節點的性別。

 

迴歸樹總體流程也是類似,不過在每個節點(不一定是葉子節點)都會得一個預測值,以年齡為例,該預測值等於屬於這個節點的所有人年齡的平均值。分枝時窮舉每一個feature的每個閾值找最好的分割點,但衡量最好的標準不再是最大熵,而是最小化均方差--即(每個人的年齡-預測年齡)^2 的總和 / N,或者說是每個人的預測誤差平方和 除以 N。這很好理解,被預測出錯的人數越多,錯的越離譜,均方差就越大,通過最小化均方差能夠找到最靠譜的分枝依據。分枝直到每個葉子節點上人的年齡都唯一(這太難了)或者達到預設的終止條件(如葉子個數上限),若最終葉子節點上人的年齡不唯一,則以該節點上所有人的平均年齡做為該葉子節點的預測年齡。若還不明白可以Google "Regression Tree",或閱讀本文的第一篇論文中Regression Tree部分。

 

二、 GB:梯度迭代 Gradient Boosting


好吧,我起了一個很大的標題,但事實上我並不想多講Gradient Boosting的原理,因為不明白原理並無礙於理解GBDT中的Gradient Boosting。喜歡打破砂鍋問到底的同學可以閱讀這篇英文wikihttp://en.wikipedia.org/wiki/Gradient_boosted_trees#Gradient_tree_boosting

 

Boosting,迭代,即通過迭代多棵樹來共同決策。這怎麼實現呢?難道是每棵樹獨立訓練一遍,比如A這個人,第一棵樹認為是10歲,第二棵樹認為是0歲,第三棵樹認為是20歲,我們就取平均值10歲做最終結論?--當然不是!且不說這是投票方法並不是GBDT,只要訓練集不變,獨立訓練三次的三棵樹必定完全相同,這樣做完全沒有意義。之前說過,GBDT是把所有樹的結論累加起來做最終結論的,所以可以想到每棵樹的結論並不是年齡本身,而是年齡的一個累加量。GBDT的核心就在於,每一棵樹學的是之前所有樹結論和的殘差,這個殘差就是一個加預測值後能得真實值的累加量。比如A的真實年齡是18歲,但第一棵樹的預測年齡是12歲,差了6歲,即殘差為6歲。那麼在第二棵樹裡我們把A的年齡設為6歲去學習,如果第二棵樹真的能把A分到6歲的葉子節點,那累加兩棵樹的結論就是A的真實年齡;如果第二棵樹的結論是5歲,則A仍然存在1歲的殘差,第三棵樹裡A的年齡就變成1歲,繼續學。這就是Gradient BoostingGBDT中的意義,簡單吧。

 

三、 GBDT工作過程例項


還是年齡預測,簡單起見訓練集只有4個人,A,B,C,D,他們的年齡分別是14,16,24,26。其中AB分別是高一和高三學生;C,D分別是應屆畢業生和工作兩年的員工。如果是用一棵傳統的迴歸決策樹來訓練,會得到如下圖1所示結果:


 

現在我們使用GBDT來做這件事,由於資料太少,我們限定葉子節點做多有兩個,即每棵樹都只有一個分枝,並且限定只學兩棵樹。我們會得到如下圖2所示結果:

 

 


在第一棵樹分枝和圖1一樣,由於A,B年齡較為相近,C,D年齡較為相近,他們被分為兩撥,每撥用平均年齡作為預測值。此時計算殘差(殘差的意思就是: A的預測值 + A的殘差 = A的實際值),所以A的殘差就是16-15=1(注意,A的預測值是指前面所有樹累加的和,這裡前面只有一棵樹所以直接是15,如果還有樹則需要都累加起來作為A的預測值)。進而得到A,B,C,D的殘差分別為-1,1-1,1。然後我們拿殘差替代A,B,C,D的原值,到第二棵樹去學習,如果我們的預測值和它們的殘差相等,則只需把第二棵樹的結論累加到第一棵樹上就能得到真實年齡了。這裡的資料顯然是我可以做的,第二棵樹只有兩個值1-1,直接分成兩個節點。此時所有人的殘差都是0,即每個人都得到了真實的預測值。

 

換句話說,現在A,B,C,D的預測值都和真實年齡一致了。Perfect!

A: 14歲高一學生,購物較少,經常問學長問題;預測年齡A = 15 – 1 = 14

B: 16歲高三學生;購物較少,經常被學弟問問題;預測年齡B = 15 + 1 = 16

C: 24歲應屆畢業生;購物較多,經常問師兄問題;預測年齡C = 25 – 1 = 24

D: 26歲工作兩年員工;購物較多,經常被師弟問問題;預測年齡D = 25 + 1 = 26 

 

那麼哪裡體現了Gradient呢?其實回到第一棵樹結束時想一想,無論此時的cost function是什麼,是均方差還是均差,只要它以誤差作為衡量標準,殘差向量(-1, 1, -1, 1)都是它的全域性最優方向,這就是Gradient

 

講到這裡我們已經把GBDT最核心的概念、運算過程講完了!沒錯就是這麼簡單。不過講到這裡很容易發現三個問題:

 

1)既然圖1和圖最終效果相同,為何還需要GBDT呢?

答案是過擬合。過擬合是指為了讓訓練集精度更高,學到了很多”僅在訓練集上成立的規律“,導致換一個資料集當前規律就不適用了。其實只要允許一棵樹的葉子節點足夠多,訓練集總是能訓練到100%準確率的(大不了最後一個葉子上只有一個instance)。在訓練精度和實際精度(或測試精度)之間,後者才是我們想要真正得到的。

我們發現圖1為了達到100%精度使用了3feature(上網時長、時段、網購金額),其中分枝“上網時長>1.1h” 很顯然已經過擬合了,這個資料集上A,B也許恰好A每天上網1.09h, B上網1.05小時,但用上網時間是不是>1.1小時來判斷所有人的年齡很顯然是有悖常識的;

相對來說圖2boosting雖然用了兩棵樹 ,但其實只用了2feature就搞定了,後一個feature是問答比例,顯然圖2的依據更靠譜。(當然,這裡是LZ故意做的資料,所以才能靠譜得如此狗血。實際中靠譜不靠譜總是相對的) Boosting的最大好處在於,每一步的殘差計算其實變相地增大了分錯instance的權重,而已經分對的instance則都趨向於0。這樣後面的樹就能越來越專注那些前面被分錯的instance。就像我們做網際網路,總是先解決60%使用者的需求湊合著,再解決35%使用者的需求,最後才關注那5%人的需求,這樣就能逐漸把產品做好,因為不同型別使用者需求可能完全不同,需要分別獨立分析。如果反過來做,或者剛上來就一定要做到盡善盡美,往往最終會竹籃打水一場空。

 

2Gradient呢?不是“GBDT麼?

 到目前為止,我們的確沒有用到求導的Gradient。在當前版本GBDT描述中,的確沒有用到Gradient,該版本用殘差作為全域性最優的絕對方向,並不需要Gradient求解.

 

 

3)這不是boosting吧?Adaboost可不是這麼定義的。

這是boosting,但不是AdaboostGBDT不是Adaboost Decistion Tree就像提到決策樹大家會想起C4.5,提到boost多數人也會想到AdaboostAdaboost是另一種boost方法,它按分類對錯,分配不同的weight,計算cost function時使用這些weight,從而讓“錯分的樣本權重越來越大,使它們更被重視”。Bootstrap也有類似思想,它在每一步迭代時不改變模型本身,也不計算殘差,而是從Ninstance訓練集中按一定概率重新抽取Ninstance出來(單個instance可以被重複sample),對著這N個新的instance再訓練一輪。由於資料集變了迭代模型訓練結果也不一樣,而一個instance被前面分錯的越厲害,它的概率就被設的越高,這樣就能同樣達到逐步關注被分錯的instance,逐步完善的效果。Adaboost的方法被實踐證明是一種很好的防止過擬合的方法,但至於為什麼則至今沒從理論上被證明。GBDT也可以在使用殘差的同時引入Bootstrap re-samplingGBDT多數實現版本中也增加的這個選項,但是否一定使用則有不同看法。re-sampling一個缺點是它的隨機性,即同樣的資料集合訓練兩遍結果是不一樣的,也就是模型不可穩定復現,這對評估是很大挑戰,比如很難說一個模型變好是因為你選用了更好的feature,還是由於這次sample的隨機因素。


四、Shrinkage


Shrinkage(縮減)的思想認為,每次走一小步逐漸逼近結果的效果,要比每次邁一大步很快逼近結果的方式更容易避免過擬合。即它不完全信任每一個棵殘差樹,它認為每棵樹只學到了真理的一小部分,累加的時候只累加一小部分,通過多學幾棵樹彌補不足。用方程來看更清晰,即

沒用Shrinkage時:(yi表示第i棵樹上y的預測值, y(1~i)表示前i棵樹y的綜合預測值)

y(i+1) = 殘差(y1~yi) 其中: 殘差(y1~yi) =  y真實值 - y(1 ~ i)

y(1 ~ i) = SUM(y1, ..., yi)

Shrinkage不改變第一個方程,只把第二個方程改為: 

y(1 ~ i) = y(1 ~ i-1) + step * yi

 

Shrinkage仍然以殘差作為學習目標,但對於殘差學習出來的結果,只累加一小部分(step*殘差)逐步逼近目標,step一般都比較小,如0.01~0.001(注意該stepgradientstep),導致各個樹的殘差是漸變的而不是陡變的。直覺上這也很好理解,不像直接用殘差一步修復誤差,而是隻修復一點點,其實就是把大步切成了很多小步。本質上,Shrinkage為每棵樹設定了一個weight,累加時要乘以這個weight,但和Gradient並沒有關係。這個weight就是step。就像Adaboost一樣,Shrinkage能減少過擬合發生也是經驗證明的,目前還沒有看到從理論的證明。

 


五、 GBDT的適用範圍


該版本GBDT幾乎可用於所有迴歸問題(線性/非線性),相對logistic regression僅能用於線性迴歸,GBDT的適用面非常廣。亦可用於二分類問題(設定閾值,大於閾值為正例,反之為負例)。

 


六、 搜尋引擎排序應用 RankNet


搜尋排序關注各個doc的順序而不是絕對值,所以需要一個新的cost function,而RankNet基本就是在定義這個cost function,它可以相容不同的演算法(GBDT、神經網路...)。

 

實際的搜尋排序使用的是LambdaMART演算法,必須指出的是由於這裡要使用排序需要的cost functionLambdaMART迭代用的並不是殘差Lambda在這裡充當替代殘差的計算方法,它使用了一種類似Gradient*步長模擬殘差的方法。這裡的MART在求解方法上和之前說的殘差略有不同,其區別描述見這裡


就像所有的機器學習一樣,搜尋排序的學習也需要訓練集,這裡一般是用人工標註實現,即對每一個(query,doc) pair給定一個分值(如1,2,3,4,分值越高表示越相關,越應該排到前面。然而這些絕對的分值本身意義不大,例如你很難說1分和2分文件的相關程度差異是1分和3分文件差距的一半。相關度本身就是一個很主觀的評判,標註人員無法做到這種定量標註,這種標準也無法制定。但標註人員很容易做到的是”AB都不錯,但文件A比文件B更相關,所以A4分,B3分“。RankNet就是基於此制定了一個學習誤差衡量方法,即cost function。具體而言,RankNet對任意兩個文件A,B,通過它們的人工標註分差,用sigmoid函式估計兩者順序和逆序的概率P1。然後同理用機器學習到的分差計算概率P2sigmoid的好處在於它允許機器學習得到的分值是任意實數值,只要它們的分差和標準分的分差一致,P2就趨近於P1)。這時利用P1P2求的兩者的交叉熵,該交叉熵就是cost function。它越低說明機器學得的當前排序越趨近於標註排序。為了體現NDCG的作用(NDCG是搜尋排序業界最常用的評判標準),RankNet還在cost function中乘以了NDCG

 

好,現在我們有了cost function,而且它是和各個文件的當前分值yi相關的,那麼雖然我們不知道它的全域性最優方向,但可以求導求GradientGradient即每個文件得分的一個下降方向組成的N維向量,N為文件個數(應該說是query-doc pair個數)。這裡僅僅是把”求殘差“的邏輯替換為”求梯度“,可以這樣想:梯度方向為每一步最優方向,累加的步數多了,總能走到區域性最優點,若該點恰好為全域性最優點,那和用殘差的效果是一樣的。這時套到之前講的邏輯,GDBT就已經可以上了。那麼最終排序怎麼產生呢?很簡單,每個樣本通過Shrinkage累加都會得到一個最終得分,直接按分數從大到小排序就可以了(因為機器學習產生的是實數域的預測分,極少會出現在人工標註中常見的兩文件分數相等的情況,幾乎不同考慮同分文件的排序方式)

 

另外,如果feature個數太多,每一棵迴歸樹都要耗費大量時間,這時每個分支時可以隨機抽一部分feature來遍歷求最優(ELF原始碼實現方式)。


七、 明確幾個概念


1. GBDT的兩個不同版本(重要)
目前GBDT有兩個不同的描述版本,兩者各有支持者,讀文獻時要注意區分。殘差版本把GBDT說成一個殘差迭代樹,認為每一棵迴歸樹都在學習前N-1棵樹的殘差,之前我寫的GBDT入門教程主要在描述這一版本,ELF開源軟體實現中用的也是這一版本。Gradient版本把GBDT說成一個梯度迭代樹,使用梯度下降法求解,認為每一棵迴歸樹在學習前N-1棵樹的梯度下降值,之前leftnoteasy的部落格中介紹的為此版本,umass的原始碼實現中用的則是這一版本(準確的說是LambdaMART中的MART為這一版本,MART實現則是前一版本)。


對GBDT無基礎的朋友可以先分別看一下前面兩篇博文教程。總的來說兩者相同之處在於,都是迭代迴歸樹,都是累加每顆樹結果作為最終結果(Multiple Additive Regression Tree),每棵樹都在學習前N-1棵樹尚存的不足,從總體流程和輸入輸出上兩者是沒有區別的;兩者的不同主要在於每步迭代時,是否使用Gradient作為求解方法。前者不用Gradient而是用殘差----殘差是全域性最優值,Gradient是區域性最優方向*步長,即前者每一步都在試圖讓結果變成最好,後者則每步試圖讓結果更好一點。


兩者優缺點。看起來前者更科學一點--有絕對最優方向不學,為什麼捨近求遠去估計一個區域性最優方向呢?原因在於靈活性。前者最大問題是,由於它依賴殘差,cost function一般固定為反映殘差的均方差,因此很難處理純迴歸問題之外的問題。而後者求解方法為梯度下降,只要可求導的cost function都可以使用,所以用於排序的LambdaMART就是用的後者。

2. GBDT中的Tree是迴歸樹,不是分類決策樹。
詳見之前我寫的GBDT入門教程

3. GBDT中的Boost是樣本目標的迭代,不是re-sampling的迭代,也不是Adaboost。
Adaboost中的boosting指從樣本中按分類對錯,分配不同的weight,計算cost function時使用這些weight,從而讓“錯分的樣本權重越來越大,直到它們被分對”。Bootstrap也有類似思想,只不過它可以利用不同的weight作為sample概率對訓練樣本集做re-sample,讓錯分的樣本被進一步學習,而分類正確的樣本就不用再學了。但GBDT中的boost完全不同,跟上述邏輯沒有任何關係,GBDT中每步boost的樣本集都是不變的,變的是每個樣本的迴歸目標值。詳見之前我寫的GBDT入門教程。

4. Shrinkage不是Gradient的步長
Shrinkage只是一種大步變小步的逐步求精方法。這點看起來和Gradient目標=Gradient單位方向*步長挺像。
但其實很不同:1)shrinkage的處理物件不一定是Gradient方向,也可以是殘差,可以是任何增量,即目標=任何東西*shrinkage步長。2)shrinkage決定的是最終走出的一步大小,而不是希望走出的一步大小。前者是對於已有的學習結果打折,後者是在決定學習目標時對區域性最優方向上走多遠負責。3)shrinkage設小了只會讓學習更慢,設大了就等於沒設,它適用於所有增量迭代求解問題;而Gradient的步長設小了容易陷入區域性最優點,設大了容易不收斂。它僅用於用梯度下降求解。--這兩者其實沒太大關係。LambdaMART中其實兩者都用了,而外部可配的引數是shrinkage而不是Gradient步長。

5. GBDT中的Gradient不一定必須是Gradient
見第1部分的兩個版本。

相關文章