<EYD與機器學習>七:Ensemble Learning and Random Forests

各位知乎兒大家好,這是<EYD與機器學習>專欄的第七篇文章,這篇文章以《Hands-on Machine Learning with Scikit-Learn and TensorFlow》(後面簡稱為HMLST)第七章的內容為主線,其間也會加入我們成員的一些感受和想法與大家分享。

這一章主要介紹了集成學習的原理及具體的多種方法實現,重點講解了以決策樹為基學習器集成的隨機森林模型的構建及應用。

第七章:Ensemble Learning and Random Forests

我們先來看實際生活中的一個例子,假設你向隨機選取的幾千個人提出一個複雜的問題,然後整理匯總他們的答案,通過這樣方式得到的答案有的時候比直接從專家那得到的答案更好,這就是集體的智慧。把這種思想應用到機器學習演算法上可以這麼理解,如果你得到一組不同分類器的分類結果或一組不同回歸器的預測值,然後整理匯總,那麼很可能會得到比其中最佳個體的分類或預測結果更好的結果,通常把這種技術稱為集成學習(Ensemble Learning),集成學習演算法稱為集成方法。

例如,首先訓練一組決策樹分類器,每個分類器在訓練集的不同隨機子集進行訓練,然後利用所有分類器分別進行預測,將出現最多次的那個預測結果作為最終的結果。這樣的基於決策樹集成學習演算法稱為隨機森林,儘管它看起來很簡單(當然是在熟練使用決策樹的前提下),但它是目前最強大的機器學習演算法之一。

本章我們重點討論常用的幾種集成方法及具體實現,包括bagging, boosting, stacking等。

7.1 投票法(Voting Classifiers)

我們在日常生活中,經常會遇到利用投票的方式來進行決策,通常按照少數服從多數的原則來執行。對機器學習的分類任務來說,最常用的集成學習策略也是投票法。以具體的例子來看這種方法的具體實現。假設你已經訓練了一些分類器,且每個分類器的準確率都達到了80%左右,這些分類器可能為:一個Logistic回歸分類器,一個SVM分類器,一個隨機森林分類器,一個K-近鄰分類器,等等 。如下圖所示。

訓練不同的分類器(HMLST figure 7-1)

所以如果你想得到更高準確率的分類器,可以將這些個體分類器的預測結果中出現最多次(即票數最多的類標記預測)的結果作為最終的預測結果,這種基於類標記的多數投票分類器稱為「硬投票分類器」分類過程示意圖如下。還有一種「軟投票分類器」原理類似,不同在於它是基於類概率來進行投票。

硬投票分類器預測(HMLST figure 7-2)

通常來說,這個投票分類器能夠得到比最好的個體分類器更高的準確率。事實上,對弱學習器來說,這種性能的提升表現的更加顯著,弱學習器(weak learner)一般指性能略優於隨機猜測的學習器,例如在二分類問題準確率略高於50%的分類器。因此集成學習的很多理論研究都是針對弱學習器進行的,一般來說,如果有足夠且多樣的弱學習器,是可以通過集成方法得到性能良好的強學習器的。

這裡你可能會有疑惑,在生活中,如果把好壞不同的東西混在一起,那麼通常得到的是比最壞的好一些,比最好的要壞一些,而集成學習把多個學習器結合起來為什麼效果比最好的個體學習器要好呢?

舉個例子來說明這個問題。假設現在有1000個分類器,這些分類器單獨的準確率都為51%,然後利用投票法來對所有分類器進行集成,將預測出現最多次的類別作為最終預測,通過數學計算,正確的類別處於多數的概率約為75%,所以硬投票分類器的準確率能達到75%,比個體分類器好得多。不過,上面這種情況只在各個分類器完全獨立時才成立,但是在現實任務中,個體學習器是為解決同一個問題訓練出來的,是在相同的數據上進行訓練的,這樣就導致它們可能會犯相同類型的錯誤。

所以當個體學習器儘可能相互獨立時,集成學習效果最好。獲得不同學習器的一種方法是使用不同的的學習演算法來訓練數據,這種方法可以降低它們犯相同類型錯誤的可能性,從而提高了集成學習器的準確性。下面給出了利用Scikit-Learn訓練三個常用分類器的部分代碼和投票法結果。結果顯示VotingClassifier略優於所有個體分類器。

from sklearn.ensemble import RandomForestClassifierfrom sklearn.ensemble import VotingClassifierfrom sklearn.linear_model import LogisticRegressionfrom sklearn.svm import SVC......LogisticRegression 0.864RandomForestClassifier 0.872SVC 0.888VotingClassifier 0.896

