機器學習算法GBDT的面試要點總結-上篇

1.簡介html

    gbdt全稱梯度降低樹,在傳統機器學習算法裏面是對真實分佈擬合的最好的幾種算法之一,在前幾年深度學習尚未大行其道以前,gbdt在各類競賽是大放異彩。緣由大概有幾個,一是效果確實挺不錯。二是便可以用於分類也能夠用於迴歸。三是能夠篩選特徵。這三點實在是太吸引人了,致使在面試的時候你們也很是喜歡問這個算法。 gbdt的面試考覈點,大體有下面幾個:面試

  • gbdt 的算法的流程?
  • gbdt 如何選擇特徵 ?
  • gbdt 如何構建特徵 ?
  • gbdt 如何用於分類?
  • gbdt 經過什麼方式減小偏差 ?
  • gbdt的效果相比於傳統的LR,SVM效果爲何好一些 ?
  • gbdt 如何加速訓練?
  • gbdt的參數有哪些,如何調參 ?
  • gbdt 實戰當中遇到的一些問題 ?
  • gbdt的優缺點 ?

2. 正式介紹算法

    首先gbdt 是經過採用加法模型(即基函數的線性組合),以及不斷減少訓練過程產生的殘差來達到將數據分類或者回歸的算法。app

  •  gbdt的訓練過程

        咱們經過一張圖片,圖片來源來講明gbdt的訓練過程: 框架

    

                      圖 1:GBDT 的訓練過程機器學習

        gbdt經過多輪迭代,每輪迭代產生一個弱分類器,每一個分類器在上一輪分類器的殘差基礎上進行訓練。對弱分類器的要求通常是足夠簡單,而且是低方差和高誤差的。由於訓練的過程是經過下降誤差來不斷提升最終分類器的精度,(此處是能夠證實的)。函數

        弱分類器通常會選擇爲CART TREE(也就是分類迴歸樹)。因爲上述高誤差和簡單的要求 每一個分類迴歸樹的深度不會很深。最終的總分類器 是將每輪訓練獲得的弱分類器加權求和獲得的(也就是加法模型)。學習

        模型最終能夠描述爲:$$F_{m}(x) = \sum_{m=1}^{M}T\left ( x;\theta _m \right )$$spa

        模型一共訓練M輪,每輪產生一個弱分類器 $T\left ( x;\theta _m \right )$。弱分類器的損失函數$$\hat\theta_{m} = \mathop{\arg\min}_{\theta_{m}} \sum_{i=1}^{N}L\left ( y_{i},F_{m-1}(x_{i})+T(x_{i};\theta_{m} ) \right )$$3d

        $F_{m-1}(x)$ 爲當前的模型,gbdt 經過經驗風險極小化來肯定下一個弱分類器的參數。具體到損失函數自己的選擇也就是L的選擇,有平方損失函數,0-1損失函數,對數損失函數等等。若是咱們選擇平方損失函數,那麼這個差值其實就是咱們日常所說的殘差

    • 可是其實咱們真正關注的,1.是但願損失函數可以不斷的減少,2.是但願損失函數可以儘量快的減少。因此如何儘量快的減少呢?
    • 讓損失函數沿着梯度方向的降低。這個就是gbdt 的 gb的核心了。 利用損失函數的負梯度在當前模型的值做爲迴歸問題提高樹算法中的殘差的近似值去擬合一個迴歸樹。gbdt 每輪迭代的時候,都去擬合損失函數在當前模型下的負梯度。
    • 這樣每輪訓練的時候都可以讓損失函數儘量快的減少,儘快的收斂達到局部最優解或者全局最優解。
  • gbdt如何選擇特徵?

        gbdt選擇特徵的細節實際上是想問你CART Tree生成的過程。這裏有一個前提,gbdt的弱分類器默認選擇的是CART TREE。其實也能夠選擇其餘弱分類器的,選擇的前提是低方差和高誤差。框架服從boosting 框架便可。

        下面咱們具體來講CART TREE(是一種二叉樹) 如何生成。CART TREE 生成的過程其實就是一個選擇特徵的過程。假設咱們目前總共有 M 個特徵。第一步咱們須要從中選擇出一個特徵 j,作爲二叉樹的第一個節點。而後對特徵 j 的值選擇一個切分點 m. 一個 樣本的特徵j的值 若是小於m,則分爲一類,若是大於m,則分爲另一類。如此便構建了CART 樹的一個節點。其餘節點的生成過程和這個是同樣的。如今的問題是在每輪迭代的時候,如何選擇這個特徵 j,以及如何選擇特徵 j 的切分點 m:

    • 原始的gbdt的作法很是的暴力,首先遍歷每一個特徵,而後對每一個特徵遍歷它全部可能的切分點,找到最優特徵 m 的最優切分點 j。
    • 如何衡量咱們找到的特徵 m和切分點 j 是最優的呢? 咱們用定義一個函數 FindLossAndSplit 來展現一下求解過程:

 

 1 def findLossAndSplit(x,y):
 2     # 咱們用 x 來表示訓練數據
 3     # 咱們用 y 來表示訓練數據的label
 4     # x[i]表示訓練數據的第i個特徵
 5     # x_i 表示第i個訓練樣本
 6 
 7     # minLoss 表示最小的損失
 8     minLoss = Integet.max_value
 9     # feature 表示是訓練的數據第幾緯度的特徵
