bagging集成和stacking集成(代碼篇)機器學習你會遇到的「坑」

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

我們在昨天的《bagging集成和stacking集成(理論篇)》中說,沒有任何一個模型可以勝任全部的機器學習任務,並且從理論上來講,集成學習會擴大假設空間,使得最終的模型可以達到更好的性能。那麼事實是否如此呢?在這篇文章中,我們主要把隨機森林的實踐與理論做對應。

在之前的《非參數模型(代碼篇)》中,我們觀察過未加任何限制條件的決策樹,雖然其存在著過擬合,但也並沒有對其進行對比。拿決策樹與其他模型對比時,要獲得它的最佳泛化誤差,這樣的對比才是一個有效的對比,所以我們以葉節點包含的最小樣本數作為超參數,來觀察泛化誤差的變化:

import matplotlib.pyplot as plt

import seaborn as sns

fromsklearn.model_selection import cross_validate

from sklearn import datasets

from sklearn.tree import DecisionTreeClassifieras DTC

iris = datasets.load_iris()

X = iris.data

y = iris.target

test_mse=[]

train_mse=[]

depths=range(1,20)

for d in depths:

clf =DTC(criterion='entropy',min_samples_leaf=d)

clf_dict=cross_validate(clf,X,y,cv=10,scoring='accuracy')

test_mse.append(clf_dict['test_score'].mean())

train_mse.append(clf_dict['train_score'].mean())

sns.set(style='darkgrid')

plt.plot(depths,train_mse,'b-',label='Train Accuracy')

plt.plot(depths,test_mse,'r-',label='Test Accuracy')

plt.xlabel(' minimum number ofsamples required to be at a leaf node')

plt.ylabel('Accuracy')

plt.legend()

plt.show()

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

可以發現,當葉節點包含的最小樣本個數為3時,單棵決策樹的性能達到了最優,我們就可以說決策樹在IRIS數據上的準確率最優可以達到0.967,請記住這個結果,接下來我們為iris數據構建隨機森林:

import numpy as np

import matplotlib.pyplot as plt

from sklearn import datasets

from sklearn.ensemble import RandomForestClassifieras RFC

iris = datasets.load_iris()

X = iris.data[:, :2]

y = iris.target

def make_meshgrid(x, y, h=.02):

x_min, x_max = x.min() -1, x.max() +1

y_min, y_max = y.min() -1, y.max() +1

xx, yy =np.meshgrid(np.arange(x_min, x_max, h),

np.arange(y_min,y_max, h))

return(xx, yy)

xx,yy=make_meshgrid(X[:,0],X[:,1])

clf =RFC(random_state=42)

clf.fit(X, y)

Z =clf.predict(np.c_[xx.ravel(), yy.ravel()])

Z = Z.reshape(xx.shape)

plt.figure()

plt.contourf(xx,yy,Z,cmap=plt.cm.RdBu,alpha=0.6)

for c,i,names inzip("rgb",[0,1,2],iris.target_names):

plt.scatter(X[y==i,0],X[y==i,1],c=c,label=names,edgecolor='k')

plt.title("RandomForest")

plt.legend()

plt.show()

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

這個隨機森林可能過擬合了,注意最左邊的藍點,顯然我們的隨機森林單獨為一個孤立的樣本創立了規則。

因為隨機森林有著隨機的特徵擾動,所以我們需要設置隨機數種子,確保每次結果的穩定性,在這裡將隨機數種子設置為42。隨機森林是由決策樹所構成,所以決策樹的防止過擬合手段在隨機森林中仍然使用,理論上我們可以進行剪枝,實踐中可以通過限制葉節點的最小樣本數。樹的最大深度,以及葉節點的個數來對隨機森林進行調節,但是比起這些,集成學習組合了大量的決策樹,決策樹的數量會成為一個很有意義的超參數。

有些人會想到,我們選取的決策樹數量越多越好,因為bagging集成就要結合大量學習器。但這種看法是錯誤的,因為隨著學習器數量的增加,我們很難保證模型的差異化足夠大,集成學習反而會變得很糟糕,模型差異化是集成學習最重要的問題,根據主要的三種獲得差異化的手段,基學習器的數量要隨著樣本量,特徵數和學習器本身的參數分佈來決定。

比如,以基學習器的數量作為超參數,將葉節點包含的最小樣本數設置為3(與單棵決策樹一樣),來觀察決策邊界的變化:

.....

numbers=[1,5,10,50]

sns.set(style='darkgrid')

for k,j in enumerate(numbers):

clf =RFC(n_estimators=j,min_samples_leaf=3)

clf.fit(X,y)

Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

Z = Z.reshape(xx.shape)

plt.subplot(len(numbers)/2,len(numbers)/2,k+1)

plt.contourf(xx,yy,Z,cmap=plt.cm.RdBu,alpha=0.6)

for i,v,l in [[0,'r','setosa'],[1,'g','vericolor'],[2,'b','virginica']]:

plt.scatter(X[y==i][:,0],X[y==i][:,1],c=v,label=l,edgecolor='k')

plt.title('$n=$%s'%j)

plt.legend()

......

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

如圖,我們可以看到隨著基學習器的增加,決策邊界的變化率也會緩慢。當基學習器從1變到5時,決策邊界的變化非常明顯,但基學習器從10變到50時,決策邊界幾乎不再變化。