這裡使用的是硬投票分類器,如果所有分類器都能夠估計類概率,那麼可以通過預測最高類概率的方法來獲得最終預測,這種投票法稱為「軟投票法」,這種基於類概率集成預測的方法性能通常比「硬投票法"好,因為它相當於給予了預測類概率更高的分類器更高的權重,提高了準確率。利用上面訓練好的那些分類器進行試驗,軟投票確實效果更好。

7.2 Bagging and Pasting

為了使個體學習器儘可能獨立,即儘可能具有較大的差異,除了使用不同的訓練演算法來訓練,還可以使用相同的演算法在訓練集的不同隨機子集上進行訓練得到不同的個體學習器。因為訓練數據不同,所以獲得的基學習器很可能具有較大的差異。通過對訓練集進行隨機採樣來獲得不同的子集,而根據採樣過程的不同可以分為Bagging和Pasting兩種方法,兩者的區別在於採樣的子集中是否有相同樣本,即是否「有放回採樣(bootstrap)」。換句話說,就是Bagging和Pasting都允許訓練樣本在多個學習器中被採樣,但是只有Bagging允許樣本在同一個學習器中多次出現,它的採樣和訓練過程如下圖所示。

Pasting/Bagging的採樣和訓練(HMLST figure 7-4)

一旦所有的學習器都訓練完成以後,集成學習就可以通過簡單地匯總所有學習器的預測來對新樣本進行預測,集成函數通常為用於分類的統計模式(即少數服從多數,就像硬投票分類器那樣)或回歸預測的平均值。雖然每個個體學習器在子集上訓練的偏置都比在原始訓練集上訓練的偏差高,但是集成學習能減小偏差和方差。一般來說,最終集成學習的結果與·原始數據集上的單個學習器相比,具有相似的偏置和更低的方差。

另外,從圖HMLST figure 7-4可以看出,訓練基學習器的過程是可以利用不同CPU內核並行實現的,同樣的,預測也可以並行實現。所以Bagging和Pasting效率很高,因此這兩種方法非常受歡迎。

7.2.1 Bagging and Pasting in Scikit-Learn

Scikit-Learn為Bagging和Pasting提供了一個BaggingClassifier類(或BaggingRegressor用於回歸)。代碼如下:

from sklearn.ensemble import BaggingClassifierfrom sklearn.tree import DecisionTreeClassifierbag_clf = BaggingClassifier( DecisionTreeClassifier(), n_estimators=500, max_samples=100, bootstrap=True, n_jobs=-1 )bag_clf.fit(X_train, y_train)y_pred = bag_clf.predict(X_test)

這裡實現的是利用Bagging方法來集成500個決策樹分類器,其中每個分類器在隨機有放回採的訓練集子集上訓練,每個子集包含100個樣本,如果想使用Pasting只需要將bootstrap設為False, 參數n_jobs決定了用於訓練和預測的CPU內核數量,設為-1表示使用所有的可用內核。另外,如果基分類器可以估計類別的概率(如決策樹),那麼BaggingClassifier將自動執行軟投票而不是硬投票。

下圖繪出了單個決策樹的決策邊界與500個決策樹集成後的決策邊界,兩者都在moons數據集上訓練。比較發現,Bagging後的預測比單個決策樹具有更好的推廣能力,因為它具有與單個決策樹相似的偏差和更小的方差。

決策邊界(HMLST figure 7-5)

Bagging和Pasting的主要區別在於採用了bootstrap的採樣方法,這樣使得每個學習器訓練的子集的多樣性增加,所以Bagging最終的偏差比Pasting略高,但是也意味著學習器之間的相關性降低,使得方差減小。總體而言,Bagging通常會產生更好的模型,所以首選Bagging。當然,也可以使用交叉驗證的方式來評估兩種方法,並採用效果更好的方法。

7.2.2 包外估計(Out-of-Bag Evaluation)

對於Bagging來說,對於單個學習器,有些樣本可能會被抽樣幾次,有些樣本可能一次都沒被抽到。對每個學習器來說,Bootstrap採樣方法會使得平均只有63%左右的樣本被採樣,剩下的37%未被採樣的樣本稱為包外(out-of-bag)樣本, 當然每個學習器的包外樣本並不相同。因為這些包外樣本在訓練期間沒有使用過,所以可以用這些樣本來進行評估,這樣就不需要再考慮建立驗證集或進行交叉驗證,還可以通過對所有個體學習器的評估結果進行平均來評估集成後的學習器。

在Scikit-Learning中提供的BaggingClassifier類中,可以通過設置參數oob_score=True來使得模型在訓練後自動進行包外估計。

事實上,包外樣本還有許多其他用途。例如當基學習器是決策樹時,可使用包外樣本來輔助剪枝,或用於估計決策樹中各節點的後驗概率以輔助對零訓練樣本節點的處理;當基學習器是神經網路時,還可以使用包外樣本來輔助早期停止以減小過擬合風險。

7.3 隨機補丁和隨機子空間(Random Patches and Random Subspaces)

前面講了通過對訓練集進行隨機採樣來獲得不同的子集用於訓練基學習器,這個過程中保留了數據的全部特徵。而這裡考慮的是對數據的特徵進行隨機採樣,然後使用特徵子集來對基學習器進行訓練。當處理高維數據或包含大量冗餘屬性的數據時,在特徵子集上訓練個體學習器不僅能產生多樣性大的基學習器,還會因數據的維度減少而大幅節省時間開銷。其中對樣本和特徵都進行採樣的方法稱為隨機補丁(Random Patches)方法,僅對特徵進行採樣而保留所有樣本的方法稱為隨機子空間(Random Subspaces)方法。

對特徵進行採樣的方法會使得基學習器間的多樣性增加,差異性增強,從而導致集成後有略高的偏差和較低的方差。

7.4 隨機森林(Random Forests)

隨機森林(Random Forests)是Bagging的一個擴展變體,它是在以決策樹為基學習器構建Bagging集成的基礎上,進一步在決策樹的訓練過程中引入了隨機屬性選擇。具體來說,傳統決策樹在選擇劃分屬性時是在當前節點的屬性集合中選擇一個最佳屬性,而在隨機森林中是對基決策樹的每個節點,先從該節點的屬性集中隨機選擇一個包含k個屬性的子集,然後再從這個屬性子集中選擇一個最優屬性用於劃分。 像Bagging一樣,隨機森林也會導致各個決策樹間的多樣性增加,即獲得比單個決策樹略高的偏差和更低的方差。

Scikit-Learn專門提供了一個RandomForestClassifier類(RandomForestRegressor類用於回歸任務)來實現隨機森林,而不用先構建BaggingClassifier類再將其結果傳遞DecisionTree-Classifier類,也相當於對決策樹進行了優化,用起來更方便。當然RandomForestClassifier一般具有DecisionTreeClassifier的所有超參數(除極少數例外)以及BaggingClassifier的所有超參數來控制集成的學習器。以下代碼實現了使用所有可用CPU內核來訓練以500個決策樹為基分類器的隨機森林分類器。

from sklearn.ensemble import RandomForestClassifierrnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1)rnd_clf.fit(X_train, y_train)y_pred_rf = rnd_clf.predict(X_test)

總的來說,隨機森林思想簡單、容易實現、計算成本小,但是它在很多現實任務中表現出了強大的性能,甚至被譽為代表集成學習技術的方法。我們從原理可以看出,隨機森林集成方法是在Bagging的基礎上做了一點小改動, 但是性能上有很大的提升。這是因為Bagging中基學習器的多樣性僅通過樣本擾動(對初始訓練集採樣)而來,而隨機森林中基學習器的多樣性不僅來自樣本擾動,還來自屬性擾動,這就導致最終集成的學習器的泛化性能由於個體學習器之間差異度的增加而進一步提升。

另外值得一提的是,隨機森林的訓練效率常優於Bagging,因為在個體決策樹的構建過程中,Bagging使用的是確定型決策樹,在選擇劃分屬性時要對節點的所有屬性進行考察,而隨機森林使用的隨機型決策樹則只需考察一個屬性子集。

7.4.1 極端隨機樹(Extra Trees)

極端隨機樹(Extremely Randomized Trees)集成方法與隨機森林集成方法非常類似,它們主要有兩點區別:首先是隨機森林應用的是Bagging模型,而Extra Trees是使用所有的訓練樣本來得到每棵決策樹,也就是每棵決策樹應用的是相同的全部訓練樣本;其次是隨機森林是在一個隨機屬性子集中搜索一個最佳屬性進行分裂,而Extra Trees是在隨機子集中隨機選擇一個屬性進行分裂。所以Extra Trees比隨機森林更加隨機,所以同樣也是具有隨機森林相似的偏差更低的方差。而且它比隨機森林訓練快得多,因為它不需要為每個節點搜索最佳屬性,而這個搜索過程通常是決策樹中最耗時的過程之一。

