1. 程式人生 > >決策樹(六)--隨機森林

決策樹(六)--隨機森林

原文:

http://www.cnblogs.com/hrlnw/p/3850459.html

1.隨機森林原理介紹

隨機森林,指的是利用多棵樹對樣本進行訓練並預測的一種分類器。該分類器最早由Leo Breiman和Adele Cutler提出,並被註冊成了商標。簡單來說,隨機森林就是由多棵CART(Classification And Regression Tree)構成的。對於每棵樹,它們使用的訓練集是從總的訓練集中有放回取樣出來的,這意味著,總的訓練集中的有些樣本可能多次出現在一棵樹的訓練集中,也可能從未出現在一棵樹的訓練集中。在訓練每棵樹的節點時,使用的特徵是從所有特徵中按照一定比例隨機地無放回的抽取的,根據Leo Breiman的建議,假設總的特徵數量為M,這個比例可以是sqrt(M),1/2sqrt(M),2sqrt(M)。

因此,隨機森林的訓練過程可以總結如下:

(1)給定訓練集S,測試集T,特徵維數F。確定引數:使用到的CART的數量t,每棵樹的深度d,每個節點使用到的特徵數量f,終止條件:節點上最少樣本數s,節點上最少的資訊增益m

對於第1-t棵樹,i=1-t:

(2)從S中有放回的抽取大小和S一樣的訓練集S(i),作為根節點的樣本,從根節點開始訓練

(3)如果當前節點上達到終止條件,則設定當前節點為葉子節點,如果是分類問題,該葉子節點的預測輸出為當前節點樣本集合中數量最多的那一類c(j),概率p為c(j)佔當前樣本集的比例;如果是迴歸問題,預測輸出為當前節點樣本集各個樣本值的平均值。然後繼續訓練其他節點。如果當前節點沒有達到終止條件,則從F維特徵中無放回的隨機選取f維特徵。利用這f維特徵,尋找分類效果最好的一維特徵k及其閾值th,當前節點上樣本第k維特徵小於th的樣本被劃分到左節點,其餘的被劃分到右節點。繼續訓練其他節點。有關分類效果的評判標準在後面會講。

(4)重複(2)(3)直到所有節點都訓練過了或者被標記為葉子節點。

(5)重複(2),(3),(4)直到所有CART都被訓練過。

利用隨機森林的預測過程如下:

對於第1-t棵樹,i=1-t:

(1)從當前樹的根節點開始,根據當前節點的閾值th,判斷是進入左節點(<th)還是進入右節點(>=th),直到到達,某個葉子節點,並輸出預測值。

(2)重複執行(1)直到所有t棵樹都輸出了預測值。如果是分類問題,則輸出為所有樹中預測概率總和最大的那一個類,即對每個c(j)的p進行累計;如果是迴歸問題,則輸出為所有樹的輸出的平均值。

注:有關分類效果的評判標準,因為使用的是CART,因此使用的也是CART的評判標準,和C3.0,C4.5都不相同。

對於分類問題(將某個樣本劃分到某一類),也就是離散變數問題,CART使用Gini值作為評判標準。定義為Gini=1-∑(P(i)*P(i)),P(i)為當前節點上資料集中第i類樣本的比例。例如:分為2類,當前節點上有100個樣本,屬於第一類的樣本有70個,屬於第二類的樣本有30個,則Gini=1-0.7×07-0.3×03=0.42,可以看出,類別分佈越平均,Gini值越大,類分佈越不均勻,Gini值越小。在尋找最佳的分類特徵和閾值時,評判標準為:argmax(Gini-GiniLeft-GiniRight),即尋找最佳的特徵f和閾值th,使得當前節點的Gini值減去左子節點的Gini和右子節點的Gini值最大。

對於迴歸問題,相對更加簡單,直接使用argmax(Var-VarLeft-VarRight)作為評判標準,即當前節點訓練集的方差Var減去減去左子節點的方差VarLeft和右子節點的方差VarRight值最大。

2.OpenCV函式使用

OpenCV提供了隨機森林的相關類和函式。具體使用方法如下:

(1)首先利用CvRTParams定義自己的引數,其格式如下

 CvRTParams::CvRTParams(int max_depth, int min_sample_count, float regression_accuracy, bool use_surrogates, int max_categories, const float* priors, bool calc_var_importance, int nactive_vars, int max_num_of_trees_in_the_forest, float forest_accuracy, int termcrit_type)

大部分引數描述都在http://docs.opencv.org/modules/ml/doc/random_trees.html上面有,說一下沒有描述的幾個引數的意義

bool use_surrogates:是否使用代理,指的是,如果當前的測試樣本缺少某些特徵,但是在當前節點上的分類or迴歸特徵正是缺少的這個特徵,那麼這個樣本就沒法繼續沿著樹向下走了,達不到葉子節點的話,就沒有預測輸出,這種情況下,可以利用當前節點下面的所有子節點中的葉子節點預測輸出的平均值,作為這個樣本的預測輸出。

const float*priors:先驗知識,這個指的是,可以根據各個類別樣本數量的先驗分佈,對其進行加權。比如:如果一共有3類,第一類樣本佔整個訓練集的80%,其餘兩類各佔10%,那麼這個資料集裡面的資料就很不平均,如果每類的樣本都加權的話,就算把所有樣本都預測成第一類,那麼準確率也有80%,這顯然是不合理的,因此我們需要提高後兩類的權重,使得後兩類的分類正確率也不會太低。

float regression_accuracy:迴歸樹的終止條件,如果當前節點上所有樣本的真實值和預測值之間的差小於這個數值時,停止生產這個節點,並將其作為葉子節點。

後來發現這些引數在決策樹裡面有解釋,英文說明在這裡http://docs.opencv.org/modules/ml/doc/decision_trees.html#cvdtreeparams

具體例子如下,網上找了個別人的例子,自己改成了可以讀取MNIST資料並且做分類的形式,如下:

按 Ctrl+C 複製程式碼 按 Ctrl+C 複製程式碼

MNIST樣本可以在這個網址http://yann.lecun.com/exdb/mnist/下載,改一下路徑可以直接跑的。

3.如何自己設計隨機森林程式

有時現有的庫無法滿足要求,就需要自己設計一個分類器演算法,這部分講一下如何設計自己的隨機森林分類器,程式碼實現就不貼了,因為在工作中用到了,因此比較敏感。

首先,要有一個RandomForest類,裡面儲存整個樹需要的一些引數,包括但不限於:訓練樣本數量、測試樣本數量、特徵維數、每個節點隨機提取的特徵維數、CART樹的數量、樹的最大深度、類別數量(如果是分類問題)、一些終止條件、指向所有樹的指標,指向訓練集和測試集的指標,指向訓練集label的指標等。還要有一些函式,至少要有train和predict吧。train裡面直接呼叫每棵樹的train方法即可,predict同理,但要對每棵樹的預測輸出做處理,得到森林的預測輸出。

其次,要有一個sample類,這個類可不是用來儲存訓練集和對應label的,這是因為,每棵樹、每個節點都有自己的樣本集和,如果你的儲存每個樣本集和的話,需要的記憶體實在是太過巨大了,假設樣本數量為M,特徵維數為N,則整個訓練集大小為M×N,而每棵樹的每層都有這麼多樣本,樹的深度為D,共有S棵樹的話,則需要儲存M×N×D×S的儲存空間。這實在是太大了。因此,每個節點訓練時用到的訓練樣本和特徵,我們都用序號陣列來代替,sample類就是幹這個的。sample的函式基本需要兩個就行,第一個是從現有訓練集有放回的隨機抽取一個新的訓練集,當然,只包含樣本的序號。第二個函式是從現有的特徵中無放回的隨機抽取一定數量的特徵,同理,也是特徵序號即可。

然後,需要一個Tree類,代表每棵樹,裡面儲存樹的一些引數以及一個指向所有節點的指標。

最後,需要一個Node類,代表樹的每個節點。

需要說明的是,儲存樹的方式可以是最普通的陣列,也可是是vector。Node的儲存方式同理,但是個人不建議用連結串列的方式,在程式設計以及函式處理上太麻煩,但是在省空間上並沒有太多的體現。

目前先寫這麼多,最後這部分我還會再擴充一些。