談談模型融合之三 —— GBDT

前言

原本應該是年後就要寫的一篇博客,由於考完試後忙了一段時間課設和實驗,而後回家後又在摸魚,就一直沒開動。趁着這段時間只能呆在家裏來把這些博客補上。在以前的文章中介紹了 Random Forest 和 AdaBoost,這篇文章將介紹介紹在數據挖掘競賽中,最經常使用的算法之一 —— GBDT(Gradient Boosting Decision Tree)。python

GBDT

原理

GBDT 其實是 GBM(Gradient Boosting Machine) 中的一種,採用 CART 樹做爲基學習器,因此稱爲 GBDT。與 AdaBoost 同樣,GBDT 爲 Boosting 家族中的一員。其表達式爲
\[ f_m(x) = \sum_{m=1}^{M} T(x;\Theta_m) \]
其中\(T(x;\Theta_m)\)表示決策樹;\(\Theta_m\)爲決策樹參數;M爲樹的個數。面試

這裏來回顧下 AdaBoost,AdaBoost 經過不斷調整樣本權重,使得上一次分類錯誤的樣本權重變大,最後訓練出 m 個弱分類器,而後對 m 個弱分類器加權結合造成強分類器。算法

而 GBDT 又是另外一思想,當咱們假設前一輪迭代獲得的學習器爲 \(f_{m-1}(x)\) ,損失函數爲 \(L(y, f_{m-1}(x))\) ,那麼,本輪迭代的目標就是使損失函數 \(L(y, f_{m-1}(x) + h_m(x))\) 的值儘量的小。app

咱們先將損失函數假設爲最經常使用的平方損失dom

\(r = y - f_{m-1}(x)\) ,那麼第 m 步的目標就是最小化 \(L(y, f_m(x)) = \frac{1}{2}(y-f_m(x))^2=\frac{1}{2}(r-h_m(x))^2\)函數

到這裏,彷佛發現了點什麼,咱們對損失函數求導,發現:
\[ \frac{\partial{L}}{\partial{h_m(x)}}=h_m(x)-r \]
看出什麼來了沒?對其取個負號就變成 \(r-h_m(x)\) ,即咱們常說的殘差 ,因此,當爲平方損失函數時,弱學習器擬合的目標爲以前擬合結果的殘差。那到這裏代碼就很好寫了,可是,咱們實際中有不少其它的損失函數,並且在不少問題中,這些損失函數比平方損失要好得多。那這時候,若是咱們還採用一樣的思路,那就沒辦法像上面那樣直接展開並擬合殘差了,這時候該怎麼辦?學習

這裏別忘了,咱們最終的目標是使得 \(L(y, f_m(x))\) 最小,那麼只須要保證 \(L(y, f_{m-1}(x)+h_m(x))\) 的值比 \(L(y, f_{m-1}(x))\) 小就行了。spa


\[ max[L(y,f_{m-1}(x))-L(y,f_{m-1}(x)+h(x))] \]
檢驗大一高數學的怎麼樣的時候到了 orzrest

咱們前面說了第 m 輪迭代的損失函數爲 \(L(y, f_{m-1}(x) + h_m(x))\) ,換一種形式,寫成 \(L(f_{m-1}(x) + h_m(x))\) ,對其進行一階泰勒展開,得
\[ L(f_{m-1}(x)+h_m(x)) \approx L(f_{m-1}(x)) + L'(f_{m-1}(x))h_m(x) \]
因此,咱們只需使得知足
\[ \max L'(f_{m-1}(x))h_m(x) \\ L'(f_{m-1}(x))h_m(x)<0 \]
那咱們的 \(h_m(x)\) 到底要擬合什麼呢?別忘了,咱們是要求梯度的,在這裏咱們已知的是 \(L'(f_{m-1}(x))\) ,咱們確定是根據上一次的擬合的結果來擬合這一次的結果,因此,要使得結果最大,天然就是梯度方向。那麼 \(h_m(x)=-L'(f_{m-1}(x))\) , 這樣原先的 \(r\) 也就變成了梯度。這裏若是把損失函數看做是平方損失,咱們獲得的結果也剛好就是咱們所說的殘差!!code

此時也總算明白了以前面騰訊的時候我說 GBDT 是擬合殘差的時候面試官讓我再回去從新康康這個算法的緣由了。

算法步驟

輸入: 訓練數據集 \(T = {(x_1, y_1),(x_2, y_2), ..., (x_N, y_N)}, x_i \in X \subset R^n, y_i \in Y \subset R\); 損失函數 L(y,f(x)),樹的個數M.

輸出: 梯度提高樹\(F_M(x)\)

(1) 初始化 \(f_0(x) = argmin_c \Sigma_{i=1}^N L(y_i,c)\).

(2) 對 \(m=1,2,...,M\)

​ (a) 對\(i =1,2,...,N\),計算, \(r_{mi} = - [\frac{\partial L(y_i, f(x_i))}{\partial f(x_i)}]_{f(x) = F_{m-1}(x)}\).

​ (b) 擬合殘差\(r_{mi}\)學習一個迴歸樹,獲得\(f_m(x)\).

​ (c) 更新\(F_m(x) = F_{m-1}(x) + f_m(x)\).

(3) 獲得迴歸問題提高樹 \(F_M(x) = \Sigma_{i=0}^M f_i(x)\)

代碼

這裏代碼是採用了平方損失的方法來寫的,且解決的是分類問題

def sigmoid(x):
    """
    計算sigmoid函數值
    """
    return 1 / (1 + np.exp(-x))


def gbdt_classifier(X, y, M, max_depth=None):
    """
    用於分類的GBDT函數
    參數:
        X: 訓練樣本
        y: 樣本標籤,y = {0, +1}
        M: 使用M個迴歸樹
        max_depth: 基學習器CART決策樹的最大深度
    返回:
        F: 生成的模型
    """
    # 用0初始化y_reg
    y_reg = np.zeros(len(y))
    f = []
    
    for m in range(M):
        # 計算r
        r = y - sigmoid(y_reg)
        
        # 擬合r
        # 使用DecisionTreeRegressor,設置樹深度爲5,random_state=0
        f_m = DecisionTreeRegressor(max_depth=5, random_state=0)
        # 開始訓練
        f_m.fit(X, r)
        # 更新f
        f.append(f_m)
        
        y_reg += f_m.predict(X)
    
    def F(X):
        num_X, _ = X.shape
        reg = np.zeros((num_X))
        
        for t in f:
            reg += t.predict(X)
        
        y_pred_gbdt = sigmoid(reg)
        
        # 以0.5爲閾值,獲得最終分類結果0或1
        one_position = y_pred_gbdt >= 0.5
        y_pred_gbdt[one_position] = 1
        y_pred_gbdt[~one_position] = 0
        
        return y_pred_gbdt
    
    return F

小節

到這裏 GBDT 也就講完了,從決策樹 ID3 開始一直到 GBDT,後面終於要迎來最開始想要梳理的數據挖掘的兩大殺器 XGBoost 和 LightGBM 了,下一篇將介紹 XGBoost。

相關文章
相關標籤/搜索