機器學習入門:極度舒適的GBDT原理拆解
機器學習入門:極度舒適的GBDT拆解
本文旨用小例子+視覺化的方式拆解GBDT原理中的每個步驟,使大家可以徹底理解GBDT
Boosting→Gradient Boosting
Boosting是整合學習的一種基分類器(弱分類器)生成方式,核心思想是通過迭代生成了一系列的學習器,給誤差率低的學習器高權重,給誤差率高的學習器低權重,結合弱學習器和對應的權重,生成強學習器。
Boosting演演算法要涉及到兩個部分,加法模型和前向分步演演算法。
加法模型就是說強分類器由一系列弱分類器線性相加而成。一般組合形式如下:
$$F_M(x;P)=\sum_{m=1}^n\beta_mh(x;a_m)$$
其中,$h(x;a_m)$就是一個個的弱分類器,$a_m$是弱分類器學習到的最優引數,$β_m$就是弱學習在強分類器中所佔比重,P是所有$α_m$和$β_m$的組合。這些弱分類器線性相加組成強分類器。
前向分步就是說在訓練過程中,下一輪迭代產生的分類器是在上一輪的基礎上訓練得來的。也就是可以寫成這樣的形式:
$$F_m (x)=F_{m-1}(x)+ \beta_mh_m (x;a_m)$$
Gradient Boosting = Gradient Descent + Boosting
Boosting 演演算法(以AdaBoost為代表)用錯分資料點來識別問題,通過調整錯分資料點的權重來改進模型。Gradient Boosting通過負梯度來識別問題,通過計算負梯度來改進模型。
Gradient Boosting每次迭代的目標是為了減少上一次的殘差,在殘差減少的梯度(Gradient)方向上建立一個新的模型,每個新的模型的建立是使之前模型的殘差往梯度方向減少。
第t輪的第i個樣本的損失函式的負梯度為:
$$
\large {r_{mi}} = -\left[\frac{\partial L(y_i,f(x_i))}{\partial f(x_i)} \right]{f(x)=f{m-1}(x)}
$$
此時不同的損失函式將會得到不同的負梯度,如果選擇平方損失
$L(y_i,f(x_i)) = \frac{1}{2}(y_i - f(x_i))^2$
負梯度為$r_{mi} = y_i - f(x_i)$
此時我們發現GBDT的負梯度就是殘差,所以說對於迴歸問題,我們要擬合的就是殘差。
GBDT迴歸演演算法
輸入是訓練集樣本$T={(x_,y_1),(x_2,y_2), ...(x_m,y_m)}$, 最大迭代次數T, 損失函式L。
輸出是強學習器$f(x)$
- 初始化弱學習器
- 對迭代輪數t=1,2,...T有:
$f_0(x) = \underbrace{arg; min}{c}\sum\limits{i=1}^{m}L(y_i, c)$
a)對樣本$i=1,2,...m$,計算負梯度
$$r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial f(x_i)}\bigg]{f(x) = f{t-1};;(x)}$$
b)利用$(x_i,r_{ti});; (i=1,2,..m)$, 擬合一顆CART迴歸樹,得到第t顆迴歸樹,其對應的葉子節點區域為$R_{tj}, j =1,2,..., J$。其中J為迴歸樹t的葉子節點的個數。
c) 對葉子區域$j =1,2,..J$,計算最佳擬合值
$$c_{tj} = \underbrace{arg; min}{c}\sum\limits{x_i \in R_{tj}} L(y_i,f_{t-1}(x_i) +c)$$
d)更新強學習器
$$f_{t}(x) = f_{t-1}(x) + \sum\limits_{j=1}^{J}c_{tj}I(x \in R_{tj})$$
3) 得到強學習器f(x)的表示式
$$f(x) = f_T(x) =f_0(x) + \sum\limits_{t=1}{T}\sum\limits_{j=1}{J}c_{tj}I(x \in R_{tj})$$
二元GBDT分類演演算法
對於二元GBDT,如果用類似於邏輯迴歸的對數似然損失函式,則損失函式為:
$L(y, f(x)) = log(1+ exp(-yf(x)))$
其中y∈{?1,+1}。則此時的負梯度誤差為
$$r_{ti} = -\bigg[\frac{\partial L(y, f(x_i)))}{\partial f(x_i)}\bigg]{f(x) = f{t-1};; (x)} = y_i/(1+exp(y_if(x_i)))$$
對於生成的決策樹,我們各個葉子節點的最佳負梯度擬合值為
$$c_{tj} = \underbrace{arg; min}{c}\sum\limits{x_i \in R_{tj}} log(1+exp(-y_i(f_{t-1}(x_i) +c)))$$
由於上式比較難優化,我們一般使用近似值代替
$$c_{tj} = \sum\limits_{x_i \in R_{tj}}r_{ti}\bigg / \sum\limits_{x_i \in R_{tj}}|r_{ti}|(1-|r_{ti}|)$$
除了負梯度計算和葉子節點的最佳負梯度擬合的線性搜尋,二元GBDT分類和GBDT迴歸演演算法過程相同。
小例子+視覺化理解GBDT
上面對原理進行了分析之後,大致對GBDT有了一定的認識,為了更加形象的解釋GBDT的內部執行過程,這裡引用《統計學習方法》中adaboost一節中的案例資料來進行進一步分析。強烈建議大家對比學習,看一下Adaboost和 GBDT 的區別和聯絡。
資料集如下:
採用GBDT進行訓練,為了方便,我們採用MSE作為損失函式,並且將樹的深度設為1,決策樹個數設為5,其他引數使用預設值
import numpy as np
import pandas as pd
from sklearn import tree
import matplotlib.pyplot as plt
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
X = np.arange(1,11)
y = np.array([5.56, 5.70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05])
display(X,y)
gbdt = GradientBoostingRegressor(n_estimators=5,max_depth=1)
gbdt.fit(X.reshape(-1,1),y)
其中GradientBoostingRegressor主要引數如下
GradientBoostingRegressor(alpha=0.9, criterion='friedman_mse', init=None,
learning_rate=0.1, loss='ls', max_depth=1,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=5,
n_iter_no_change=None, presort='auto',
random_state=None, subsample=1.0, tol=0.0001,
validation_fraction=0.1, verbose=0, warm_start=False)
其他引數為決策樹引數,大家應該已經很熟悉了,不再贅述。
下面我們根據GBDT迴歸演演算法原理,開始分步硬核拆解:
第一步:根據初始化公式
$f_0(x) = \underbrace{arg; min}{c}\sum\limits{i=1}^{m}L(y_i, c)$
可以計算出$F_{0}(x)=7.307$(本例中,恰好為yi均值)
第二步:計算損失函式的負梯度值:
$$r_{ti} = -\bigg[\frac{\partial L(y_i, f(x_i)))}{\partial f(x_i)}\bigg]{f(x) = f{t-1};; (x)}$$
由於是MSE損失,上式等於$\hat{y}i = y_i - F{m-1}(x_i)$,結果如下:
#計算殘差
y - y.mean()
[out]:
array([-1.747, -1.607, -1.397, -0.907, -0.507, -0.257, 1.593, 1.393,
1.693, 1.743])
第三步:對上面殘差擬合第一棵樹
根據所給的資料,可以考慮的切分點為1.5、2.5、3.5、4.5、5.5、6.5、7.5、8.5、9.5分別計算$y_i - F_{0}(x_i)$的值,並計算出切分後的左右兩側加和MSE最小的切分,最後得到的是6.5
找到最佳的切分點之後,我們可以得到各個葉子節點區域,並計算出$R_{jm}$和$\gamma_{jm}$.此時,$R_{11}$為$x$小於6.5的資料,$R_{21}$為x大於6.5的資料。同時,
$$r_{11} = \frac{1}{6} \sum_{x_i \in R_{11}} y_{i}=-1.0703$$
$$r_{21} = \frac{1}{4} \sum_{x_i \in R_{21}} y_{i}=1.6055$$
print((y - y.mean())[:6].mean(),
(y - y.mean())[6:10].mean())
[out]:-1.07 1.605
#計算mse
print(
((y - y.mean())**2).mean(),
((y[:6] - y[:6].mean())**2).mean(),
((y[6:10] - y[6:10].mean())**2).mean())
[out]
1.911421 0.309689 0.0179686
第一棵樹的視覺化
tree.plot_tree(gbdt[0,0],filled=True)
最後:更新$F_{1}(x_i)$的值
$F_1(x_i)=F_{0}(x_i)+ \rho_m \sum^2_{j=1} \gamma_{j1} I(x_i \in R_{j1})$,其中$\rho_m$為學習率,或稱shrinkage,目的是防止預測結果發生過擬合,預設值是0.1。
至此第一輪迭代完成,後面的迭代方式與上面一樣,
本例中我們生成了5棵樹,大家可以用tree.plot_tree視覺化其他樹
課後作業,大家可以思考一下,第二棵樹中的value是如何計算出來的?其實很簡單哈????
迭代$m$次後,第$m$次的$F_{m}(x)$即為最終的預測結果。
$$
F_{m}(x) = F_{m-1}(x) + \rho_{m} h(x; a_m)$$
參考
https://www.cnblogs.com/pinard/p/6140514.html
https://blog.csdn.net/u014168855/article/details/105481881
https://www.csuldw.com/2019/07/12/2019-07-12-an-introduction-to-gbdt/