Scikit-Learn提供了一個ExtraTreesClassifier類來直接創建Extra Trees分類器,它的API與隨機森林的基本相同。事實上,很難預先知道Extra Trees的性能是否比隨機森林更好或更差,通常可以通過交叉驗證來比較兩種方法的優劣,在這個過程中還可以使用網格搜索的方法來調整超參數。

7.4.2 特徵重要性

在上一章講到的決策樹中,更重要的特徵通常位於更接近於根的位置,而不是很重要的特徵通常更接近葉的位置,所以可以通過計算特徵在隨機森林中出現的平均深度來估計某個特徵的重要性。在Scikit-Learn提供的RandomForestClassifier類中會在訓練後自動計算每個特徵的重要性。所以可以使用隨機森林來進行實際任務中非常重要的特徵選擇過程,因為它能非常快速的了解哪些特徵很重要,哪些特徵不重要。

7.5 Boosting

前面講到的Bagging等方法都是在個體學習器不存在強依賴關係可同時生成的並行化方法,而主要針對個體學習器存在強依賴關係、必須串列生成這一類情形的常用集成方法為Boosting。Boosting具體指的是一族可將弱學習器提升為強學習器的演算法。這族演算法的工作機製為:先從初始訓練集中訓練出一個基學習器,再根據基學習器的表現對訓練樣本分布進行調整,使得先前基學習器做錯的訓練樣本在後續學習過程中得到更多的關注,然後基於調整後的樣本分布來訓練下一個基學習器,如此重複進行,直至基學習器數目達到事先設定的數量,最終將這些基學習器進行加權結合。目前最流行的Boosting方法是AdaBoost(Adaptive Boosting的簡寫)和Gradient Boosting。

7.5.1 AdaBoost

AdaBoost是基於Boosting思想的機器學習演算法,它是一種迭代型的演算法,其核心思想是針對同一個訓練集訓練不同的學習器,即弱學習器,然後將這些弱學習器整合成一個更強的最終學習器。

為了構造出一個強學習器,首先需要選定一個弱學習器,並利用同一個訓練集不斷訓練弱學習器,以提升弱學習器的性能。在AdaBoost演算法中有兩個非常重要的權重,第一個是訓練集中每個樣本的權重,在訓練過程中調整樣本的權重,賦予前面學習器些容易出錯的樣本更高的權重,使得後續的學習器更關注這些樣本;第二個權重是弱學習器的權重,這是由學習器本身的錯誤率來決定的,錯誤率低的學習器理應獲得更高的權重。AdaBoost的訓練過程如下圖所示。

AdaBoost的訓練過程(HMLST figure 7-7)

來看一個具體的例子。下圖給出了在moons數據集上連續重複訓練的五個學習器的決策邊界,其中每個學習器都是一個高度正則化的帶有RBF核的SVM分類器。

連續訓練的學習器的決策邊界(HMLST figure 7-8)

如圖所示,最開始的時候樣本的權重都一樣,經過第一個分類器得到許多分類錯誤的樣本,在將這些樣本給第二個學習器訓練之前,調整這些錯誤樣本的權重,使得第二個分類器能在這些樣本上表現的更好,以此類推。正如你所看到的,這種方法與梯度下降演算法有一些相似之處,而不同的是AdaBoost不是調整單個分類器的模型參數來最小化成本函數, 而是調整樣本權重和學習器權重來優化集成學習器的性能。

我們現在具體來看一下Adaboost演算法的流程,最初將每個樣本的權重都設為1/m(樣本總數為m),然後訓練得到第一個學習器的錯誤率 r_1 ,學習器的錯誤率計算公式為

其中 hat{y}_j^{(i)} 表示第 j 個學習器對第 i 個樣本的預測值。

得到每個學習器的錯誤率後,再來計算學習器的權重 alpha_j ,計算公式如下所示,其中 eta 表示學習率超參數(默認為1)。

所以學習器性能越好,其權重就越高,如果是隨機猜測,那麼它的權重接近於零。但是如果學習器經常出錯,甚至比隨機猜測還不準確,那麼它的權重將是負值。

另外,訓練過程中樣本權重更新規則為

每次更新後對所有樣本權重進行歸一化。

在Scikit-Learn中一般使用的是AdaBoost演算法的多分類版本SAMME,當只有兩個類時,兩者等效。此外,如果學習器可以估計類別概率,那麼可以使用Scikit-Learn中的SAMME的變體SAMME.R來實現,它依賴於類別概率,而不是類標記,通常表現地更好。

注意,如果你的AdaBoost集成模型出現了過擬合問題,可以嘗試減少基學習器的數量或添加更規範的基學習器。

7.5.2 Gradient Boosting

