深入解析GBDT二分类算法(附代码实现)
目錄:
GBDT分類算法簡介
GBDT二分類算法
-
2.1 邏輯回歸的對數損失函數
-
2.2 GBDT二分類原理
-
GBDT二分類算法實例
-
手撕GBDT二分類算法
-
4.1 用Python3實現GBDT二分類算法
-
4.2 用sklearn實現GBDT二分類算法
-
GBDT分類任務常見的損失函數
-
總結
-
Reference
本文的主要內容概覽:
1 GBDT分類算法簡介
GBDT無論用于分類還是回歸,一直使用的是CART回歸樹。GBDT不會因為我們所選擇的任務是分類任務就選用分類樹,這里的核心原因是GBDT每輪的訓練是在上一輪訓練模型的負梯度值基礎之上訓練的。這就要求每輪迭代的時候,真實標簽減去弱分類器的輸出結果是有意義的,即殘差是有意義的。如果選用的弱分類器是分類樹,類別相減是沒有意義的。對于這樣的問題,可以采用兩種方法來解決:
-
采用指數損失函數,這樣GBDT就退化成了Adaboost,能夠解決分類的問題;
-
使用類似于邏輯回歸的對數似然損失函數,如此可以通過結果的概率值與真實概率值的差距當做殘差來擬合;
下面我們就通過二分類問題,去看看GBDT究竟是如何做分類的。
2?GBDT二分類算法
2.1 邏輯回歸的對數損失函數
邏輯回歸的預測函數為:
?
函數?的值有特殊的含義,它表示結果取??的概率,因此對于輸入??分類結果為類別??和類別??的概率分別為:
?
?
下面我們根據上式,推導出邏輯回歸的對數損失函數??。上式綜合起來可以寫成:
然后取似然函數為:
因為??和??在同一??處取得極值,因此我們接著取對數似然函數為:
最大似然估計就是求使?取最大值時的??。這里對??取相反數,可以使用梯度下降法求解,求得的??就是要求的最佳參數:
2.2 GBDT二分類原理
邏輯回歸單個樣本??的損失函數可以表達為:
其中,??是邏輯回歸預測的結果。假設第??步迭代之后當前學習器為??,將??替換為?帶入上式之后,可將損失函數寫為:
其中,第??棵樹對應的響應值為(損失函數的負梯度,即偽殘差):
對于生成的決策樹,計算各個葉子節點的最佳殘差擬合值為:
由于上式沒有閉式解(closed form solution),我們一般使用近似值代替:
補充近似值代替過程:
假設僅有一個樣本:
令??,則?
求一階導:
求二階導:
對于?? 的泰勒二階展開式:
? 取極值時,上述二階表達式中的為:
GBDT二分類算法完整的過程如下:
(1)初始化第一個弱學習器??:
其中,??是訓練樣本中??的比例,利用先驗信息來初始化學習器。
(2)對于建立??棵分類回歸樹??:
a)對??,計算第??棵樹對應的響應值(損失函數的負梯度,即偽殘差):
b)對于?,利用CART回歸樹擬合數據??,得到第??棵回歸樹,其對應的葉子節點區域為??,其中??,且??為第棵回歸樹葉子節點的個數。
c)對于?個葉子節點區域?,計算出最佳擬合值:
d)更新強學習器??:
(3)得到最終的強學習器??的表達式:
從以上過程中可知,除了由損失函數引起的負梯度計算和葉子節點的最佳殘差擬合值的計算不同,二元GBDT分類和GBDT回歸算法過程基本相似。那么二元GBDT是如何做分類呢?
將邏輯回歸的公式進行整理,我們可以得到? ,其中,也就是將給定輸入?? 預測為正樣本的概率。邏輯回歸用一個線性模型去擬合Y=1|x這個事件的對數幾率(odds)。二元GBDT分類算法和邏輯回歸思想一樣,用一系列的梯度提升樹去擬合這個對數幾率,其分類模型可以表達為:
?
3?GBDT二分類算法實例
3.1 數據集介紹
訓練集如下表所示,一組數據的特征有年齡和體重,把身高大于1.5米作為分類邊界,身高大于1.5米的令標簽為1,身高小于等于1.5米的令標簽為0,共有4組數據。
測試數據如下表所示,只有一組數據,年齡為25、體重為65,我們用在訓練集上訓練好的GBDT模型預測該組數據的身高是否大于1.5米?
3.2 模型訓練階段
參數設置:
-
學習率:learning_rate = 0.1
-
迭代次數:n_trees = 5
-
樹的深度:max_depth = 3
1)初始化弱學習器:
2)對于建立棵分類回歸樹:
由于我們設置了迭代次數:n_trees=5,這就是設置了。
首先計算負梯度,根據上文損失函數為對數損失時,負梯度(即偽殘差、近似殘差)為:
我們知道梯度提升類算法,其關鍵是利用損失函數的負梯度的值作為回歸問題提升樹算法中的殘差的近似值,擬合一個回歸樹。這里,為了稱呼方便,我們把負梯度叫做殘差。
現將殘差的計算結果列表如下:
此時將殘差作為樣本的標簽來訓練弱學習器??,即下表數據:
接著尋找回歸樹的最佳劃分節點,遍歷每個特征的每個可能取值。從年齡特征值為開始,到體重特征為結束,分別計算分裂后兩組數據的平方損失(Square Error),?為左節點的平方損失,??為右節點的平方損失,找到使平方損失和??最小的那個劃分節點,即為最佳劃分節點。
例如:以年齡為劃分節點,將小于的樣本劃分為到左節點,大于等于7的樣本劃分為右節點。左節點包括?,右節點包括樣本?,?,?,?,所有可能的劃分情況如下表所示:
以上劃分點的總平方損失最小為,有兩個劃分點:年齡和體重,所以隨機選一個作為劃分點,這里我們選年齡。現在我們的第一棵樹長這個樣子:
我們設置的參數中樹的深度max_depth=3,現在樹的深度只有,需要再進行一次劃分,這次劃分要對左右兩個節點分別進行劃分,但是我們在生成樹的時候,設置了三個樹繼續生長的條件:
-
深度沒有到達最大。樹的深度設置為3,意思是需要生長成3層;
-
點樣本數 >= min_samples_split;
-
此節點上的樣本的標簽值不一樣。如果值一樣說明已經劃分得很好了,不需要再分;(本程序滿足這個條件,因此樹只有2層)
最終我們的第一棵回歸樹長下面這個樣子:
此時我們的樹滿足了設置,還需要做一件事情,給這棵樹的每個葉子節點分別賦一個參數,來擬合殘差。
根據上述劃分結果,為了方便表示,規定從左到右為第個葉子結點,其計算值過程如下:
?
?
此時的第一棵樹長下面這個樣子:
接著更新強學習器,需要用到學習率:learning_rate=0.1,用lr表示。更新公式為:
為什么要用學習率呢?這是Shrinkage的思想,如果每次都全部加上擬合值??,即學習率為,很容易一步學到位導致GBDT過擬合。
重復此步驟,直到??結束,最后生成棵樹。
下面將展示每棵樹最終的結構,這些圖都是我GitHub上的代碼生成的,感興趣的同學可以去運行一下代碼。
https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/GBDT_GradientBoostingBinaryClassifier
第一棵樹:
第二棵樹:
第三棵樹:
第四棵樹:
第五棵樹:
3)得到最后的強學習器:
3.3 模型預測階段
-
?
-
在??中,測試樣本的年齡為,大于劃分節點歲,所以被預測為。
-
在??中,測試樣本的年齡為,大于劃分節點歲,所以被預測為。
-
在??中,測試樣本的年齡為,大于劃分節點歲,所以被預測為。
-
在??中,測試樣本的年齡為,大于劃分節點歲,所以被預測為。
-
在??中,測試樣本的年齡為,大于劃分節點歲,所以被預測為。
最終預測結果為:
4?手撕GBDT二分類算法
本篇文章所有數據集和代碼均在GitHub中,地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning
4.1 用Python3實現GBDT二分類算法
需要的Python庫:
pandas、PIL、pydotplus、matplotlib br其中pydotplus庫會自動調用Graphviz,所以需要去Graphviz官網下載graphviz-2.38.msi安裝,再將安裝目錄下的bin添加到系統環境變量,最后重啟計算機。
由于用Python3實現GBDT二分類算法代碼量比較多,我這里就不列出詳細代碼了,感興趣的同學可以去GitHub中看一下,地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/GBDT_GradientBoostingBinaryClassifier
4.2 用sklearn實現GBDT二分類算法
import numpy as np from sklearn.ensemble import GradientBoostingClassifier''' 調參: loss:損失函數。有deviance和exponential兩種。deviance是采用對數似然,exponential是指數損失,后者相當于AdaBoost。 n_estimators:最大弱學習器個數,默認是100,調參時要注意過擬合或欠擬合,一般和learning_rate一起考慮。 learning_rate:步長,即每個弱學習器的權重縮減系數,默認為0.1,取值范圍0-1,當取值為1時,相當于權重不縮減。較小的learning_rate相當于更多的迭代次數。 subsample:子采樣,默認為1,取值范圍(0,1],當取值為1時,相當于沒有采樣。小于1時,即進行采樣,按比例采樣得到的樣本去構建弱學習器。這樣做可以防止過擬合,但是值不能太低,會造成高方差。 init:初始化弱學習器。不使用的話就是第一輪迭代構建的弱學習器.如果沒有先驗的話就可以不用管由于GBDT使用CART回歸決策樹。以下參數用于調優弱學習器,主要都是為了防止過擬合 max_feature:樹分裂時考慮的最大特征數,默認為None,也就是考慮所有特征。可以取值有:log2,auto,sqrt max_depth:CART最大深度,默認為None min_sample_split:劃分節點時需要保留的樣本數。當某節點的樣本數小于某個值時,就當做葉子節點,不允許再分裂。默認是2 min_sample_leaf:葉子節點最少樣本數。如果某個葉子節點數量少于某個值,會同它的兄弟節點一起被剪枝。默認是1 min_weight_fraction_leaf:葉子節點最小的樣本權重和。如果小于某個值,會同它的兄弟節點一起被剪枝。一般用于權重變化的樣本。默認是0 min_leaf_nodes:最大葉子節點數 '''gbdt = GradientBoostingClassifier(loss='deviance', learning_rate=0.1, n_estimators=5, subsample=1, min_samples_split=2, min_samples_leaf=1, max_depth=3, init=None, random_state=None, max_features=None, verbose=0, max_leaf_nodes=None, warm_start=False)train_feat = np.array([[1, 5, 20],[2, 7, 30],[3, 21, 70],[4, 30, 60],]) train_label = np.array([[0], [0], [1], [1]]).ravel()test_feat = np.array([[5, 25, 65]]) test_label = np.array([[1]]) print(train_feat.shape, train_label.shape, test_feat.shape, test_label.shape)gbdt.fit(train_feat, train_label) pred = gbdt.predict(test_feat)total_err = 0 for i in range(pred.shape[0]):print(pred[i], test_label[i])err = (pred[i] - test_label[i]) / test_label[i]total_err += err * err print(total_err / pred.shape[0])用sklearn中的GBDT庫實現GBDT二分類算法的難點在于如何更好的調節下列參數:
用sklearn實現GBDT二分類算法的GitHub地址:https://github.com/Microstrong0305/WeChat-zhihu-csdnblog-code/tree/master/Ensemble%20Learning/GBDT_Classification_sklearn
5?GBDT分類任務常見損失函數
對于GBDT分類算法,其損失函數一般有對數損失函數和指數損失函數兩種:
(1)如果是指數損失函數,則損失函數表達式為:
其負梯度計算和葉子節點的最佳負梯度擬合可以參看Adaboost算法過程。
(2)如果是對數損失函數,分為二元分類和多元分類兩種,本文主要介紹了GBDT二元分類的損失函數。
6?總結
在本文中,我們首先簡單介紹了如何把GBDT回歸算法變成分類算法的思路;然后從邏輯回歸的對數損失函數推導出GBDT的二分類算法原理;其次不僅用Python3實現GBDT二分類算法,還用sklearn實現GBDT二分類算法;最后介紹了GBDT分類任務中常見的損失函數。GBDT可以完美的解決二分類任務,那么它對多分類任務是否有效呢?如果有效,GBDT是如何做多分類呢?這些問題都需要我們不停的探索和挖掘GBDT的深層原理。讓我們期待一下GBDT在多分類任務中的表現吧!
?
文章來源:https://mp.weixin.qq.com/s/XLxJ1m7tJs5mGq3WgQYvGw#
總結
以上是生活随笔為你收集整理的深入解析GBDT二分类算法(附代码实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Facebook 推出多模态通用模型 F
- 下一篇: 算法工程师怎样提升业务理解能力?