10     feature = 0
11     # split 表示切分點的個數
12     split = 0
13 
14     # M 表示 樣本x的特徵個數
15     for j in range(0,M):
16         # 該維特徵下,特徵值的每一個切分點,這裏具體的切分方式能夠本身定義
17         for c in range(0,x[j]):
18             L = 0
19             # 第一類
20             R1 = {x|x[j] <= c}
21             # 第二類
22             R2 = {x|x[j] > c}
23             # 屬於第一類樣本的y值的平均值
24             y1 = ave{y|x 屬於 R1}
25             # 屬於第二類樣本的y值的平均值
26             y2 = ave{y| x 屬於 R2}
27             # 遍歷全部的樣本,找到 loss funtion 的值
28             for x_1 in all x
29                 if x_1 屬於 R1: 
30                     L += (y_1 - y1)^2 
31                 else:
32                     L += (y_1 - y2)^2
33             if L < minLoss:
34                minLoss = L
35                feature  = i
36                split = c
37     return minLoss,feature ,split

 

    • 若是對這段代碼不是很瞭解的,能夠先去看看李航第五章中對CART TREE 算法的敘述。在這裏,咱們先遍歷訓練樣本的全部的特徵,對於特徵 j,咱們遍歷特徵 j 全部特徵值的切分點 c。找到可讓下面這個式子最小的特徵 j 以及切分點c.
    •     
  • gbdt 如何構建特徵 ?

        其實說gbdt 可以構建特徵並不是很準確,gbdt 自己是不能產生特徵的,可是咱們能夠利用gbdt去產生特徵的組合。在CTR預估中,工業界通常會採用邏輯迴歸去進行處理,在個人上一篇博文當中已經說過,邏輯迴歸自己是適合處理線性可分的數據,若是咱們想讓邏輯迴歸處理非線性的數據,其中一種方式即是組合不一樣特徵,加強邏輯迴歸對非線性分佈的擬合能力。

        長久以來,咱們都是經過人工的先驗知識或者實驗來得到有效的組合特徵,可是不少時候,使用人工經驗知識來組合特徵過於耗費人力,形成了機器學習當中一個很奇特的現象:有多少人工就有多少智能。關鍵是這樣經過人工去組合特徵並不必定可以提高模型的效果。因此咱們的從業者或者學界一直都有一個趨勢即是經過算法自動,高效的尋找到有效的特徵組合。Facebook 在2014年 發表的一篇論文即是這種嘗試下的產物,利用gbdt去產生有效的特徵組合,以便用於邏輯迴歸的訓練,提高模型最終的效果。

             圖 2:用GBDT 構造特徵

        如圖 2所示,咱們 使用 GBDT 生成了兩棵樹,兩顆樹一共有五個葉子節點。咱們將樣本 X 輸入到兩顆樹當中去,樣本X 落在了第一棵樹的第二個葉子節點,第二顆樹的第一個葉子節點,因而咱們即可以依次構建一個五緯的特徵向量,每個緯度表明了一個葉子節點,樣本落在這個葉子節點上面的話那麼值爲1,沒有落在該葉子節點的話,那麼值爲 0.

        因而對於該樣本,咱們能夠獲得一個向量[0,1,0,1,0] 做爲該樣本的組合特徵,和原來的特徵一塊兒輸入到邏輯迴歸當中進行訓練。實驗證實這樣會獲得比較顯著的效果提高。

  • GBDT 如何用於分類 ?

        首先明確一點,gbdt 不管用於分類仍是迴歸一直都是使用的CART 迴歸樹不會由於咱們所選擇的任務是分類任務就選用分類樹,這裏面的核心是由於gbdt 每輪的訓練是在上一輪的訓練的殘差基礎之上進行訓練的。這裏的殘差就是當前模型的負梯度值 。這個要求每輪迭代的時候,弱分類器的輸出的結果相減是有意義的。殘差相減是有意義的。

        若是選用的弱分類器是分類樹,類別相減是沒有意義的。上一輪輸出的是樣本 x 屬於 A類,本一輪訓練輸出的是樣本 x 屬於 B類。 A 和 B 不少時候甚至都沒有比較的意義,A 類- B類是沒有意義的。

        咱們具體到分類這個任務上面來,咱們假設樣本 X 總共有 K類。來了一個樣本 x,咱們須要使用gbdt來判斷 x 屬於樣本的哪一類。

    • 圖三 gbdt 多分類算法流程

        第一步 咱們在訓練的時候,是針對樣本 X 每一個可能的類都訓練一個分類迴歸樹。舉例說明,目前樣本有三類,也就是 K = 3。樣本 x 屬於 第二類。那麼針對該樣本 x 的分類結果,其實咱們能夠用一個 三維向量 [0,1,0] 來表示。0表示樣本不屬於該類,1表示樣本屬於該類。因爲樣本已經屬於第二類了,因此第二類對應的向量維度爲1,其餘位置爲0。

        針對樣本有 三類的狀況,咱們實質上是在每輪的訓練的時候是同時訓練三顆樹。第一顆樹針對樣本x的第一類,輸入爲$(x,0)$。第二顆樹輸入針對 樣本x 的第二類,輸入爲$(x,1)$。第三顆樹針對樣本x 的第三類,輸入爲$(x,0)$

        在這裏每顆樹的訓練過程其實就是就是咱們以前已經提到過的CATR TREE 的生成過程。在此處咱們參照以前的生成樹的程序 便可以就解出三顆樹,以及三顆樹對x 類別的預測值$f_{1}(x),f_{2}(x),f_{3}(x)$。那麼在此類訓練中,咱們仿照多分類的邏輯迴歸 ,使用softmax 來產生機率,則屬於類別 1 的機率$$p_{1}=exp(f_{1}{(x)})/\sum_{k= 1}^{3}exp(f_{k}{(x)})$$

        而且咱們咱們能夠針對類別1 求出 殘差$y_{11}(x) = 0-p_{1}(x)$;類別2 求出殘差$y_{22}(x)= 1-p_2(x)$;類別3 求出殘差$y_{33}(x)= 0-p_{3}(x)$.

        而後開始第二輪訓練 針對第一類 輸入爲(x,$y_{11}(x)$), 針對第二類輸入爲(x,$y_{22}(x))$, 針對 第三類輸入爲 (x,$y_{33}(x)$).繼續訓練出三顆樹。一直迭代M輪。每輪構建 3顆樹。

        因此當K =3。咱們其實應該有三個式子 $$F_{1M}{(x)}=\sum_{m=1}^{M}{\hat{C_{1m}}I(x\epsilon R_{1m})}$$ $$F_{2M}{(x)}=\sum_{m=1}^{M}{\hat{C_{2m}}I(x\epsilon R_{2m})}$$ $$F_{3M}{(x)}=\sum_{m=1}^{M}{\hat{C_{3m}}I(x\epsilon R_{3m})}$$

        當訓練完畢之後,新來一個樣本 x1 ,咱們須要預測該樣本的類別的時候,即可以有這三個式子產生三個值,$f_{1}(x),f_{2}(x),f_{3}(x)$。樣本屬於 某個類別c的機率爲 $$p_{c}=exp(f_{c}{(x)})/\sum_{k= 1}^{3}exp(f_{k}{(x)})$$

  • GBDT 多分類舉例說明

        上面的理論闡述可能仍舊過於難懂,咱們下面將拿Iris 數據集中的六個數據做爲例子,來展現gbdt 多分類的過程。

    • 樣本編號 花萼長度(cm) 花萼寬度(cm) 花瓣長度(cm) 花瓣寬度 花的種類
      1 5.1 3.5 1.4 0.2 山鳶尾
      2 4.9 3.0 1.4 0.2 山鳶尾
      3 7.0 3.2 4.7 1.4 雜色鳶尾
      4 6.4 3.2 4.5 1.5 雜色鳶尾
      5 6.3 3.3 6.0 2.5 維吉尼亞鳶尾
      6 5.8 2.7 5.1 1.9 維吉尼亞鳶尾
      圖四 Iris 數據集

        這是一個有6個樣本的三分類問題。咱們須要根據這個花的花萼長度,花萼寬度,花瓣長度,花瓣寬度來判斷這個花屬於山鳶尾,雜色鳶尾,仍是維吉尼亞鳶尾。具體應用到gbdt多分類算法上面。咱們用一個三維向量來標誌樣本的label。[1,0,0] 表示樣本屬於山鳶尾,[0,1,0] 表示樣本屬於雜色鳶尾,[0,0,1] 表示屬於維吉尼亞鳶尾。

        gbdt 的多分類是針對每一個類都獨立訓練一個 CART Tree。因此這裏,咱們將針對山鳶尾類別訓練一個 CART Tree 1。雜色鳶尾訓練一個 CART Tree 2 。維吉尼亞鳶尾訓練一個CART Tree 3,這三個樹相互獨立。

        咱們以樣本 1 爲例。針對 CART Tree1 的訓練樣本是$[5.1, 3.5 , 1.4, 0.2]$,label 是 1,最終輸入到模型當中的爲$[5.1, 3.5 , 1.4, 0.2, 1]$。針對 CART Tree2 的訓練樣本也是$[5.1, 3.5 , 1.4, 0.2]$,可是label 爲 0,最終輸入模型的爲$[5.1, 3.5 , 1.4, 0.2, 0]$. 針對 CART Tree 3的訓練樣本也是$[5.1, 3.5 , 1.4, 0.2] $,label 也爲0,最終輸入模型當中的爲$[5.1, 3.5 , 1.4, 0.2, 0]$. 

        下面咱們來看 CART Tree1 是如何生成的,其餘樹 CART Tree2 , CART Tree 3的生成方式是同樣的。CART Tree的生成過程是從這四個特徵中找一個特徵作爲CART Tree1 的節點。好比花萼長度作爲節點。6個樣本當中花萼長度 大於5.1 cm的就是 A類,小於等於 5.1 cm 的是B類。生成的過程其實很是簡單,問題 1.是哪一個特徵最合適? 2.是這個特徵的什麼特徵值做爲切分點? 即便咱們已經肯定了花萼長度作爲節點。花萼長度自己也有不少值。在這裏咱們的方式是遍歷全部的可能性,找到一個最好的特徵和它對應的最優特徵值可讓當前式子的值最小。

         咱們以第一個特徵的第一個特徵值爲例。R1 爲全部樣本中花萼長度小於 5.1 cm 的樣本集合,R2 爲全部樣本當中花萼長度大於等於 5.1cm 的樣本集合。因此 $R1 = \left \{  2\right \}$,$R2 = \left \{ 1,3,4,5,6 \right \}$.

       

