摘要:隨機森林和決策樹相比,能更好的防止過擬合。雖然每個基分類器很弱,但最後組合的結果通常很強,這也類似於:“三個臭皮匠頂個諸葛亮”的思想。對比發現Random Forest(隨機森林)和SVM(支援向量機)名列第一、第二名。
01 樹與森林
在構建決策樹的時候,可以讓樹進行完全生長,也可以通過引數控制樹的深度或者葉子節點的數量,通常完全生長的樹會帶來過擬合問題。過擬合一般由資料中的噪聲和離群點導致,一種解決過擬合的方法是進行剪枝,去除樹的一些雜亂的枝葉。
注:你可能需要參考前面的文章:《0x0B 菩提決策樹,姻緣演算法求》
實際應用中,一般可用隨機森林來代替,隨機森林在決策樹的基礎上,會有更好的表現,尤其是防止過擬合。
在機器學習演算法中,有一類演算法比較特別,叫組合演算法(Ensemble),即將多個基演算法(Base)組合起來使用。每個基演算法單獨預測,最後的結論由全部基演算法進行投票(用於分類問題)或者求平均(包括加權平均,用於迴歸問題)。
組合演算法中,一類是Bagging(裝袋),另一類是Boosting(提升),隨機森林便是Bagging中的代表。使用多顆樹進行單獨預測,最後的結論由這些樹預測結果的組合共同來決定,這也是“森林”名字的來源。每個基分類器可以很弱,但最後組合的結果通常能很強,這也類似於:“三個臭皮匠頂個諸葛亮”的思想。
《統計學習方法》作者李航博士的文章:http://blog.sina.com.cn/s/blog_7ad48fee0102vb9c.html 有說明:“來自JMLR(Journal of Machine Learning Research,機器學習頂級期刊)雜誌的文章,有人讓179種不同的分類學習演算法在UCI 121個資料集上進行了“大比武”(UCI是機器學習公用資料集,每個資料集的規模都不大)。結果發現Random Forest(隨機森林)和SVM(支援向量機)名列第一、第二名,但兩者差異不大”。英文的原文在這兒http://jmlr.org/papers/v15/delgado14a.html, 感興趣的可以參考。
上面文章換一種理解,即為:掌握了隨機森林,基本上可以處理很多常見的機器學習問題。由此可見,組合演算法在很多時候,其預測的效能都會優於單獨的演算法,這也正是隨機森林的魅力所在。
02 處處隨機
多個人組成的團隊,是不是一定就強於一個人呢?團隊的產出並不能把每個人的力量相加,並非和“眾人拾柴火焰高”的道理一樣。要讓團隊的總產出高於單個人的產出,那必須是每個人都有其它人不具備的知識或者能力,如果大家都是完全相同的知識或者能力,在解決難題上並沒有幫助。假設對一個資料的預測,大家的結論都是1,最後組合結論依然是1,沒有任何改變。對預測準確率,沒有任何提升。
這也是“森林”前面還有“隨機”這個修飾詞的原因,隨機就是讓每個顆樹不一樣,如果都一樣,組合後的效果不會有任何提升。假設每顆樹不一樣,單獨預測錯誤率大概都是40%(夠弱了吧,很多時候都會犯錯),但三顆樹組合的後的錯誤率就變成了35.2%(至少一半以上(兩顆樹)同時犯錯結果才會犯錯),其計算方法為:
3個全錯(一種情況) + 2個錯1個對(3種組合):
1 0.4^3 + 3 0.4^2 * (1-0.4)^1 = 0.352
因此,隨機森林演算法中,“隨機”是其核心靈魂,“森林”只是一種簡單的組合方式而已。隨機森林在構建每顆樹的時候,為了保證各樹之間的獨立性,通常會採用兩到三層的隨機性。
從資料抽樣開始,每顆樹都隨機地在原有資料的基礎上進行有放回的抽樣。假定訓練資料有1萬條,隨機抽取8千條資料,因為是有放回的抽樣,可能原資料中有500條被抽了兩次,即最後的8千條中有500條是重複的資料。每顆樹都進行獨立的隨機抽樣,這樣保證了每顆樹學習到的資料側重點不一樣,保證了樹之間的獨立性。
抽取了資料,就可以開始構建決策分支了,在每次決策分支時,也需要加入隨機性,假設資料有20個特徵(屬性),每次只隨機取其中的幾個來判斷決策條件。假設取4個屬性,從這4個特徵中來決定當前的決策條件,即忽略其它的特徵。取特徵的個數,通常不能太小,太小了使得單顆樹的精度太低,太大了樹之間的相關性會加強,獨立性會減弱。通常取總特徵的平方根,或者log2(特徵數)+1,在scikit-learn的實現中,支援sqrt與log2,而spark還支援onethird(1/3)。
在結點進行分裂的時候,除了先隨機取固定個特徵,然後選擇最好的分裂屬性這種方式,還有一種方式,就是在最好的幾個(依然可以指定sqrt與log2)分裂屬性中隨機選擇一個來進行分裂。scikit-learn中實現了兩種隨機森林演算法,一種是RandomForest,另外一種是ExtraTrees,ExtraTrees就是用這種方式。在某些情況下,會比RandomForest精度略高。
總結起來,使用隨機性的三個地方:
- 隨機有放回的抽取資料,數量可以和原資料相同,也可以略小;
- 隨機選取N個特徵,選擇最好的屬性進行分裂;
- 在N個最好的分裂特徵中,隨機選擇一個進行分裂;
因此,理解了這幾個地方的隨機性,以及隨機性是為了保證各個基演算法模型之間的相互獨立,從而提升組合後的精度。當然,還需要保證每個基分類演算法不至於太弱,至少要強於隨機猜測,即錯誤率不能高於0.5。
03 sklearn與mllib
scikit-learn和spark中都實現了隨機森林,但各自有些細小的區別。
在scikit-learn中,同樣只是簡單幾行程式碼即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# sklearn_rf.py import pandas as pd from sklearn.ensemble import RandomForestClassifier df = pd.read_csv('sklearn_data.csv') train, test = df.query("is_date != -1"), df.query("is_date == -1") y_train, X_train = train['is_date'], train.drop(['is_date'], axis=1) X_test = test.drop(['is_date'], axis=1) model = RandomForestClassifier(n_estimators=50, criterion='gini', max_features="sqrt", min_samples_leaf=1, n_jobs=4, ) model.fit(X_train, y_train) print model.predict(X_test) print zip(X_train.columns, model.feature_importances_) |
呼叫RandomForestClassifier時的引數說明:
- n_estimators:指定森林中樹的顆數,越多越好,只是不要超過記憶體;
- criterion:指定在分裂使用的決策演算法;
- max_features:指定了在分裂時,隨機選取的特徵數目,sqrt即為全部特徵的平均根;
- min_samples_leaf:指定每顆決策樹完全生成,即葉子只包含單一的樣本;
- n_jobs:指定並行使用的程式數;
從前面的隨機森林構建過程來看,隨機森林的每顆樹之間是獨立構建的,而且儘量往獨立的方向靠,不依賴其它樹的構建,這一特點,在當前的大資料環境下,尤其被人喜愛,因為它能並行,並行,並行……。
能完全並行的演算法,一定會被人們追捧,在資源夠的情況下,可以同時並行構建大量的決策樹。scikit-learn雖然是單機版本,不能做分散式,但也可以利用單機的多枋來並行。
spark中,更是能發揮分散式的特點了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from pprint import pprint from pyspark import SparkContext from pyspark.mllib.tree import RandomForest from pyspark.mllib.regression import LabeledPoint sc = SparkContext() data = sc.textFile('spark_data.csv').map(lambda x: x.split(',')).map(lambda x: (float(x[0]), int(x[1]), int(x[2]), float(x[3]), int(x[4]), int(x[5]))) train = data.filter(lambda x: x[5]!=-1).map(lambda v: LabeledPoint(v[-1], v[:-1])) test = data.filter(lambda x: x[5]==-1)#.map(lambda v: LabeledPoint(v[-1], v[:-1])) model = RandomForest.trainClassifier(train, numClasses=2, numTrees=50, categoricalFeaturesInfo={1:2, 2:2, 4:3}, impurity='gini', maxDepth=5, ) print 'The predict is:', model.predict(test).collect() print 'The Decision tree is:', model.toDebugString() |
和決策樹版本相比,唯一的變化,就是將DecistionTree換成了RandomForest,另外增加了一個指定樹顆數的引數:numTrees=50。
而和scikit-learn版本相比,spark中會通過categoricalFeaturesInfo={1:2, 2:2, 4:3}引數指定第5個屬性(工作屬性)具有3種不同的類別,因此spark在劃分的時候,是按類別變數進行處理。而scikit-learn中,依然當成連續的變數處理,所以在條件判斷的時候,才會有house
當有多個最優分割的時候,spark與scikit-learn在選擇上也有區別,spark會按屬性順序進行選擇,而scikit-learn會隨機選擇一個。這也是導致scikit-learn在多次執行中會輸出0和1的問題。
scikit-learn中,還可以輸出引數重要性,這也是決策樹和隨機森林的優點之一(目前pyspark還不支援輸入引數重要性):
1 2 |
# scikit-learn中 print zip(X_train.columns, model.feature_importances_) |
[(‘height’, 0.25), (‘house’,’car’, 0.0), (‘handsome’, 0.60), (‘job’, 0.0)]
04 特點與應用
隨機森林基本上繼承決策樹的全部優點,只需做很少的資料準備,其他演算法往往需要資料歸一化。決策樹能處理連續變數,還能處理離散變數,當然也能處理多分類問題,多分類問題依然還是二叉樹。決策樹就是if-else語句,區別只是哪些條件寫在if,哪些寫在else,因此易於理解和解釋。
決策樹的可解釋性強 ,你可以列印出整個樹出來,從哪個因素開始決策,一目瞭然。但隨機森林的可解釋性就不強了。所謂可解釋性,就是當你通過各種調參進行訓練,得出一個結論,你老大來問你,這個結論是怎麼得出來的?你說是模型自己訓練出來的,老大又問了,比如舉一條具體的資料,你說一說得出結論的過程呢?因為隨機森林引入了隨機取特徵,而且是由多顆樹共同決定,樹一旦多了,很難說清楚得出結論的具體過程。雖然可以列印每顆樹的結構,但很難分析。
雖然不好解釋,但它解決了決策樹的過擬合問題,使模型的穩定性增加,對噪聲更加魯棒,從而使得整體預測精度得以提升。
因為隨機森林能計算引數的重要性,因此也可用於對資料的降維,只選取少量幾維重要的特徵來近似表示原資料。同理,在資料有眾多的特徵時,也可以用於特徵選擇,選擇關鍵的特徵用於演算法中。
隨機森林還有天生的並行性,可以很好的處理大規模資料,也可以很容易的在分散式環境中使用。
最後,在大資料環境下,隨著森林中樹的增加,最後生成的模型可能過大,因為每顆樹都是完全生長,儲存了用於決策的全部資料,導致模型可能達到幾G甚至幾十G。如果用於線上的預測,光把模型載入到記憶體就需要很長時間,因此比較適合離線處理。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式