背景與原理:
首先我們需要知道集成學習的概念,所謂集成學習,就是使用一系列學習器進行學習,並且通過某種規則把這些學習器的學習結果整合起來從而獲得比單個學習器學習效果更好的機器學習方法。這樣的方法可以用於解決單個學習器的過擬合、性能瓶頸等問題,常用的集成方式主要有Bagging(並行)和Boosting(串行),隨機森林就是Bagging的一種擴展變體。
傳統意義上的隨機森林算法是基於決策樹的集成學習方法,在數據量比較大時決策樹會出現性能瓶頸,而隨機森林以決策樹為基分類器,其由多個由Bagging集成學習技術訓練得到的決策樹,當輸入待分類樣本時,最終的分類結果由單個決策樹的輸出結果投票決定,這樣的隨機森林解決了性能瓶頸問題、對噪聲有較好的容忍度,高度並行且泛化性能較好;同時其還是非參數驅動的分類方法,並不需要對樣本的先驗知識。
隨機森林算法的第一步是構建數據集,假設我們的隨機森林由$k$棵決策樹組成,那么我們就要從原始數據集中生成$k$個數據集,使用的抽樣方法稱為Bootstrap方法,是一種隨機的有放回抽樣。
而對於每個數據集,我們都需要構建一棵決策樹,這里我們使用CART,全名為Classification and Regression Tree,即這種樹既可以用於分類問題,也可以用於回歸問題(即連續值的預測)(值得注意的是這種樹每個節點至多有兩個分支)
CART回歸樹:
回顧一下,所謂回歸問題就是給定一組數據,每個數據形如$(x_{1},...,x_{n},y)$,現想要訓練一個模型$f$實現在已知$X$的情況下對$y$進行預測。
而回歸樹是怎么操作呢?我們看到這組數據有$n$個特征,我們從中選取一個特征,不妨設為第$i$個特征吧,然后選定一個分界點$p$,然后做一個非常簡單粗暴的預測:如果$x_{i}<p$,那么我們就預測滿足這樣條件的$f(X)=y_{1}$,否則我們預測$f(X)=y_{2}$,這就是最基礎的一個樹形結構
因為我們的觀點是如果數據集中兩個數據點足夠接近,那么這兩個數據點對應的$y$值應該也很接近,所以如果我們把這些數據點划分的足夠細,我們就可以認為此時還被划分在一起的點的值可以近似看做相等了。
當然,為了准確性,假設此時被划分在一起的點對應的值為$y_{1},..,y_{k}$,那么我們的預測值應該是$\hat{y}=\dfrac{\sum_{i=1}^{k}y_{i}}{k}$
而我們的回歸樹的構建就是不斷選取一個特征,找到一個划分點把數據划分成幾個部分,直到我們認為誤差在可以接受的范圍內為止。
而我們度量准確性的方法也是非常自然的MSE損失函數,即$J=\dfrac{1}{m}\sum_{i=1}^{m}(f(X_{i})-y_{i})^{2}$
於是現在的問題就變成了:在每個節點上,我們要選取一個特征和一個分界點把這個節點上的數據集分成兩部分,如何選取呢?
這個選取方式直觀來講就是遍歷所有特征和分界點,每個特征和分界點都會把這個數據集分成兩部分,那么我們如果不再向下划分,這兩部分的預測值就分別是每個部分的真實值的平均,那么我們有了預測值,自然可以計算出損失函數,而我們選取的特征和分界點應該是使得損失函數值最小的那個!
因此我們重復這個過程就可以構造出一個回歸樹了。
CART分類樹:
這個分類樹與前一篇博客所述的決策樹有少許不同,這里我們並不使用信息增益,而是使用基尼系數(GINI)作為特征划分點的依據。
基尼系數:在分類問題中,對於一個有$k$個類的數據集$D$,其基尼系數$GINI(D)=\sum_{i=1}^{k}p_{i}(1-p_{i})=1-\sum_{i=1}^{k}p_{i}^{2}$,其中$p_{i}$是屬於第$i$類的數據在數據集中的占比。
那么基尼系數越大,說明樣本純度越低,如果想追求比較好的分類效果,我們希望分類到最后的葉節點時基尼系數盡可能低,這樣我們如果選了某個特征和划分點將數據集$D$划分為$D_{1},D_{2}$兩部分,那么這里的基尼系數可以寫作$GINI=\dfrac{|D_{1}|}{|D|}GINI(D_{1})+\dfrac{|D_{2}|}{|D|}GINI(D_{2})$,即兩部分的基尼系數的加權平均,這樣我們找到使這個基尼系數最小的特征和划分點將數據集分裂,重復這一過程直至取得較好效果即可。
在建立了$k$棵決策樹之后,我們通過這$k$棵決策樹的多數投票(如果是隨機問題,可以按某種加權平均)來得到最終的分類(回歸)結果即可。
代碼實現:
import numpy as np from sklearn import datasets from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.svm import LinearSVC import matplotlib.pyplot as plt import pylab as plt from sklearn.model_selection import train_test_split X,y=datasets.make_classification(n_samples=10000,n_features=20,n_informative=2,n_redundant=2) X_train,X_test,Y_train,Y_test=train_test_split(X,y,test_size=0.25) lr=LogisticRegression() svc=LinearSVC(C=1.0) rfc=RandomForestClassifier(n_estimators=100)#森林中樹的個數 lr=lr.fit(X_train,Y_train) score1=lr.score(X_test,Y_test) print(score1) svc=svc.fit(X_train,Y_train) score2=svc.score(X_test,Y_test) print(score2) rfc=rfc.fit(X_train,Y_train) score3=rfc.score(X_test,Y_test) print(score3) x=[] sc=[] for i in range(1,101): rfc = RandomForestClassifier(n_estimators=i) rfc = rfc.fit(X_train, Y_train) sc.append(rfc.score(X_test, Y_test)) x.append(i) plt.plot(x,sc,c='r') plt.show()
這段代碼對比了常用的三種分類器的表現:邏輯回歸、支持向量機和隨機森林,使用了sklearn的一個數據生成器,同時還展示了隨機森林的表現隨森林中樹的個數的變化。
變化圖形如上所示,可以看到事實上選擇20棵左右的樹就已經能有相當不錯的表現了,因此隨機森林中樹的個數選擇一定要注意,如果選擇的樹過多不僅不會使預測效果有明顯提高,反而會大大提高模型訓練的開銷。