通俗理解kaggle比賽大殺器xgboost

v_JULY_v發表於2018-08-04

                             通俗理解kaggle比賽大殺器xgboost

 

說明:若出現部分圖片無法正常顯示而影響閱讀,請以此處的文章為準:xgboost 題庫版
時間:二零一九年三月二十五日。

 

0 前言

xgboost一直在競賽江湖裡被傳為神器,比如時不時某個kaggle/天池比賽中,某人用xgboost於千軍萬馬中斬獲冠軍。

而我們的機器學習課程裡也必講xgboost,如寒所說:“RF和GBDT是工業界大愛的模型,Xgboost 是大殺器包裹,Kaggle各種Top排行榜曾一度呈現Xgboost一統江湖的局面,另外某次滴滴比賽第一名的改進也少不了Xgboost的功勞”。

此外,公司七月線上從2016年上半年起,就開始組織學員參加各種比賽,以在實際競賽專案中成長(畢竟,搞AI不可能沒實戰,而參加比賽歷經資料處理、特徵選擇、模型調優、程式碼調參,是一個極好的真刀真槍的實戰機會,對能力的提升和找/換工作的幫助都非常大)。

AI大潮之下,今年特別多從傳統IT轉行轉崗轉型AI的朋友,很多朋友都諮詢如何轉行AI,我一般都會著重強調學習AI或找/換AI的四大金剛課程 + 題庫 + OJ + kaggle/天池。包括集訓營的畢業考核更會融合kaggle或天池比賽。

考慮到kaggle/天池比賽對搞數學科學的重要性,特寫此文介紹xgboost,助力大家快速入門xgboost以及在比賽中獲得優異成績。

最後,xgboost不是我July發明的,但我會確保本文對它的介紹是最通俗易懂的(且本文得到七月線上AI lab負責人陳博士審校)。另,感謝文末所列的全部參考文獻,有何問題,歡迎隨時留言評論,thanks。


1 決策樹

舉個例子,集訓營某一期有100多名學員,假定給你一個任務,要你統計男生女生各多少人,當一個一個學員依次上臺站到你面前時,你會怎麼區分誰是男誰是女呢?

很快,你考慮到男生的頭髮一般很短,女生的頭髮一般比較長,所以你通過頭髮的長短將這個班的所有學員分為兩撥,長髮的為“女”,短髮為“男”。

相當於你依靠一個指標“頭髮長短”將整個班的人進行了劃分,於是形成了一個簡單的決策樹,而劃分的依據是頭髮長短。 
這時,有的人可能有不同意見了:為什麼要用“頭髮長短”劃分呀,我可不可以用“穿的鞋子是否是高跟鞋”,“有沒有喉結”等等這些來劃分呢,答案當然是可以的。

但究竟根據哪個指標劃分更好呢?很直接的判斷是哪個分類效果更好則優先用哪個。所以,這時就需要一個評價標準來量化分類效果了。 

怎麼判斷“頭髮長短”或者“是否有喉結”是最好的劃分方式,效果怎麼量化呢?直觀上來說,如果根據某個標準分類人群后,純度越高效果越好,比如說你分為兩群,“女”那一群都是女的,“男”那一群全是男的,那這個效果是最好的。但有時實際的分類情況不是那麼理想,所以只能說越接近這種情況,我們則認為效果越好。

量化分類效果的方式有很多,比如資訊增益(ID3)、資訊增益率(C4.5)、基尼係數(CART)等等。

資訊增益的度量標準:熵

ID3演算法的核心思想就是以資訊增益度量屬性選擇,選擇分裂後資訊增益最大的屬性進行分裂。

什麼是資訊增益呢?為了精確地定義資訊增益,我們先定義資訊理論中廣泛使用的一個度量標準,稱為(entropy),它刻畫了任意樣例集的純度(purity)。給定包含關於某個目標概念的正反樣例的樣例集S,那麼S相對這個布林型分類的熵為:

上述公式中,p+代表正樣例,比如在本文開頭第二個例子中p+則意味著去打羽毛球,而p-則代表反樣例,不去打球(在有關熵的所有計算中我們定義0log0為0)。

舉例來說,假設S是一個關於布林概念的有14個樣例的集合,它包括9個正例和5個反例(我們採用記號[9+,5-]來概括這樣的資料樣例),那麼S相對於這個布林樣例的熵為:

Entropy([9+,5-])=-(9/14)log2(9/14)-(5/14)log2(5/14)=0.940。

So,根據上述這個公式,我們可以得到:

  • 如果S的所有成員屬於同一類,則Entropy(S)=0;
  • 如果S的正反樣例數量相等,則Entropy(S)=1;
  • 如果S的正反樣例數量不等,則熵介於0,1之間