圖 5 節點分裂示意圖

        y1 爲 R1 全部樣本的label 的均值 $1/1 = 1$。y2 爲 R2 全部樣本的label 的均值 $(1+0+0+0+0) /5 = 0.2$。

        下面便開始針對全部的樣本計算這個式子的值。樣本1 屬於 R2 計算的值爲$( 1 - 0.2)^2$, 樣本2 屬於R1 計算的值爲$( 1 -1 )^2$, 樣本 3,4,5,6同理都是 屬於 R2的 因此值是$(0-0.2)^2$. 把這六個值加起來,即是 山鳶尾類型在特徵1 的第一個特徵值的損失值。這裏算出來(1-0.2)^2+ (1-1)^2 + (0-0.2)^2+(0-0.2)^2+(0-0.2)^2 +(0-0.2)^2= 0.84

        接着咱們計算第一個特徵的第二個特徵值,計算方式同上,R1 爲全部樣本中 花萼長度小於 4.9 cm 的樣本集合,R2 爲全部樣本當中 花萼長度大於等於 4.9 cm 的樣本集合.因此 $R1 = \left \{  \right \}$,$R1 = \left \{ 1,2,3,4,5,6 \right \}$. y1 爲 R1 全部樣本的label 的均值 = 0。y2 爲 R2 全部樣本的label 的均值 $(1+1+0+0+0+0) /6 = 0.3333$。