其中的原因與我們的隨機森林的構建方式有關,有兩點至關重要:

  • IRIS數據只有四個,而我們為了可視化,也只選取了兩個特徵,只有兩個特徵的情況下,所謂的特徵擾動法幾乎不會對決策樹的差異化做出貢獻,所以少量的決策樹就可以保證很好的差異化,繼續增加決策樹,只是在增加重複的決策樹,並不會對最後的結果做出很大的優化。
  • 在隨機森林的框架下,只包含一個決策樹時,因為添加了特徵和數據擾動,所以性能比不上單棵決策樹的效果,但隨著基學習器數目的增加,隨機森林的性能會很快超過單棵決策樹。

同樣根據理論,隨機森林的性能會隨著基學習器的增加而增加,但是會增加的越來越緩慢,而且集成學習的特性也決定了我們在訓練集上和測試集上都會出現相同的趨勢,因為我們繼續來驗證這一點:

......

iris = datasets.load_iris()

X = iris.data

y = iris.target

test_mse=[]

train_mse=[]

numbers=range(1,50)

for d in numbers:

clf =RFC(n_estimators=d,min_samples_leaf=3)

clf_dict=cross_validate(clf,X,y,cv=5,scoring='accuracy')

test_mse.append(clf_dict['test_score'].mean())

train_mse.append(clf_dict['train_score'].mean())

sns.set(style='darkgrid')

plt.plot(numbers,train_mse,'b-.',label='Train Accuracy')

plt.plot(numbers,test_mse,'r-.',label='Test Accuracy')

plt.xlabel(' n estimators')

plt.ylabel('Accuracy')

plt.legend()

plt.show()

......

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

如圖,隨著基學習器的增加,隨機森林的訓練和測試誤差都趨於穩定,在基學習器為1時,我們可以注意到隨機森林的性能很低,隨著基學習器數量的增加,性能提升的非常快,但增加到某個值後,隨機森林的性能幾乎不再提升。

盲目的增加基學習器雖然不會明顯使得性能降低,但卻會帶來運算效率的問題,所以我們一般根據選取使性能穩定下來的最小基學習器的數目,隨之而來的一個問題則是,隨機森林在性能達到穩定時,準確率也低於0.97。

此處提供兩個方向參考:一方面,對於隨機森林的每個基學習器,能夠限制的葉節點的最小樣本數並不一定和單棵決策樹相同,因為數據擾動和特徵擾動都會影響這一結果;另外,可以使用一種被普遍證明良好的技巧,在構建隨機森林的時候,在當前節點的特徵集挑選出一個特徵子集,我們挑選的最佳特徵不會在整個特徵集中搜索,而是在這個子集中,這個特徵子集的規模普遍推薦大小是特徵數的對數,我們嘗試使用這一想法:

......

clf=RFC(n_estimators=d,max_features='log2',min_samples_leaf=3)

......

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

如圖,對葉節點不加限制的話,訓練集的泛化誤差就會降到零,體現為每個樣本都會預測對,準確率為1,而測試集整體的準確率卻下降了,間隔變大,再一次證明了限制葉節點會降低過擬合的風險;另一方面,我們限制了節點的特徵子集的規模。

可以發現,此時測試準確率會達0.974,比起單棵決策樹雖有提升,但這樣的提升還是太小,一方面我們不得不承認單棵決策樹可能就已經可以非常好的適應數據,另一方面我們試著繼續提升集成性能。在此基礎上,對隨機森林增大隨機性,也就是繼續想辦法增大模型的差異性,使用一種叫做Extremely Randomized Trees的方法,它本質也是隨機森林,但是在每次節點劃分的時候,普通的隨機森林會在屬性上選擇最佳值,而Extremely Randomized Trees在選擇劃分上是完全隨機的,這樣而來的基學習器性能差異是非常大的,理論上性能也會更好。

from sklearn.ensemble import ExtraTreesClassifier as ETC

......

test_mse=[]

train_mse=[]

numbers=range(1,50)

for d in numbers:

clf=ETC(n_estimators=d,min_samples_leaf=3,bootstra=True)

clf_dict=cross_validate(clf,X,y,cv=5,scoring='accuracy')

test_mse.append(clf_dict['test_score'].mean())

train_mse.append(clf_dict['train_score'].mean())

......

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

我們可以看到測試誤差和普通的隨機森林相差不多,但是測試集性能整體有提升,即對於一定數量的基學習器,性能均有所提高。

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”

讀芯君開扒

課堂TIPS

• 集成學習與一般的機器學習算法的不同點還體現在分集測試上,如果我們採用bootstrap來採樣,會有一部分數據未進入訓練集,所以可以考慮包外估計,我們需要設置模型的oob_score=True,這樣模型會對使用過的數據做記錄。本文未採用包外估計,讀者可自己實踐。

• 在實踐隨機森林的時候,我未設置隨機數種子,因為隨機性越大的隨機森林,模型的差異也會越明顯,在實踐過程中,可以多設置幾遍隨機數種子,會發現每次的結果都有差異,這也是我們需要考慮的因素。

• 一般地,我們會認為隨著模型複雜度的升高,數據不變的情況下,就一定會出現過擬合。但可以發現隨著基學習器數量的增長,並沒有出現所謂的過擬合,而是訓練集和測試集都會趨於平穩,因為基學習器的數量並不會增加整體的複雜度,也並沒有改變模型的偏差,而是降低了模型的方差,我們會在下一章節講解其中的道理並且引出另一種非常強大的集成框架Boosting。

bagging集成和stacking集成(代碼篇)機器學習你會遇到的“坑”


分享到:


相關文章: