集成算法往往被稱為三個臭皮匠,賽過一個諸葛亮,集成算法的起源是來自與PAC中的強可學習和弱可學習,如果類別決策邊界可以被一個多項式表示,並且分類正確率高,那么就是強學習的,如果分類正確率不高,僅僅只是比隨機猜測好一點,那么就是弱可學習,后來有人證明強可學習和弱可學習是等價的,那么弱可學習就可以提升為強可學習,集成就是其中一個方法。本文的代碼都在我的github。
引言
不過三個臭皮匠真的能賽過一個諸葛亮嗎?很明顯不是,一個科研專家並不是幾個普通人就能比得上的,很多時候,一個人能比得上千軍萬馬,而且這里的皮匠是裨將的意思,也就是副將,他們可以從不同的角度來指出問題的所在,這才能抵得上只在大營中的諸葛亮。那么多個學習器的組合,真的能獲得比最好的單一學習器更好的性能嗎?生活中總說不怕神一樣的隊友,只怕豬一樣的隊友,這里的單一學習器最好是好而不同,這就是集成算法的倆個核心,就是准確性和多樣性,那么滿足了好而不同就能更好嗎?在實驗中,訓練數據往往並不能包含全部的數據情況,在數據不充分的條件下,如果我們從不同的假設空間角度來學習或者學習到不同數據分布的假設,並且學習的都是正確的,那么就能從多角度來看待問題,這樣就能減小錯誤的風險了。所以並不是隨便幾個算法集成起來就能夠得到一個更好的結果,這幾個算法必須要多樣並且准確。
但是准確性和多樣性是存在沖突,准確率很高之后,再提升多樣性就會犧牲准確率,因為准確率高了之后,說明模型與訓練數據分布的偏差很低了,這時的模型往往就會固定成一個樣子,因而就沒有多樣性,泛化能力就降低了,方差就會變大,這也是集成算法的核心問題,偏差小了,方差自然就大,大白話就是在訓練數據上的准確率很高的話,在測試數據上的准確率就不行了,沒有很強的泛化能力,產生過擬合現象。那么從降低方差和降低偏差的倆個角度來看,自然就有了倆種思路bagging和boosting。補充一下偏差和方差,偏差指的是算法的期望預測與真實預測之間的偏差程度,反應了模型本身的擬合能力;方差度量了同等大小的訓練集的變動導致學習性能的變化,刻畫了數據擾動所導致的影響。
bagging
如果模型偏差小,方差很大,比如不剪枝的決策樹就容易過擬合,那么如何降低方差,自然要從多樣性入手,多樣性有倆種分別是數據多樣性和模型多樣性。對於數據多樣性,在面對同一堆數據,可以采用有放回的采樣,就是隨便選擇一個數據,再把數據放回到數據集,之后再選擇。這樣重復並且重疊的采樣方式產生了樣本擾動,具有了多樣性。對於模型多樣性,采用的都是性能較好的相互獨立的分類器,這里的相互獨立非常重要,這就讓模型之間沒有了依賴關系,每個人都可以從自己的角度來思考問題。那么我們就可以使用有放回的采樣得到多個數據集,用這些數據集分別訓練多個獨立模型,再將這些模型的結果結合,這就是Bagging的基本思想了。過程可以看下圖
之前講過決策樹算法,決策樹算法就是易受數據的擾動,這個其實就是決策樹過擬合的表現。之前也說過這種過擬合的表現其實也被稱為偏差小,方差大。如果對決策樹進行bagging的話,就會形成random forest。
隨機森林(random forest)
隨機森林就是以決策樹為基學習器構建bagging集成的基礎上,進一步在決策樹訓練過程中引入隨機屬性選擇。就是每個基分類器都是決策樹,但是基決策樹的特征選擇都是從該特征集合中隨機一個子集,從子集中選出最優的,這種屬性擾動生成的每個決策樹都是獨立並且不同,就能做到模型多樣性。之后使用有放回的采樣數據,形成每個決策樹的訓練數據,這樣就能做到了樣本擾動。這樣不同數據,隨機屬性生成的多個決策樹就能滿足集成算法的要求。
在之前我實現的隨機森林中,對於特征的隨機選擇,有倆種思路,一種就是每一個結點都是隨機選擇一些特征,選擇其中最好的特征,通常是log2d,還有一種就是也可以每棵樹只選擇固定的特征子集來選擇。對於實現,我的代碼中使用的就是第二種,對每棵樹選出特征子集來對樹進行創建。
if __name__ == '__main__':
dataSet,labelSet = dataSE.extractData()
#森林中數的個數
numTree = 50
#森林
forest = []
#查看每個數據的選中情況
choiceDataNum = [ 0 for i in range(len(dataSet))]
#建立每棵決策樹
for i in range(numTree):
#首先選出數據集
#print(labelSet)
childData,chidFeatures,noUseData = randomSample(dataSet,labelSet)
#建立決策樹
childTree = Tree.buildTree(childData,chidFeatures)
Tree.printTree(childTree)
forest.append(childTree)
for i in range(len(noUseData)):
choiceDataNum[i] += noUseData[i]
testData = produceData(choiceDataNum,dataSet)
results = []
correct = 0
#開始測試每個測試集
for data in testData:
results = []
for tree in forest:
result = Tree.classify(data,tree,labelSet)
#print(list(result.keys()))
results.append(list(result.keys()))
#print(results)
feaNum = 0
noFeaNum = 0
resultData = 'feafits'
#采用投票法,票數最多的就是類別
for i in range(len(results)):
#print('result[i]',results[i])
if results[i][0] == 'feafits':
feaNum += 1
else:
noFeaNum += 1
#print(feaNum,noFeaNum)
if feaNum < noFeaNum:
resultData = 'nofeafits'
print('結果是',resultData)
results.append(resultData)
print('實際結果是:',data[-1])
if resultData == data[-1]:
correct += 1
ccur = (correct / len(testData))*100
print('精確度:',ccur)
boosting
boosting方法是關注偏差,基於性能相當弱的簡單學習器構建出很強的集成。它不像bagging,各學習器都是獨立的,它的學習器之間具有很強的依賴關系,這是因為它關注的是降低偏差,所以它做的就是每次都會在上一輪基礎上更加擬合原數據,這樣就可保證偏差,既然訓練過程出來的模型能夠保證偏差,那么需要選擇方差更小的單個學習器,就是更簡單的學習器。我們也可以看成它是通過不斷地調整樣本分布來訓練學習器,使得學習器針對不同的訓練數據分布,它更像每個人就只學習一種技能,最后大家合在一起使用干活,少了誰都不行。
有一些算法是通過調整數據權重來調整樣本分布的,而數據權重是通過loss調整。但是在這個思想基礎下,有着倆個不同的想法,第一個就是如果樣本訓練的不夠好,也就是模型無法擬合這個數據,那么對於這個樣本要給予更高的權重,這類工作就是hard negative mining和focal loss,第二個想法就是從易到難學習,慢慢增加難度,最終如果還有樣本的loss依舊很大,那么認為這類樣本是離群點,強行擬合很容易造成性能的下降,這類工作就是curriculum learning和self-paced learning。可以看出這倆種算法思想基本上是完全相反,第一類認為模型擬合不好的數據,應該盡力去fit,第二種認為模型擬合不好的數據,可能是訓練的標簽有誤,這其實是對數據分布的不同假設的緣故,如果感興趣可以看看這篇文章,而我們的adaboost就是第一種。
adaboost
adaboost通過調整數據權重來不斷地調整樣本分布,並且使用的是增大分類錯誤的樣本權重,使后面的學習器更加關注那些分類錯誤的數據,按照這樣的過程重復訓練出M個學習器,最后進行加權組合,它更像我們高中時期記錯題到錯題集中,然后在錯題集上反復做的過程。具體算法過程如下圖所示
從上面看出我們需要更新數據權重,還需要得到各學習器的權重進行結合。這里就有了倆個問題,如何更新數據權重?如何確定各學習器的權重?那么我們就推導一下這個算法是如何出來的,首先假設的就是模型為加法模型,損失函數是指數損失。
先來看我們的模型函數就是\(f(x) = \sum\limits_{m=1}^M\alpha_mG_m(x)\),指數損失函數就是\(L(y,f(x)) = e^{-yf(x)}\),假設模型在m次迭代中,前m-1個系數\(\alpha\)和基學習器\(G(x)\)都是固定的,那么模型函數就會變成\(f_m(x) = f_{m-1}(x) + \alpha_mG_m(x)\),這樣的話,最小化損失函數與\(\alpha_m\)和\(G_m(x)\)有關。那么現在我們的目標就是損失函數最小的時候,求出\(\alpha_m\)和\(G_m(x)\)的值,公式表示如下
這里的N就是N個訓練數據\(((x_1,y_1),(x_2, y_2),...(x_N, y_N))\)。
我們設置\(w_i^m = e^{-y_i(f_{m-1}(x_i))}\),代入其中
之后我們將整個數據集分成倆部分,一部分是分類正確的,還有一部分是分類錯誤的
下面將之轉化成只有\(y \neq G_m(x)\)的數據
這里最終的式子可以看出最小化損失函數就是最小化帶權誤差率,誤差率就是\(\epsilon_m = \frac{\sum\limits_{y \ne G_m(x)}w_i^m}{\sum\limits_{n=1}^Nw_i^m}\),我們對最終的式子求\(\alpha_m\)導數使之等於0就能得到
式子的倆邊同時乘以\(e^{\alpha_m}\)可以得到
我們可以根據誤差率式子\(\epsilon_m = \frac{\sum\limits_{y \ne G_m(x)}w_i^m}{\sum\limits_{n=1}^Nw_i^m}\),將上面的式子進行化簡
這里我們就得到了分類器的權重\(\alpha_m = \frac{1}{2}ln\frac{1 - \epsilon_m}{\epsilon_m}\),我們再回頭看我們的模型函數\(f_m(x) = f_{m-1}(x) + \alpha_mG_m(x)\),從\(w_i^m = e^{-y_i(f_{m-1}(x_i))}\),我們可以轉化成\(w_i^{m+1} = e^{-y_i(f_m(x_i))}\),帶入其中就是
我們知道\(w_i^m = e^{-y_i(f_{m-1}(x_i))}\),帶入其中就是
最終再加入一個規范化因子\(Z_m\),使得\(w^{m+1}\)成為一個概率分布
其中規范化因子就是
這樣就得到了權重的更新公式就是\(w_i^{m+1} = \frac{w_i^m}{Z_m}e^{-y_i\alpha_mG_m(x_i)}\),我們還可以從權重公式\(w_i^{m+1} = w_i^me^{-y_i\alpha_mG_m(x_i)}\)可以看出,如果分類正確,那么\(y_i = G_m(x_i)\),那么權重的更新就是\(w_i^{m+1} = w_i^me^{-\alpha_m}\),權重就是在減少,反之就是在增大。
此時我們可以得出adaboost算法的全部過程:
- 設置每個樣本的初始權重值
- 將數據、類別和樣本權重放入弱學習器中,選出分類效果最好的
(1)根據列來循環,計算出步長,用於后面選擇閾值
(2)循環閾值來進行切分,找出最好的那個,並且記錄列,閾值,正反
(3)找出所有列中最好的那個閾值返回
補充:這里是只是根據一個維度進行切分,因而每次都是在所有列中選擇 - 根據錯誤率計算alphas,更新權重值,並且計算alpha值
- 將此分類放入弱學習器結果中
- 利用alpha和結果計算值,利用sign來查看錯誤,錯誤為0,則完成,否則繼續
下面我們再看一個例子來加深一下印象,
| 序號 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| y | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
我們的基礎學習器使用的是樹樁決策樹。首先初始化每個樣本的權重,我們使用的是平均值,那么\(w_i^{1} = 0.1\),
對m=1,之后根據權重和數據設置基學習器,因為是樹樁,我們只要找出閾值使得誤差率最低就好了,先找出所有的切分點1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,之后計算所有切分點的誤差率,找出最低誤差率的時候切分點為2.5,誤差率就是\(\epsilon_1 = \frac{0.3}{1} = 0.3\),
然后根據上面的公式\(\alpha_m = \frac{1}{2}ln\frac{1 - \epsilon_m}{\epsilon_m}\)來求我們得出的這個基分類器的權重,
之后根據上面得到的權重更新公式\(w_i^{m+1} = \frac{w_i^m}{Z_m}e^{-y_i\alpha_mG_m(x_i)}\),先計算規范化因子\(Z_m\),它的公式就是\(Z_m = \sum\limits_{i=1}^Nw_i^me^{-\alpha_my_iG_m(x_i)}\)
之后計算\(w_1^2\)
然后依次更新所有數據權重,最后得到(0.07143,0.07143,0.07143,0.07143,0.07143,0.07143,0.16667,0.16667,0.16667,0.07143),此時的分類器就是\(f_1(x) = 0.4236G_1(x)\),將這個\(sign(f_1(x))\)來測試所有的訓練集,發現錯了三個,那么繼續更新。
對於 m = 2,先計算所有切分點的誤差率,可以得到8.5的時候誤差率最低,最低\(\epsilon_2 = \frac{0.07143\times3}{0.07143\times7+0.16667\times3} = 0.2143\),和上面的過程一樣計算基分類器的權重\(\alpha_2 = 0.6496\),再更新所有數據的權重,此數據上有三個誤分類點,此時的分類器就是\(f_2(x) = 0.4236G_1(x) + 0.6496G_2(x)\),將這個\(sign(f_2(x))\)來測試所有的訓練集,發現錯了三個,那么繼續更新。
對於 m = 3,先計算所有切分點的誤差率,可以得到5.5的時候誤差率最低,最低\(\epsilon_3 = 0.1820\),和上面的過程一樣計算基分類器的權重\(\alpha_3 = 0.7514\),再更新所有數據的權重,此數據上有三個誤分類點,此時的分類器就是\(f_3(x) = 0.4236G_1(x) + 0.6496G_2(x) + 0.7514G_3(x)\),將這個\(sign(f_3(x))\)來測試所有的訓練集,發現全部正確,那么停止完成。
那么最終的分類器就是
#numIt就是循環次數,返回整個分類器
def adaboostTraining(dataSet,labelSet,numIt = 40):
dataMat = np.mat(dataSet)
ResultMat = np.mat(labelSet).T
weakResult = []
m,n = dataMat.shape
#判斷數據是否已經分類好了
result = np.mat(np.zeros((m,1)))
#設置出初始樣本權重值
Weights = np.mat(np.ones((m,1))/m)
for i in range(numIt):
#選擇最好的弱學習器
stumpClass,errorRate,classResult = choiceBestWeak(dataMat,\
ResultMat,Weights)
#計算出alphas的值,防止錯誤率為0時
alphas = float(0.5*np.log((1-errorRate)/max(errorRate,1e-16)))
stumpClass['alphas'] = alphas
#放入到結果學習器中
weakResult.append(stumpClass)
#更新樣本權重值
temp = np.multiply(-1*alphas*ResultMat,classResult)
#print('temp',temp)
tempTop = np.multiply(Weights,np.exp(temp))
Weights = tempTop/sum(Weights)
#查看是否分類完全正確,全部分類正確就完成
#print('alphas',alphas)
#print('classResult',classResult)
result += alphas*classResult
error = np.multiply(np.sign(result) != ResultMat\
,np.ones((m,1)))
errorR = error.sum()/m
#print('errorR',errorR)
if errorR == 0:
break
return weakResult
那么adaboost為什么要用指數損失函數,可不可以使用其他的損失函數?當然也是可以的,但是如果用了其他的損失函數,推導過程就不同,並且只是boosting家族中的另一個算法而已了,adaboost使用指數損失函數就是計算和表達簡單,我們可以從下表看一下換損失函數會變成什么算法。
如果使用adaboost對一些好分割的數據進行分類的話,你會發現,adaboost不會過擬合,並且還會出現一個特別奇怪的現象:當訓練集的誤差率減小到0的時候,如果你繼續增加基礎分類器,也就是讓算法繼續運行下來,它的測試誤差率也會下降。這一點周志華老師在2014年的CCL中也討論過,其中能解釋比較清楚的就是Boosting Margin,一組數據它的測試誤差率能夠達到0,說明數據集比較好分割,margin也就可以看成是它分類的置信度,也許剛開始分類的時候,它只有60%的把握分對,之后再繼續訓練,就有了80%的把握,margin就可以看成這個置信度,confidence變得越來越大,決策邊界自然也會越來越公正,自然結果也更加可靠了,后面SVM還會談到這個margin。
提升樹
提升樹是以分類樹或者回歸樹作為基本分類器的提升方法,並且有着大量的引用,一度被認為是與SVM一樣具有很強泛化能力的算法。與adaboost使用數據權重不同,普通的提升樹是利用殘差進行計算的,並且多用於回歸問題中,利用最小均方損失函數學習殘差回歸樹。對於它的過程,我們可以先來大概看一下cart回歸樹的過程,利用最小化均方誤差來找出最優屬性的最好切分點,首先就是找出一個屬性的所有切分點,然后對所有的切分點做均方誤差,找出最小誤差的那個切分點,計算所有屬性的最小切分點,得到最優屬性的最好切分點。然后將數據分成倆個分支之后,繼續在數據集上尋找最優屬性的最好切分點。普通的提升樹中的一棵回歸樹的建立和它一樣,都是不斷地利用最小化均方誤差來找出最優屬性的最好切分點,只不過提升樹是許多回歸樹,每顆回歸樹擬合的數據不同,從一開始的訓練數據到之后的殘差數據,最后的結果就是所有樹的結果加在一起。回歸結果與真實結果差距越大的數據,那么下一次殘差也越大,這其實也變相的增大了分錯的數據權重。提升樹說起來很抽象,先來整理一下過程。
- 初始化\(f_0(x) = 0\),最開始的擬合函數就是0;
- i = 0,1,2...., 進行循環計算殘差,\(r_{mi} = y_i - f_{m-1}(x_i)\);
- 通過均方誤差擬合數據\(((x_1,r_{m1}),(x_2,r_{m2}),....,(x_i,r_{mi}))\),學習到一個回歸樹,得到T(x)
(1). 找出一個屬性的所有切分點;
(2). 計算所有切分點的均方誤差;
(3). 得到均方誤差最小的那個切分點;
(4). 計算出所有屬性的最好切分點,之后找出最優屬性的最好切分點,作為節點;
(5). 直到每個葉子節點年齡都唯一或者葉子節點個數達到了預設終止條件,如果不唯一,葉子節點的值就是平均值,注意,它並不會刪除用過的屬性,會循環用; - 更新\(f_m(x) = f_{m-1}(x) + T(x)\);
- 達到循環停止條件,便得到了回歸問題提升樹\(f_M(x)\);
- 測試的結果就是所有回歸樹的結果相加。
普通提升樹的損失函數是均方誤差,殘差作為每一輪的損失,而且均方誤差能夠很好的擬合殘差,但是損失函數有很多種,這些一般性的損失函數無法很好的擬合殘差。對此,Freidman提出用損失函數的負梯度代替殘差作為每一輪損失的近似值,之后通過擬合負梯度每輪產生一個cart回歸樹。
- 初始化\(f_0(x) = arg\min\limits_c\sum\limits_{i=1}^NL(y_i,c)\),這個是選取初始值,不同損失函數初始值選取不同,因為它要讓損失函數達到最小,經常對損失函數求導等於0來找出c的求解公式,因為是argmin,所以\(f_0(x) = c\),比如均方差就是平均數;
- i = 0,1,2...., 進行循環計算梯度,\(r_{mi} = -\frac{\partial L(yi, f_{m-1}(x_i))}{\partial x_i}\);
- 通過一般誤差函數擬合數據\(((x_1,r_{m1}),(x_2,r_{m2}),....,(x_i,r_{mi}))\),學習到一個回歸樹,得到T(x)
(1). 找出一個屬性的所有切分點;
(2). 計算所有切分點的一般誤差函數;
(3). 得到一般誤差函數最小的那個切分點;
(4). 計算出所有屬性的最好切分點,之后找出最優屬性的最好切分點,作為節點;
(5). 直到每個葉子節點年齡都唯一或者葉子節點個數達到了預設終止條件,如果不唯一,葉子節點的值就是第一步中損失函數求導等於0來得到的值,注意,它並不會刪除用過的屬性,會循環用; - 更新\(f_m(x) = f_{m-1}(x) + l_rT(x)\),可以設置一個學習率\(l_r\);
- 達到循環停止條件,便得到了回歸問題提升樹\(f_M(x)\);
- 測試的結果就是所有回歸樹的結果相加。
再來看一個例子熟悉一下
| 編號 | 年齡(歲) | 體重(kg) | 身高(m)標簽值 |
|---|---|---|---|
| 0 | 5 | 20 | 1.1 |
| 1 | 7 | 30 | 1.3 |
| 2 | 21 | 70 | 1.7 |
| 3 | 30 | 60 | 1.8 |
| 4(預測) | 25 | 65 | ? |
如果我們設置生成5棵樹,並且每棵樹的最大深度為3,學習率\(l_r = 0.1\),我們使用均方差作為損失函數,這樣也能與普通提升樹聯系起來,那么下面來看過程
第一步就是初始化\(f_0(x) = arg\min\limits_c\sum\limits_{i=1}^NL(y_i,c)\),直接對均方差損失函數進行求導,並且等於0。
令導數等於0
所以初始化\(c = \frac{1.1+1.3+1.7+1.8}{4} = 1.475 = f_0(x)\)
第二步就是循環計算梯度,先計算本次的梯度就是\(-\frac{\partial L(y_i, c)}{\partial c} = y_i - c\),將之代入可以得到本次需要擬合的數據。
| 編號 | 年齡(歲) | 體重(kg) | 身高(m)標簽值 |
|---|---|---|---|
| 0 | 5 | 20 | -0.375 |
| 1 | 7 | 30 | -0.175 |
| 2 | 21 | 70 | 0.225 |
| 3 | 30 | 60 | 0.325 |
第三步就是通過一般誤差函數擬合上面得到的數據,尋找回歸樹的最佳屬性的最好切分點。先看年齡,分別查找出全部的切分點,分別是6,14,25.5,然后使用誤差函數計算誤差,切分點6的時候,左邊只有數據1,右邊有數據7,21,30,先計算出左邊的MSE,只有一個數據,那肯定就是0,再計算右邊的MSE,先計算期望,\(\frac{-0.175+0.225+0.325}{3} = 0.125\),然后再將數據與期望相減平方得到MSE,\((-0.175-0.125)^2 + (0.225-0.125)^2 + (0.325-0.125)^2 = 0.14\),再將倆邊的MSE相加就得到了切分點6的\(MSE_6 = 0.14\),按照這個步驟計算出全部屬性的全部切分點的MSE。
| 年齡切分點6 | 年齡切分點14 | 年齡切分點25.5 | 體重切分點25 | 體重切分點45 | 體重切分點65 |
|---|---|---|---|---|---|
| 0.14 | 0.025 | 0.187 | 0.14 | 0.025 | 0.260 |
選擇年齡切分點14作為節點,那么我們的樹就是
因為我設置樹的深度是3,這里才是2,那么還需要再來一次,先划分左節點,左節點的數據只有0,1,按照上面的步驟計算年齡切分點6和體重切分點25的MSE,
| 年齡切分點6 | 體重切分點25 |
|---|---|
| 0 | 0 |
我們選擇年齡切分點6,之后同樣計算右節點,同樣是選擇年齡切分點25.5,那么最后的樹就是
我們還需要計算出葉子節點的值,我們需要對損失函數求導,等於0,第一步的時候我們就計算出來
那么葉子節點的值就是平均值,我們可以計算出來,那么樹就是
第四步更新\(f_1(x) = f_{0}(x) + l_rT(x)\),可以設置一個學習率\(l_r\),\(T(x)\)就是這棵樹,如果用表達式的話,就是\(f_1(x) = f_{0}(x) + l_r\sum\limits_{j=1}^{4}\Upsilon_{j1}\)
第五步就是不斷地循環,然后直到創建了五棵樹達到了停止條件,得到最后的回歸樹,用數學表達就是
第六步就是測試數據,將測試數據放進所有的樹中,然后相加起來
如果你在競賽中使用過它,你就會發現它即使樹的深度很淺,但是依舊能夠達到很高的精度,但是隔壁的隨機森林樹的深度卻很高,你也許會認為它比隨機森林還好,其實這就是倆者的算法特性造成,隨機森林降低的就是方差,偏差有基學習器作為保障的,boosting降低的就是偏差,方差是基學習器作為保障的,既然基學習器保障方差,那么基學習器一定不會很深,不然就會過擬合了,方差就會減小的。
其實我們思考思考就會發現殘差不就是全局最優方向嘛,而且殘差就是均方差損失函數的梯度,那也說明它其實也是一種特殊的梯度。
如果懂了GBDT,那么你再看XGBoost,就能看得懂思想原理了,因為XGBoost本質上就是GBDT的一種高效的工業界實現,外加一些算法上的優化,最主要的就是GBDT是利用的梯度,XGBoost是利用的二階泰勒展開,更加准確,並且XGBoost還利用了並行化實現和缺失值的處理等等操作。
總結
以前我一直覺得集成算法可以稱得上思想和效果都很好的算法,並且從降低方差和偏差角度出發,也能理解它的基本原理,但是你在研究中使用集成算法的時候,要想集成算法效果好的話,就必須要考慮基學習器的選擇,當然在競賽中,很多時候直接使用XGBoost也能得到一個很好的結果。當然集成算法還有思考點,就是如何將所有基學習器的結果結合,這方面也有很多的方法。本文的代碼都在我的github。
