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