圖 6 第一個特徵的第二個特偵值的節點分裂狀況        

        咱們須要針對全部的樣本,樣本1 屬於 R2, 計算的值爲$( 1 - 0.333 )^2$, 樣本2 屬於R2 ,計算的值爲$( 1 -0.333 )^2$, 樣本 3,4,5,6同理都是 屬於 R2的, 因此值是$(0-0.333)^2$. 把這六個值加起來山鳶尾類型在特徵1 的第二個特徵值的損失值。這裏算出來 (1-0.333)^2+ (1-0.333)^2 + (0-0.333)^2+(0-0.333)^2+(0-0.333)^2 +(0-0.333)^2 = 2.244189. 這裏的損失值大於 特徵一的第一個特徵值的損失值,因此咱們不取這個特徵的特徵值。

圖 7 全部狀況說明  

        這樣咱們能夠遍歷全部特徵的全部特徵值,找到讓這個式子最小的特徵以及其對應的特徵值,一共有24種狀況,4個特徵*每一個特徵有6個特徵值。在這裏咱們算出來讓這個式子最小的特徵花萼長度,特徵值爲5.1 cm。這個時候損失函數最小爲 0.8。

        因而咱們的預測函數此時也能夠獲得: $$f(x) = \sum_{x\epsilon R_{1}} y_{1}*I(x\epsilon R_{1})+\sum_{x\epsilon R_{2}} y_{2}*I(x\epsilon R_{2})$$

        此處 R1 = {2},R2 = {1,3,4,5,6},y1 = 1,y2 = 0.2。訓練完之後的最終式子爲 $$f_{1}(x) = \sum_{x\epsilon R_{1}} 1*I(x\epsilon R_{1})+\sum_{x\epsilon R_{2}} 0.2*I(x\epsilon R_{2})$$

        藉由這個式子,咱們獲得對樣本屬於類別1 的預測值 $f_{1}(x) = 1 + 0.2 * 5  = 2$。同理咱們能夠獲得對樣本屬於類別2,3的預測值$f_{2}(x)$,$f_{3}(x)$.樣本屬於類別1的機率 即爲 $$p_{1}=exp(f_{1}{(x)})/\sum_{k= 1}^{3}exp(f_{k}{(x)})$$

        下面咱們用代碼來實現整個找特徵的過程,你們能夠本身再對照代碼看看

 1 # 定義訓練數據
 2 train_data = [[5.1,3.5,1.4,0.2],[4.9,3.0,1.4,0.2],[7.0,3.2,4.7,1.4],[6.4,3.2,4.5,1.5],[6.3,3.3,6.0,2.5],[5.8,2.7,5.1,1.9]]
 3 
 4 # 定義label
 5 label_data = [[1,0,0],[1,0,0],[0,1,0],[0,1,0],[0,0,1],[0,0,1]]
 6 # index 表示的第幾類
 7 def findBestLossAndSplit(train_data,label_data,index):
 8         sample_numbers = len(label_data)
 9         feature_numbers = len(train_data[0])
