機器學習系列(三十七)——集成學習與隨機森林

集成學習的原理

如果你隨機向多個人詢問一個複雜的決策問題 ,然後綜合他們的決策,通過投票的辦法選出最終決策,在許多情況下 ,你會發現 ,這個彙總的決策相當的好,這被稱爲羣體智慧。同樣,如果你聚合一組預測器(分類器或迴歸器)的預測,得到的預測結果也往往比最好的單個預測器要好。這樣的一組預測器,我們稱爲集成,所以這種技術,也被稱爲集成學習
在生活中隨處可見集成學習的例子,比如你知道一部新電影,但不知道到底值不值得看,於是你去問身邊的幾個朋友,如果他們大多數都覺得好看,你最終也決定去看,這就是一種“集成學習”。在機器學習競賽中獲勝的解決方案通常都涉及多種集成方法。
現在假設有一個分類任務,我們已經訓練好了一些分類器,包括一個Logistic分類器、一個 SVM 分類器、一個RF分類器 、 一個 K- 近鄰分類器等等。每個分類器的準確率約爲70% 。

這時,要創建出一個更好的分類器,最簡單的辦單就是聚合每個分類器的預測,然後將得票最多的結果作爲預測類別。這種大多數投票分類器被稱爲硬投票分類器

我們會發現,這個投票分類器的準確率通常比集成中最好的分類器還要高。
相應地,我們有軟投票分類器,它是一種概率平均的決策分類器。(本篇後面會介紹)
事實上,即使每個分類器都是弱學習器(意味着它僅比隨機猜測好一 點),通過集成依然可以實現一個強學習器(高準確率),只要有足夠大數量數據且足夠多種類的弱學習器就可以。
爲什麼會這樣呢?來類比一個情況:假設你有一個略微偏倚的硬幣,它有 51%的可能正面數字朝上,49%的可能背面花朝上。如果你擲這樣的一枚硬幣1000 次,你大致會得到差不多510次數字和490次花,所以正面是大多數。而如果你做數學計算,你會發現,“在1000次投擲後,大多數爲正面朝上”這件事的概率爲:
\sum_{i=501}^{1000}C_{1000}^{i}0.51^{i}0.49^{1000-i}

這個結果接近 75%,比單獨投擲一次要大很多。事情的關鍵就在這個概率上。如果假設有一個決策,每個決策器有60%的概率給出正確決策,那麼500個這樣的決策器綜合的結果就將有99.999%的概率給出正確決策!
\sum_{i=251}^{500}C_{500}^{i}0.51^{i}0.49^{500-i}

這就是集成學習的威力,理論上只要集成分類器中每個分類器的分類準確率比隨機猜測準確率高,則只要這個集成學習器中有足夠多的這樣的分類器,就一定會獲得接近100%準確率的性能。下面以一個具體的例子來感受一下集成學習的威力,首先用make_moons生成二類別模擬數據:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
x,y = datasets.make_moons(n_samples=500,noise=0.3,random_state=42)
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()

接下來訓練三個分類器,Logistic,SVM,DecisionTree,並分別查看準確率:

from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(x,y,random_state=42)
from sklearn.linear_model import LogisticRegression
log_clf=LogisticRegression()
log_clf.fit(x_train,y_train)
log_clf.score(x_test,y_test)
from sklearn.svm import SVC
svm_clf = SVC()
svm_clf.fit(x_train,y_train)
svm_clf.score(x_test,y_test)
from sklearn.tree import DecisionTreeClassifier
dt_clf = DecisionTreeClassifier()
dt_clf.fit(x_train,y_train)
dt_clf.score(x_test,y_test)

接下來使用少數服從多數的投票方式重新對測試集進行分類預測,並查看準確率:

y_predict1 = log_clf.predict(x_test)
y_predict2 = svm_clf.predict(x_test)
y_predict3 = dt_clf.predict(x_test)
y_predict = np.array((y_predict1 + y_predict2 + y_predict3) >= 2,dtype='int')
'''綜合3個算法給出預測'''
from sklearn.metrics import accuracy_score
accuracy_score(y_test,y_predict)

由結果看出,之前的三個分類器最高的準確率都不到90%,而綜合三者的決策,準確率達到了90.4%。可見相比於單獨的分類,集成學習確實提高了預測準確率。

sklearn中的集成學習

上述過程在sklearn中可以用VotingClassifier封裝(hard voting):