另外一種非常流行的Boosting方法是Gradient Boosting,它主要的思想是,每一次建立模型是在之前建立模型損失函數的梯度下降方向。損失函數(loss function)描述的是模型的不靠譜程度,損失函數越大,則說明模型越容易出錯。所以如果能夠讓模型損失函數持續的下降,則說明模型在不停的改進,而最好的方式就是讓損失函數在其梯度(Gradient)的方向上下降。它不像AdaBoost那樣在每次迭代時更新樣本權重,而是對新的學習器和前一個學習器產生的殘差進行擬合。

其實可以認為Gradient Boosting是在類似於"知錯就改"思想下的一種模型優化方法,首先將函數分解為可加的形式(其實所有的函數都是可加的,只是是否好放在這個框架中,以及最終的效果如何)。然後進行m次迭代,通過使得損失函數在梯度方向上減少,最終得到一個優秀的模型。值得一提的是,每次模型在梯度方向上的減少的部分,可以認為是一個弱的模型,最終通過加權(也就是每次在梯度方向上下降的距離)的方式將這些弱的模型合併起來,集成一個更好的模型。

Scikit-Learn提供了一個GradientBoostingRegressor類來直接實現GBRT(Gradient Boosted Regression Trees)的訓練,就像RandomForestRegressor類一樣,它具有控制決策樹的生長的超參數,以及控制集成學習訓練的超參數。

7.6 Stacking

Stacking是一種與以上方法截然不同的集成學習方法,它的主要思想是指訓練一個模型用於組合(combine)其他各個模型,而不是使用簡單的集成策略(如硬投票法)來匯總所有的基學習器的預測結果。即首先我們先訓練多個不同的初級模型,然後再以之前訓練的各個模型的輸出為輸入來訓練一個次級模型,以得到一個最終的輸出。如果可以選用任意一個組合演算法,那麼理論上,Stacking可以表示上面提到的各種Ensemble方法。然而,實際中,我們通常使用單層logistic回歸作為組合模型。

一個具體的例子如下圖所示,首先分別利用最底層的三個學習器進行回歸預測得到不同的預測值(3.1,2.7和2.9),然後將這些輸出作為最終學習器(Blending)的輸入進行訓練並進行預測(3.0)。

Stacking的預測原理(HMLST figure 7-12)

一種常用的訓練Blender的方法是留出法(hold-out set)。讓我們看看它是如何工作的。首先,將訓練集分為兩個子集,第一個子集用於訓練第一層中的學習器,然後用訓練好的第一層學習器對第二個子集進行預測,這樣做確保了預測是"乾淨"的,因為在訓練過程中沒有使用第二個子集。所以得到了第二個子集的三個預測值,然後使用這些預測值作為輸入特徵並保留目標值來構造新的訓練集,利用新的訓練集對Blender進行訓練,並將第一層的預測值作為目標值來進行學習,如下圖所示。

Blender的訓練過程(HMLST figure 7-14)

實際上,我們可以通過將初始訓練集分為三個子集來構造整層的Blender學習器,第一個子集用於訓練第一層,第二個子集用於構造第二層的訓練集,第三個用於構造訓練集以訓練第三層。 一旦完成,就可以通過順序遍歷每一層來預測新樣本,如下圖所示。

多層Stacking的預測過程(HMLST figure 7-15)

值得一提的是次級學習器的輸入特徵表示和次級學習演算法對Stacking集成的泛化性能有很大的影響。研究表明,將初級學習器的輸出類概率作為次級學習器的輸入屬性效果較好。

7.7 總結

本章主要討論了集成學習的幾種常用方法和基於Scikit-Learn框架的具體實現,結合具體例子講解了各種方法的原理,比如Bagging, 隨機森林等等,以及它們的適用情形,比如投票法適用於基學習器較獨立的情況,而如果基學習器相互依賴,一般採用Boosting的方法。

總的來說,集成學習往往能在那些單一模型表現不好的實際任務中表現地很好,在目前的機器學習系統中,集成學習演算法是屬於非常強力的演算法。

如果對本文章有任何問題和建議,歡迎指正和提出。

— King WaY

編輯

參考文獻:

[1]Géron A. Hands-on machine learning with Scikit-Learn and TensorFlow: concepts, tools, and techniques to build intelligent systems[M]. " OReilly Media, Inc.", 2017.

[2] 周志華. 機器學習[M]. Qing hua da xue chu ban she, 2016.

完整代碼:

ageron/handson-ml?

github.com圖標
推薦閱讀:

TAG:機器學習 | 深度學習DeepLearning | 集成學習 |