10         current_label = []
11 
12         # define the minLoss
13         minLoss = 10000000
14 
15         # feature represents the dimensions of the feature
16         feature = 0
17 
18         # split represents the detail split value
19         split = 0
20 
21         # get current label
22         for label_index in range(0,len(label_data)):
23             current_label.append(label_data[label_index][index])
24 
25         # trans all features
26         for feature_index in range(0,feature_numbers):
27             ## current feature value
28             current_value = []
29 
30             for sample_index in range(0,sample_numbers):
31                 current_value.append(train_data[sample_index][feature_index])
32             L = 0
33             ## different split value
34             print current_value
35             for index in range(0,len(current_value)):
36                 R1 = []
37                 R2 = []
38                 y1 = 0
39                 y2 = 0
40 
41                 for index_1 in range(0,len(current_value)):
42                     if current_value[index_1] < current_value[index]:
43                         R1.append(index_1)
44                     else:
45                         R2.append(index_1)
46 
47                 ## calculate the samples for first class
48                 sum_y = 0
49                 for index_R1 in R1:
50                     sum_y += current_label[index_R1]
51                 if len(R1) != 0:
52                     y1 = float(sum_y) / float(len(R1))
53                 else:
54                     y1 = 0
55 
56                 ## calculate the samples for second class
57                 sum_y = 0
58                 for index_R2 in R2:
59                     sum_y += current_label[index_R2]
60                 if len(R2) != 0:
61                     y2 = float(sum_y) / float(len(R2))
62                 else:
63                     y2 = 0
64 
65                 ## trans all samples to find minium loss and best split
66                 for index_2 in range(0,len(current_value)):
67                     if index_2 in R1:
68                         L += float((current_label[index_2]-y1))*float((current_label[index_2]-y1))
69                     else:
70                         L += float((current_label[index_2]-y2))*float((current_label[index_2]-y2))
71 
72                 if L < minLoss:
73                     feature = feature_index
74                     split = current_value[index]
75                     minLoss = L
76                     print "minLoss"
77                     print minLoss
78                     print "split"
79                     print split
80                     print "feature"
81                     print feature
82         return minLoss,split,feature
83 
84 findBestLossAndSplit(train_data,label_data,0)

 

  • 3 總結

        目前,咱們總結了 gbdt 的算法的流程,gbdt如何選擇特徵,如何產生特徵的組合,以及gbdt 如何用於分類,這個目前能夠認爲是gbdt 最常常問到的四個部分。至於剩餘的問題,由於篇幅的問題,咱們準備再開一個篇幅來進行總結。

相關文章
相關標籤/搜索