前言
本來應該是年后就要寫的一篇博客,因為考完試后忙了一段時間課設和實驗,然后回家后又在摸魚,就一直沒開動。趁着這段時間只能呆在家里來把這些博客補上。在之前的文章中介紹了 Random Forest 和 AdaBoost,這篇文章將介紹介紹在數據挖掘競賽中,最常用的算法之一 —— GBDT(Gradient Boosting Decision Tree)。
GBDT
原理
GBDT
實際上是 GBM(Gradient Boosting Machine)
中的一種,采用 CART 樹作為基學習器,所以稱為 GBDT。與 AdaBoost 一樣,GBDT 為 Boosting 家族中的一員。其表達式為
其中\(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))\) 的值盡可能的小。
我們先將損失函數假設為最常用的平方損失
令 \(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\)
到這里,似乎發現了點什么,我們對損失函數求導,發現:
看出什么來了沒?對其取個負號就變成 \(r-h_m(x)\) ,即我們常說的殘差
,所以,當為平方損失函數時,弱學習器擬合的目標為之前擬合結果的殘差。那到這里代碼就很好寫了,但是,我們實際中有很多其它的損失函數,而且在很多問題中,這些損失函數比平方損失要好得多。那這時候,如果我們還采用同樣的思路,那就沒辦法像上面那樣直接展開並擬合殘差了,這時候該怎么辦?
這里別忘了,我們最終的目標是使得 \(L(y, f_m(x))\) 最小,那么只需要保證 \(L(y, f_{m-1}(x)+h_m(x))\) 的值比 \(L(y, f_{m-1}(x))\) 小就好了。
即
檢驗大一高數學的怎么樣的時候到了 orz
我們前面說了第 m 輪迭代的損失函數為 \(L(y, f_{m-1}(x) + h_m(x))\) ,換一種形式,寫成 \(L(f_{m-1}(x) + h_m(x))\) ,對其進行一階泰勒展開,得
所以,我們只需使得滿足
那我們的 \(h_m(x)\) 到底要擬合什么呢?別忘了,我們是要求梯度的,在這里我們已知的是 \(L'(f_{m-1}(x))\) ,我們肯定是根據上一次的擬合的結果來擬合這一次的結果,所以,要使得結果最大,自然就是梯度方向。那么 \(h_m(x)=-L'(f_{m-1}(x))\) , 這樣原先的 \(r\) 也就變成了梯度。這里如果把損失函數看作是平方損失,我們得到的結果也恰好就是我們所說的殘差!!
此時也總算明白了之前面騰訊的時候我說 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。