如下圖所示:

看到沒,通過Entropy的值,你就能評估當前分類樹的分類效果好壞了。

更多細節如剪枝、過擬合、優缺點、可以參考此文《決策樹學習》。

所以,現在決策樹的靈魂已經有了,即依靠某種指標進行樹的分裂達到分類/迴歸的目的,總是希望純度越高越好。

 

2.迴歸樹與整合學習

如果用一句話定義xgboost,很簡單:Xgboost就是由很多CART樹整合。但,什麼是CART樹?

資料探勘或機器學習中使用的決策樹有兩種主要型別:

  1. 分類樹分析是指預測結果是資料所屬的類(比如某個電影去看還是不看)
  2. 迴歸樹分析是指預測結果可以被認為是實數(例如房屋的價格,或患者在醫院中的逗留時間)

而術語分類迴歸樹(CART,Classification And Regression Tree)分析是用於指代上述兩種樹的總稱,由Breiman等人首先提出。

2.1 迴歸樹
事實上,分類與迴歸是兩個很接近的問題,分類的目標是根據已知樣本的某些特徵,判斷一個新的樣本屬於哪種已知的樣本類,它的結果是離散值。而回歸的結果是連續的值。當然,本質是一樣的,都是特徵(feature)到結果/標籤(label)之間的對映。

理清了什麼是分類和迴歸之後,理解分類樹和迴歸樹就不難了。

分類樹的樣本輸出(即響應值)是類的形式,比如判斷這個救命藥是真的還是假的,週末去看電影《風語咒》還是不去。而回歸樹的樣本輸出是數值的形式,比如給某人發放房屋貸款的數額就是具體的數值,可以是0到300萬元之間的任意值。

所以,對於迴歸樹,你沒法再用分類樹那套資訊增益、資訊增益率、基尼係數來判定樹的節點分裂了,你需要採取新的方式評估效果,包括預測誤差(常用的有均方誤差、對數誤差等)。而且節點不再是類別,是數值(預測值),那麼怎麼確定呢?有的是節點內樣本均值,有的是最優化算出來的比如Xgboost。

CART迴歸樹是假設樹為二叉樹,通過不斷將特徵進行分裂。比如當前樹結點是基於第j個特徵值進行分裂的,設該特徵值小於s的樣本劃分為左子樹,大於s的樣本劃分為右子樹。

而CART迴歸樹實質上就是在該特徵維度對樣本空間進行劃分,而這種空間劃分的優化是一種NP難問題,因此,在決策樹模型中是使用啟發式方法解決。典型CART迴歸樹產生的目標函式為:

因此,當我們為了求解最優的切分特徵j和最優的切分點s,就轉化為求解這麼一個目標函式:

所以我們只要遍歷所有特徵的的所有切分點,就能找到最優的切分特徵和切分點。最終得到一棵迴歸樹。

2.2 boosting整合學習

所謂整合學習,是指構建多個分類器(弱分類器)對資料集進行預測,然後用某種策略將多個分類器預測的結果整合起來,作為最終預測結果。通俗比喻就是“三個臭皮匠賽過諸葛亮”,或一個公司董事會上的各董事投票決策,它要求每個弱分類器具備一定的“準確性”,分類器之間具備“差異性”。

整合學習根據各個弱分類器之間有無依賴關係,分為Boosting和Bagging兩大流派:

  1. Boosting流派,各分類器之間有依賴關係,必須序列,比如Adaboost、GBDT(Gradient Boosting Decision Tree)、Xgboost
  2. Bagging流派,各分類器之間沒有依賴關係,可各自並行,比如隨機森林(Random Forest)

而著名的Adaboost作為boosting流派中最具代表性的一種方法,本部落格曾詳細介紹它。

AdaBoost,是英文"Adaptive Boosting"(自適應增強)的縮寫,由Yoav Freund和Robert Schapire在1995年提出。它的自適應在於:前一個基本分類器分錯的樣本會得到加強,加權後的全體樣本再次被用來訓練下一個基本分類器。同時,在每一輪中加入一個新的弱分類器,直到達到某個預定的足夠小的錯誤率或達到預先指定的最大迭代次數。

    具體說來,整個Adaboost 迭代演算法就3步:

  1. 初始化訓練資料的權值分佈。如果有N個樣本,則每一個訓練樣本最開始時都被賦予相同的權值:1/N。
  2. 訓練弱分類器。具體訓練過程中,如果某個樣本點已經被準確地分類,那麼在構造下一個訓練集中,它的權值就被降低;相反,如果某個樣本點沒有被準確地分類,那麼它的權值就得到提高。然後,權值更新過的樣本集被用於訓練下一個分類器,整個訓練過程如此迭代地進行下去。
  3. 將各個訓練得到的弱分類器組合成強分類器。各個弱分類器的訓練過程結束後,加大分類誤差率小的弱分類器的權重,使其在最終的分類函式中起著較大的決定作用,而降低分類誤差率大的弱分類器的權重,使其在最終的分類函式中起著較小的決定作用。換言之,誤差率低的弱分類器在最終分類器中佔的權重較大,否則較小。