'''硬投票'''
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(estimators=[
    ('log_clf',LogisticRegression()),
    ('svm_clf',SVC()),
    ('dt_clf',DecisionTreeClassifier(random_state=666))
],voting = 'hard')
voting_clf.fit(x_train,y_train)
voting_clf.score(x_test,y_test)

準確率:

這裏只是演示操作,實際使用voting classifier時會先把使用的單個算法的超參調至最優,再使用voting classifier。
soft voting
對應於hard voting,soft voting是一種基於概率平均的投票,所以soft voting要求集成中每個模型都能計算分類概率。 邏輯迴歸本身就是基於概率的,knn可進行概率轉化,決策樹也可以,SVM轉概率比較麻煩,不過也可以,具體轉化方式這裏不做具體介紹。爲什麼要有soft voting呢?
這是因爲,在很多情況下一人一票的少數服從多數的決策結果是非常不好的,比如下面這種情況,對某個樣本5個模型預測的分類結果:

  • 模型1:A-0.99,B-0.1
    模型2:A-0.49,B-0.51
    模型3:A-0.45,B-0.55
    模型4:A-0.90,B-0.10
    模型5:A-0.40,B-0.60

如果按投票的規則,A-2,B-3,最終結果應爲B,但是觀察數據發現,模型1和4非常確信樣本應當屬於A類,而其它模型並沒有這麼確定,如模型2對A和B幾乎是一半一半,所以分類爲A好像更加合理。
soft voting就是基於概率的加權進行分類,按soft voting思想,屬於各個類別的集成預測分類概率爲各個模型預測概率的平均值,計算可得最終投票的結果爲A類。
下面在sklearn中使用soft voting,其實就是把voting參數設置爲soft,不過要注意的是SVC要使用soft voting必須將probability設置爲true:

voting_clf2 = VotingClassifier(estimators=[
    ('log_clf',LogisticRegression()),
    ('svm_clf',SVC(probability=True)),
    ('dt_clf',DecisionTreeClassifier(random_state=666))
],voting = 'soft')
voting_clf2.fit(x_train,y_train)
voting_clf2.score(x_test,y_test)

準確率:

可見soft voting比hard voting有更好的效果。

子模型進行集成學習

前面我們知道,一個集成分類器中,若其中每個分類器的分類正確的概率都大於隨機猜測的概率,則理論上只要這個集成學習器中有足夠多的分類器,一定會獲得非常非常強的性能(準確率逼近1)。
但一個問題是,雖然有很多機器學習算法,可算法種類畢竟是有限的,從投票來看,這麼少的算法還是不夠,如何集成更多算法進行集成學習呢? 那就是創建更多子模型,集成更多子模型的意見,來獲得更強的決策效果。而且子模型之間不能一致,要有差異性。即子模型最好是完全獨立的,彼此的錯誤毫不相關,這樣集成方法的效果最優。這種差異性是通過每個子模型只看樣本數據的一部分實現的。
對於取數據,取樣方式分爲放回取樣Bagging和不放回取樣Pasting,更常用的是Bagging方式,統計學中叫bootstrap。

隨機森林Random Forest

隨機森林(下稱RF)就是集成學習的一種,它的子模型是一棵一棵的決策樹,決策樹作爲一種非參數學習算法,使用它創建子模型更能產生差異性。下面使用放回抽樣方式構建一個含有500個決策樹的RF分類器對本篇之前的數據做分類:

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
bagging_clf = BaggingClassifier(DecisionTreeClassifier(),
                                n_estimators = 500#集成500個
                                ,max_samples=100
                                ,bootstrap=True)
bagging_clf.fit(x,y)
bagging_clf.score_

相應地我們可以增加子模型的個數爲1000再看分類結果:

'''增加子模型數量'''
bagging_clf2 = BaggingClassifier(DecisionTreeClassifier(),
                                n_estimators = 1000
                                ,max_samples=100
                                ,bootstrap=True)
bagging_clf2.fit(x_train,y_train)
bagging_clf2.score(x_test,y_test)

可見創建更多子模型的RF效果確實是更好的,但是由於數據本身存在噪音或錯誤樣本,我們的算法不會像理論推導那樣可以有接近100%的準確率。不過使用集成學習還是會有很好的結果。另外可以調節其中max_samples參數、DecisionTreeClassifier構造參數等來進一步獲得更好的結果。

更多關於集成學習將在下篇繼續介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章