而另一種boosting方法GBDT(Gradient Boost Decision Tree),則與AdaBoost不同,GBDT每一次的計算是都為了減少上一次的殘差,進而在殘差減少(負梯度)的方向上建立一個新的模型。

boosting整合學習由多個相關聯的決策樹聯合決策,什麼叫相關聯?舉個例子

  1. 有一個樣本[資料->標籤]是:[(2,4,5)-> 4]
  2. 第一棵決策樹用這個樣本訓練的預測為3.3
  3. 那麼第二棵決策樹訓練時的輸入,這個樣本就變成了:[(2,4,5)-> 0.7]
  4. 也就是說,下一棵決策樹輸入樣本會與前面決策樹的訓練和預測相關

很快你會意識到,Xgboost為何也是一個boosting的整合學習了。

而一個迴歸樹形成的關鍵點在於:

  • 分裂點依據什麼來劃分(如前面說的均方誤差最小,loss);
  • 分類後的節點預測值是多少(如前面說,有一種是將葉子節點下各樣本實際值得均值作為葉子節點預測誤差,或者計算所得)

至於另一類整合學習方法,比如Random Forest(隨機森林)演算法,各個決策樹是獨立的、每個決策樹在樣本堆裡隨機選一批樣本,隨機選一批特徵進行獨立訓練,各個決策樹之間沒有啥關係。本文暫不展開介紹。

 

3.GBDT

說到Xgboost,不得不先從GBDT(Gradient Boosting Decision Tree)說起。因為xgboost本質上還是一個GBDT,但是力爭把速度和效率發揮到極致,所以叫X (Extreme) GBoosted。包括前面說過,兩者都是boosting方法。

GBDT的原理很簡單,就是所有弱分類器的結果相加等於預測值,然後下一個弱分類器去擬合誤差函式對預測值的梯度/殘差(這個梯度/殘差就是預測值與真實值之間的誤差)。當然了,它裡面的弱分類器的表現形式就是各棵樹。如圖所示:Y = Y1 + Y2 + Y3。

舉一個非常簡單的例子,比如我今年30歲了,但計算機或者模型GBDT並不知道我今年多少歲,那GBDT咋辦呢?

  1. 它會在第一個弱分類器(或第一棵樹中)隨便用一個年齡比如20歲來擬合,然後發現誤差有10歲;
  2. 接下來在第二棵樹中,用6歲去擬合剩下的損失,發現差距還有4歲;
  3. 接著在第三棵樹中用3歲擬合剩下的差距,發現差距只有1歲了;
  4. 最後在第四課樹中用1歲擬合剩下的殘差,完美。

最終,四棵樹的結論加起來,就是真實年齡30歲。實際工程中,gbdt是計算負梯度,用負梯度近似殘差。

注意,為何gbdt可以用用負梯度近似殘差呢?

迴歸任務下,GBDT 在每一輪的迭代時對每個樣本都會有一個預測值,此時的損失函式為均方差損失函式,

那此時的負梯度是這樣計算的

所以,當損失函式選用均方損失函式是時,每一次擬合的值就是(真實值 - 當前模型預測的值),即殘差。此時的變數是,即“當前預測模型的值”,也就是對它求負梯度。

另外,這裡還得再囉嗦一下,上面預測年齡的第一個步驟中的“隨便”二字看似隨便,其實深入思考一下一點都不隨便,你會發現大部分做預測的模型,基本都是這麼個常規套路,先隨便用一個值去預測,然後對比預測值與真實值的差距,最後不斷調整 縮小差距。所以會出來一系列目標函式:確定目標,和損失函式:縮小誤差。

再進一步思考,你會發現這完全符合人類做預測的普遍常識、普遍做法,當對一個事物不太瞭解時,一開始也是根據經驗嘗試、初探,直到逼近某種意義上的接近或者完全吻合。

還是年齡預測的例子。

簡單起見,假定訓練集只有4個人:A,B,C,D,他們的年齡分別是14,16,24,26。其中A、B分別是高一和高三學生;C,D分別是應屆畢業生和工作兩年的員工。

所以,現在的問題就是我們要預測這4個人的年齡,咋下手?很簡單,先隨便用一個年齡比如20歲去擬合他們,然後根據實際情況不斷調整。

如果是用一棵傳統的迴歸決策樹來訓練,會得到如下圖所示結果:


現在我們使用GBDT來做這件事,由於資料太少,我們限定葉子節點做多有兩個,即每棵樹都只有一個分枝,並且限定只學兩棵樹。

我們會得到如下圖所示結果:


在第一棵樹分枝和圖1一樣,由於A,B年齡較為相近,C,D年齡較為相近,他們被分為左右兩撥,每撥用平均年齡作為預測值。

  • 此時計算殘差(殘差的意思就是:A的實際值 - A的預測值 = A的殘差),所以A的殘差就是實際值14 - 預測值15 = 殘差值-1。
  • 注意,A的預測值是指前面所有樹累加的和,這裡前面只有一棵樹所以直接是15,如果還有樹則需要都累加起來作為A的預測值。

殘差在數理統計中是指實際觀察值與估計值(擬合值)之間的差。“殘差”蘊含了有關模型基本假設的重要資訊。如果迴歸模型正確的話, 我們可以將殘差看作誤差的觀測值。

進而得到A,B,C,D的殘差分別為-1,1,-1,1。

然後拿它們的殘差-1、1、-1、1代替A B C D的原值,到第二棵樹去學習,第二棵樹只有兩個值1和-1,直接分成兩個節點,即A和C分在左邊,B和D分在右邊,經過計算(比如A,實際值-1 - 預測值-1 = 殘差0,比如C,實際值-1 - 預測值-1 = 0),此時所有人的殘差都是0。

殘差值都為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

所以,GBDT需要將多棵樹的得分累加得到最終的預測得分,且每一次迭代,都在現有樹的基礎上,增加一棵樹去擬合前面樹的預測結果與真實值之間的殘差。

 

4.Xgboost

4.1 xgboost樹的定義

本節的示意圖基本引用自xgboost原作者陳天奇的講義PPT中。

舉個例子,我們要預測一家人對電子遊戲的喜好程度,考慮到年輕和年老相比,年輕更可能喜歡電子遊戲,以及男性和女性相比,男性更喜歡電子遊戲,故先根據年齡大小區分小孩和大人,然後再通過性別區分開是男是女,逐一給各人在電子遊戲喜好程度上打分,如下圖所示。

就這樣,訓練出了2棵樹tree1和tree2,類似之前gbdt的原理,兩棵樹的結論累加起來便是最終的結論,所以小孩的預測分數就是兩棵樹中小孩所落到的結點的分數相加:2 + 0.9 = 2.9。爺爺的預測分數同理:-1 + (-0.9)= -1.9。具體如下圖所示

恩,你可能要拍案而起了,驚呼,這不是跟上文介紹的gbdt乃異曲同工麼?

事實上,如果不考慮工程實現、解決問題上的一些差異,xgboost與gbdt比較大的不同就是目標函式的定義。xgboost的目標函式如下圖所示:

其中

  • 紅色箭頭所指向的L 即為損失函式(比如平方損失函式:,或logistic損失函式:
  • 紅色方框所框起來的是正則項(包括L1正則、L2正則)
  • 紅色圓圈所圈起來的為常數項
  • 對於f(x),xgboost利用泰勒展開三項,做一個近似

我們可以很清晰地看到,最終的目標函式只依賴於每個資料點在誤差函式上的一階導數和二階導數。

額,峰迴路轉,突然丟這麼大一個公式,不少人可能瞬間就懵了。沒事,下面我們們來拆解下這個目標函式,並一一剖析每個公式、每個符號、每個下標的含義。

4.2 xgboost目標函式

xgboost的核心演算法思想不難,基本就是

  1. 不斷地新增樹,不斷地進行特徵分裂來生長一棵樹,每次新增一個樹,其實是學習一個新函式,去擬合上次預測的殘差

    注:w_q(x)為葉子節點q的分數,對應了所有K棵迴歸樹(regression tree)的集合,而f(x)為其中一棵迴歸樹。

  2. 當我們訓練完成得到k棵樹,我們要預測一個樣本的分數,其實就是根據這個樣本的特徵,在每棵樹中會落到對應的一個葉子節點,每個葉子節點就對應一個分數
  3. 最後只需要將每棵樹對應的分數加起來就是該樣本的預測值。

顯然,我們的目標是要使得樹群的預測值儘量接近真實值y_{i},而且有儘量大的泛化能力。

所以,從數學角度看這是一個泛函最優化問題,故把目標函式簡化如下:

如你所見,這個目標函式分為兩部分:損失函式和正則化項。且損失函式揭示訓練誤差(即預測分數和真實分數的差距),正則化定義複雜度。

對於上式而言,是整個累加模型的輸出,正則化項是則表示樹的複雜度的函式,值越小複雜度越低,泛化能力越強,其表示式為

T表示葉子節點的個數,w表示葉子節點的分數。直觀上看,目標要求預測誤差儘量小,且葉子節點T儘量少(γ控制葉子結點的個數),節點數值w儘量不極端(λ控制葉子節點的分數不會過大),防止過擬合。

插一句,一般的目標函式都包含下面兩項

其中,誤差/損失函式鼓勵我們的模型儘量去擬合訓練資料,使得最後的模型會有比較少的 bias。而正則化項則鼓勵更加簡單的模型。因為當模型簡單之後,有限資料擬合出來結果的隨機性比較小,不容易過擬合,使得最後模型的預測更加穩定。

4.2.1 模型學習與訓練誤差

具體來說,目標函式第一部分中的  表示第個樣本, ( − y_{i}) 表示第  個樣本的預測誤差,我們的目標當然是誤差越小越好。

類似之前GBDT的套路,xgboost也是需要將多棵樹的得分累加得到最終的預測得分(每一次迭代,都在現有樹的基礎上,增加一棵樹去擬合前面樹的預測結果與真實值之間的殘差)。

但,我們如何選擇每一輪加入什麼f呢?答案是非常直接的,選取一個 來使得我們的目標函式儘量最大地降低。

再強調一下,考慮到第t輪的模型預測值  =  前t-1輪的模型預測  +  ,因此誤差函式記為: ( ),後面一項為正則化項。

對於這個誤差函式的式子而言,在第t步,是真實值,即已知,可由上一步第t-1步中的加上計算所得,某種意義上也算已知值,故模型學習的是

上面那個Obj的公式表達的可能有些過於抽象,我們可以考慮當 是平方誤差的情況(相當於),這個時候我們的目標可以被寫成下面這樣的二次函式(圖中畫圈的部分表示的就是預測值和真實值之間的殘差):

更加一般的,損失函式不是二次函式咋辦?利用泰勒展開,不是二次的想辦法近似為二次(如你所見,定義了一階導g和二階導h)。

恩恩,注意了!不少人可能就會在這裡卡殼,網上也很少有文章解釋清楚,在和七月線上AI lab陳博士討論之後,發現這裡面最關鍵的其實就是把泰勒二階展開各項和xgboost 目標函式的對應關係搞清楚,相當於我們可以利用泰勒二階展開去做目標函式的近似。

首先,這是泰勒二階展開

對應到xgboost的目標函式裡頭

忽略損失函式 中的(別忘了上面說的“ 在第t步,是真實值,即已知 ”,不影響後續目標函式對的偏導計算),做下一一對應:

  • 泰勒二階展開f 裡的x對應目標函式裡的
  • f 裡的對應目標函式的
  • 從而f 對x求導數時,對應為目標函式對求偏導

得到:

其中,

嗚呼,透了!不過,這個轉化過程中的關鍵泰勒二次展開到底是哪來的呢?

在數學中,泰勒公式(英語:Taylor's Formula)是一個用函式在某點的資訊描述其附近取值的公式。這個公式來自於微積分的泰勒定理(Taylor's theorem),泰勒定理描述了一個可微函式,如果函式足夠光滑的話,在已知函式在某一點的各階導數值的情況之下,泰勒公式可以用這些導數值做係數構建一個多項式來近似函式在這一點的鄰域中的值,這個多項式稱為泰勒多項式(Taylor polynomial)。

相當於告訴我們可由利用泰勒多項式的某些次項做原函式的近似。

泰勒定理:
設 n 是一個正整數。如果定義在一個包含 a 的區間上的函式 f 在 a 點處 n+1 次可導,那麼對於這個區間上的任意 x,都有:

其中的多項式稱為函式在a 處的泰勒展開式,剩餘的是泰勒公式的餘項,是的高階無窮小。

接下來,考慮到我們的第t 顆迴歸樹是根據前面的t-1顆迴歸樹的殘差得來的,相當於t-1顆樹的值是已知的。換句話說,對目標函式的優化不影響,可以直接去掉,且常數項也可以移除,從而得到如下一個比較統一的目標函式。

這時,目標函式只依賴於每個資料點在誤差函式上的一階導數g和二階導數h(相信你已經看出xgboost和gbdt的不同了,目標函式保留了泰勒展開的二次項)。

總的指導原則如就職Google的讀者crab6789所說:

實質是把樣本分配到葉子結點會對應一個obj,優化過程就是obj優化。也就是分裂節點到葉子不同的組合,不同的組合對應不同obj,所有的優化圍繞這個思想展開。

到目前為止我們討論了目標函式中的第一個部分:訓練誤差。接下來我們討論目標函式的第二個部分:正則項,即如何定義樹的複雜度。

4.2.2 正則項:樹的複雜度

首先,梳理下幾個規則

  • 用葉子節點集合以及葉子節點得分表示 
  • 每個樣本都落在一個葉子節點上 
  • q(x)表示樣本x在某個葉子節點上,wq(x)是該節點的打分,即該樣本的模型預測值

所以當我們把樹成結構部分q葉子權重部分w後,結構函式q把輸入對映到葉子的索引號上面去,而w給定了每個索引號對應的葉子分數是什麼。

另外,如下圖所示,xgboost對樹的複雜度包含了兩個部分:

  1. 一個是樹裡面葉子節點的個數T
  2. 一個是樹上葉子節點的得分w的L2模平方(對w進行L2正則化,相當於針對每個葉結點的得分增加L2平滑,目的是為了避免過擬合)

在這種新的定義下,我們可以把之前的目標函式進行如下變形(另,別忘了:

其中被定義為每個葉節點 j 上面樣本下標的集合 ,g是一階導數,h是二階導數。這一步是由於xgboost目標函式第二部分加了兩個正則項,一個是葉子節點個數(T),一個是葉子節點的分數(w)。

從而,加了正則項的目標函式裡就出現了兩種累加

  • 一種是 - > n(樣本數)
  • 一種是 -> T(葉子節點數)

這一個目標包含了T個相互獨立的單變數二次函式。

理解這個推導的關鍵在哪呢?在和AI lab陳博士討論之後,其實就在於理解這個定義:被定義為每個葉節點 j 上面樣本下標的集合 ,這個定義裡的q(xi)要表達的是:每個樣本值xi 都能通過函式q(xi)對映到樹上的某個葉子節點,從而通過這個定義把兩種累加統一到了一起。

接著,我們可以定義

最終公式可以化簡為

通過對求導等於0,可以得到

然後把最優解代入得到:

4.3 打分函式計算

Obj代表了當我們指定一個樹的結構的時候,我們在目標上面最多減少多少。我們可以把它叫做結構分數(structure score)

4.3.1 分裂節點

很有意思的一個事是,我們從頭到尾瞭解了xgboost如何優化、如何計算,但樹到底長啥樣,我們卻一直沒看到。很顯然,一棵樹的生成是由一個節點一分為二,然後不斷分裂最終形成為整棵樹。那麼樹怎麼分裂的就成為了接下來我們要探討的關鍵。

對於一個葉子節點如何進行分裂,xgboost作者在其原始論文中給出了兩種分裂節點的方法

(1)列舉所有不同樹結構的貪心法

現在的情況是隻要知道樹的結構,就能得到一個該結構下的最好分數,那如何確定樹的結構呢?

一個想當然的方法是:不斷地列舉不同樹的結構,然後利用打分函式來尋找出一個最優結構的樹,接著加入到模型中,不斷重複這樣的操作。而再一想,你會意識到要列舉的狀態太多了,基本屬於無窮種,那咋辦呢?

我們試下貪心法,從樹深度0開始,每一節點都遍歷所有的特徵,比如年齡、性別等等,然後對於某個特徵,先按照該特徵裡的值進行排序,然後線性掃描該特徵進而確定最好的分割點,最後對所有特徵進行分割後,我們選擇所謂的增益Gain最高的那個特徵,而Gain如何計算呢?

還記得4.2節最後,我們得到的計算式子吧?

換句話說,目標函式中的G/(H+λ)部分,表示著每一個葉子節點對當前模型損失的貢獻程度,融合一下,得到Gain的計算表示式,如下所示:

第一個值得注意的事情是“對於某個特徵,先按照該特徵裡的值進行排序”,這裡舉個例子。

比如設定一個值a,然後列舉所有x < a、a  < x這樣的條件(x代表某個特徵比如年齡age,把age從小到大排序:假定從左至右依次增大,則比a小的放在左邊,比a大的放在右邊),對於某個特定的分割a,我們要計算a左邊和右邊的導數和。

比如總共五個人,按年齡排好序後,一開始我們總共有如下4種劃分方法:

  1. 把第一個人和後面四個人劃分開
  2. 把前兩個人和後面三個人劃分開
  3. 把前三個人和後面兩個人劃分開
  4. 把前面四個人和後面一個人劃分開

接下來,把上面4種劃分方法全都各自計算一下Gain,看哪種劃分方法得到的Gain值最大則選取哪種劃分方法,經過計算,發現把第2種劃分方法“前面兩個人和後面三個人劃分開”得到的Gain值最大,意味著在一分為二這個第一層層面上這種劃分方法是最合適的。

換句話說,對於所有的特徵x,我們只要做一遍從左到右的掃描就可以列舉出所有分割的梯度和GL和GR。然後用計算Gain的公式計算每個分割方案的分數就可以了。

然後後續則依然按照這種劃分方法繼續第二層、第三層、第四層、第N層的分裂。

第二個值得注意的事情就是引入分割不一定會使得情況變好,所以我們有一個引入新葉子的懲罰項。優化這個目標對應了樹的剪枝, 當引入的分割帶來的增益小於一個閥值γ 的時候,則忽略這個分割。

換句話說,當引入某項分割,結果分割之後得到的分數 - 不分割得到的分數得到的值太小(比如小於我們的最低期望閥值γ),但卻因此得到的複雜度過高,則相當於得不償失,不如不分割。即做某個動作帶來的好處比因此帶來的壞處大不了太多,則為避免複雜 多一事不如少一事的態度,不如不做。

相當於在我們發現“分”還不如“不分”的情況下後(得到的增益太小,小到小於閾值γ),會有2個葉子節點存在同一棵子樹上的情況。

下面是論文中的演算法

(2)近似演算法

主要針對資料太大,不能直接進行計算

就職於Google的讀者crab6789點評:

把樣本從根分配到葉子結點,就是個排列組合。不同的組合對應的cost不同。求最好的組合你就要try,一味窮舉是不可能的,所以才出來貪婪法。不看從頭到尾 就看當下節點怎麼分配最好。這才有了那個exact greddy方法,後來還想加速才有了histogram的做法。

4.4 小結:Boosted Tree Algorithm 

總結一下,如圖所示

我們們來再次回顧整個過程。

如果某個樣本label數值為4,那麼第一個迴歸樹預測3,第二個預測為1; 另外一組迴歸樹,一個預測2,一個預測2,那麼傾向後一種,為什麼呢?前一種情況,第一棵樹學的太多,太接近4,也就意味著有較大的過擬合的風險。

OK,聽起來很美好,可是怎麼實現呢,上面這個目標函式跟實際的引數怎麼聯絡起來,記得我們說過,迴歸樹的引數:

  1. 選取哪個feature分裂節點呢
  2. 節點的預測值(總不能靠取平均值這麼粗暴不講道理的方式吧,好歹高階一點)

最終的策略就是:貪心 + 最優化(對的,二次最優化) 。

通俗解釋貪心策略:就是決策時刻按照當前目標最優化決定,說白了就是眼前利益最大化決定,“目光短淺”策略。

這裡是怎麼用貪心策略的呢,剛開始你有一群樣本,放在第一個節點,這時候T=1,w多少呢,不知道,是求出來的,這時候所有樣本的預測值都是w,帶入樣本的label數值,此時loss function變為

  • 如果這裡的l(w−yi)誤差表示用的是平方誤差,那麼上述函式就是一個關於w的二次函式求最小值,取最小值的點就是這個節點的預測值,最小的函式值為最小損失函式。
  • 本質上來講,這就是一個二次函式最優化問題!但要是損失函式不是二次函式咋辦?泰勒展開,不是二次的想辦法近似為二次。

接著來,接下來要選個feature分裂成兩個節點,變成一棵弱小的樹苗,那麼需要:

  1. 確定分裂用的feature,how?最簡單的是粗暴的列舉/窮舉(嗯,夠粗暴),然後選擇loss function效果最好的那個;
  2. 如何確立節點的w以及最小的loss function,大聲告訴我怎麼做?對,二次函式的求最值(計算二次的最值一般都有固定套路,即導數等於0的點) 。所以,選擇一個feature分裂,計算loss function最小值,然後再選一個feature分裂,又得到一個loss function最小值,你列舉完,找一個效果最好的,把樹給分裂,就得到了小樹苗。

在分裂的時候,你可以注意到,每次節點分裂,loss function被影響的只有這個節點的樣本,因而每次分裂,計算分裂的增益(loss function的降低量)只需要關注打算分裂的那個節點的樣本。

總而言之,XGBoost使用了和CART迴歸樹一樣的想法,利用貪婪演算法,遍歷所有特徵的所有特徵劃分點,不同的是使用的目標函式不一樣。具體做法就是分裂後的目標函式值比單子葉子節點的目標函式的增益,同時為了限制樹生長過深,還加了個閾值,只有當增益大於該閾值才進行分裂。

以下便為設定的閾值

從而繼續分裂,形成一棵樹,再形成一棵樹,每次在上一次的預測基礎上取最優進一步分裂/建樹,是不是貪心策略?

凡是這種迴圈迭代的方式必定有停止條件,什麼時候停止呢?簡言之,設定樹的最大深度、當樣本權重和小於設定閾值時停止生長以防止過擬合。具體而言,則

  1. 當引入的分裂帶來的增益小於設定閥值的時候,我們可以忽略掉這個分裂,所以並不是每一次分裂loss function整體都會增加的,有點預剪枝的意思,閾值引數為(即正則項裡葉子節點數T的係數); 
  2. 當樹達到最大深度時則停止建立決策樹,設定一個超引數max_depth,避免樹太深導致學習區域性樣本,從而過擬合; 
  3. 當樣本權重和小於設定閾值時則停止建樹。什麼意思呢,即涉及到一個超引數-最小的樣本權重和min_child_weight,和GBM的 min_child_leaf 引數類似,但不完全一樣。大意就是一個葉子節點樣本太少了,也終止同樣是防止過擬合; 
  4. 貌似看到過有樹的最大數量的…

 

6 參考文獻與推薦閱讀

  1. xgboost原始論文:https://arxiv.org/pdf/1603.02754v1.pdf
  2. xgboost作者講義PPT:https://homes.cs.washington.edu/~tqchen/pdf/BoostedTree.pdf
  3. XGBoost 與 Boosted Tree
  4. xgboost原理:https://blog.csdn.net/a819825294/article/details/51206410
  5. xgboost入門與實戰(原理篇):https://blog.csdn.net/sb19931201/article/details/52557382
  6. CART的Wikipedia
  7. 整合學習(Ensemble Learning)
  8. 淺談整合學習:Boosting與隨機森林
  9. 從決策樹學習談到貝葉斯分類演算法
  10. 決策樹(三)--完整總結(ID3,C4.5,CART,剪枝,替代)兼原始碼剖析
  11. 一文讀懂機器學習大殺器XGBoost原理
  12. 為什麼在實際的 kaggle 比賽中 gbdt 和 random forest 效果非常好?
  13. 通俗、有邏輯的寫一篇說下Xgboost的原理,供討論參考
  14. 七月線上機器學習第8期第4課 決策樹、隨機森林、GBDT、xgboost
  15. xgboost的一些核心引數
  16. 怎樣通俗的理解泰勒級數?:https://www.zhihu.com/question/21149770
  17. xgboost 為何需要泰勒二階展開:https://www.julyedu.com/question/big/kp_id/23/ques_id/990
  18. 最通俗的梯度提升樹(GBDT)原理
  19. ID3、C4.5、CART、隨機森林、bagging、boosting、Adaboost、GBDT、xgboost演算法總結
  20. XGBoost二階泰勒展開公式推導
  21. 泰勒公司Wikipedia
  22. 七月線上集6學員超級瑪麗寫的xgboost筆記:一文帶你讀懂xgboost原理
  23. 線上編輯LaTeX公式:http://www.codecogs.com/latex/eqneditor.php?lang=zh-cn

 

後記

終於大致搞懂了這個經常刷屏的xgboost,再次印證我之前說過的一句話:當你學習某個知識點感覺學不懂時,十有八九不是你不夠聰明,十有八九是你所看的資料不夠通俗、不夠易懂(如果還是不行,問人)。

希望閱讀此文的你,也有同樣的感受。

以下的本文的改進過程,供理解上參考:

  1. 8.4上午第一版,通過一個通俗易懂的年齡預測例子介紹gbdt,因為gbdt是理解xgboost的基礎;
  2. 8.4下午第二版,xgboost的推導裡公式很多,初學者很容易陷進去,後通過抓住xgboost的核心:目標函式,梳理清晰xgboost的脈絡框架;
  3. 8.5上午第三版,優化了決策樹的介紹部分,比如增加對資訊增益的介紹;
  4. 8.5下午第四版,優化大部分公式的顯示,比如之前是純文字顯示,現改成LaTeX圖片顯示;
  5. 8.6上午第五版,優化對booting整合學習的介紹,已讓全文更循序漸進;
  6. 8.6晚上第六版,規範xgboost目標函式的公式表示,並梳理全文多處細節、公式;
  7. 9.1上午第七版,完善4.3.1節中xgboost樹的分裂劃分方式,以更清晰;
  8. 19年1.9第八版,完善4.3.1節中關於分裂節點的描述,以讓邏輯更清晰、行文更通俗;
  9. 19年1.10第九版,第3部分增加一個預測年齡的例子,以更通俗化解釋GBDT;
  10. 19年1.14第十版,把泰勒二階展開和xgboost 目標函式的對應關係寫清楚,從而理解更順暢。

看完全文後,你會發現理解xgboost有三個關鍵點:①4.2.1節中,理清楚xgboost目標函式各項和泰勒展開二項的一一對應關係,②4.2.2節中對計算數的得分w時使用的一個數學技巧,③4.3.1節中所示的樹的分裂演算法。理清了這三點,則理解xgboost不再有障礙。

July、二零一八年八月六日晚上~二零一九年一月十四日晚上。

相關文章