1. 程式人生 > >決策樹ID3、C4.5、CART、隨機森林的原理與例子

決策樹ID3、C4.5、CART、隨機森林的原理與例子

(寫在前面:作者是一名剛入學的模式識別專業的碩士生,第一次寫部落格,有錯誤的地方還請大家多多指教評論,一起交流呀~)

決策樹的基本流程

①劃分特徵的選擇(常見的選擇方法有:資訊增益、增益率、基尼指數,下文會詳細介紹)

②劃分停止準則:停止準則表示該節點不再劃分,進而形成葉節點。 一般有幾種情況:1) 當前節點包含的樣本全部屬於同一類別,則停止劃分,形成葉節點;2) 當前屬性集為空,或者所有樣本在所有屬性上取值相同,則無法劃分,並將當前節點記為葉節點,類別為該節點所含樣本數最多的類別;3) 當前節點所包含的樣本集合為空,無法劃分,則將當前節點設為葉節點,類別為該節點父節點所含樣本最多的類別;4) 預先設定一個不純度下降差的門限值。當候選分支使得節點的不純度下降差小於這個門限,則停止分支,將當前節點記為葉節點,類別為該節點所含樣本數最多的類別。(預剪枝)

③剪枝處理(預剪枝、後剪枝) 剪枝的基本思想是對相鄰的葉節點(即有公共的父節點上),考慮是否應當消去它們。若消去它們後能引起令人滿意的(即可容忍的)不純度增長,那麼就消去,否則,不消去。

預剪枝:通過設定某些規則提前停止樹的分支而對樹剪枝,一旦停止,節點就是葉節點。例如劃分停止準則(4) 預先設定一個不純度下降差的門限值。或定義一個樣本閾值,當某個節點的樣本個數小於該閾值時就可以停止決策樹的生長等方法。

後剪枝:是基於訓練集構建了一顆決策樹後,利用測試集進行驗證。若將某相鄰的(即有公共的父節點上的)葉節點合併後,測試集誤差比未合併前的小,則合併這些葉結點,否則,不合並。(此驗證依賴於若干未經訓練的樣本集,且相比於預剪枝,這種方法更常用,因為在預剪枝的方法中要精確地估計何時停止樹增長以及設定合適的閾值都比較困難)。 在這裡插入圖片描述

常見的決策樹有:ID3、C4.5、CART——(這三種方法最大的區別在於:選擇分裂屬性的方法不同)

ID3

ID3演算法的核心思想就是以資訊增益來度量屬性的選擇,選擇分裂後資訊增益最大的屬性進行分裂。該演算法採用自頂向下的貪婪搜尋遍歷可能的決策空間。劃分特徵的選擇方法:資訊增益。首先,定義資訊熵為: 在這裡插入圖片描述 其中,D為樣本集合,共分為L類,pip_{i}第i類樣本數在樣本集D中所佔的樣本數,i∈{1,2, …, L} 。Ent(D) 的取值越小,則D的純度越高。

假定某個離散屬性a有V個可能的取值{a1,a2,...,aV}\{a^{1}, a^{2}, ..., a^{V}\}

,...,aV},若使用a來對樣本D進行劃分,則會產生V個分支節點。其中,第v個分支節點包含了D中所有在屬性a上取值為aVa^{V}的樣本,記為DVD^{V}。考慮不同的分支節點所包含的樣本數不同,給分支節點賦予權重DV/D|D^{V}|/|D|,即樣本數越多的分支節點的影響越大。因此用屬性a劃分樣本集D所獲得的資訊增益為: 在這裡插入圖片描述 一般情況下,資訊增益越大,代表使用屬性a來劃分所獲得的純度提升得越大。因此我們將選擇a=argmaxGain(D,a)a_{*} = argmax Gain(D, a)

C4.5

ID3採用的資訊增益度量存在一個缺點,它一般會優先選擇有較多屬性值的特徵,為了避免這個不足,C4.5中是用資訊增益比率(Gain_ratio)來作為特徵選擇分支的準則。且C4.5演算法可用處理離散型特徵和連續型特徵。 資訊增益率定義為: 在這裡插入圖片描述 其中,Gain(D, a)即為上述ID3方法中介紹的資訊增益,而 在這裡插入圖片描述 IV(a)稱為屬性a的“固有值”,屬性a所佔數目越多,IV(a)的值通常會越大,可以避免了對屬性值樣本數目較多的特徵更關注,但卻會對屬性值樣本數目較少的特徵更關注,因此,C4.5演算法並不是直接選擇增益率最大的候選劃分屬性,而是使用了一個啟發式方法:先從候選劃分屬性中找出資訊增益高於平均水平的屬性,再從中選擇增益率最高的屬性。

對於連續型特徵,不能直接根據連續屬性可取值來對結點劃分,這種劃分方法會增加樹形的複雜度,也會帶來較嚴重的過擬合。因此常見的做法是將連續屬性離散化,一般情況下采用二分法對連續屬性進行處理。

對於給定的樣本集D以及連續屬性a,假定a在D上出現了n個不同的值,先將這些值從大到小排序,排序後記為{a1,a2,...,an}\{ a^1, a^2,..., a^n\}。對相鄰的屬性值[ai,ai+1][ a^i, a^{i+1}]來說,取二者的中間值,即會產生n-1個候選劃分點Ta={ai+ai+121in1}T_{a}=\{ \frac{ a^i +a^{i+1}}{2} | 1 \leq i \leq n-1\},則基於該候選劃分點可將D分為子集DTD_{T}^{-}DT+D_{T}^{+}兩部分,DTD_{T}^{-}表示的是在屬性a上取值不大於T的樣本,其中T∈TaT_{a}DT+D_{T}^{+}表示的是在屬性a上取值大於T的樣本。

則對資訊增益(2)的式子稍作修改: 在這裡插入圖片描述

Gain(D, a, T)是指樣本集D基於劃分點T二分劃分後的資訊增益,因此我們可選取資訊增益Gain(D, a, T)最大化後的劃分點T作為屬性a的離散值。

CART(classification and regression tree)

CART與前兩種方法不同,它可以構建分類樹和迴歸樹。由於我們目前處理的任務是分類任務,因此這裡只介紹分類樹的構建。CART分類時,使用基尼指數(Gini)來選擇最好的資料分割的特徵,Gini描述的是純度,與資訊熵的含義相似。CART 也可用來離散型特徵和連續型特徵。Gini值的定義為: 在這裡插入圖片描述 Gini(D)反映了從資料集D中隨機抽取兩個樣本,其類別標記不一致的概率。因此,Gini(D)越小,則資料集D的純度越高。

採用與式(2)相同的符號表示,即屬性a的基尼指數定義為 在這裡插入圖片描述 於是我們將在候選屬性集合A中,選擇使得劃分後的基尼指數最小的屬性作為最優劃分屬性,即: a=argminGini_index(D,a)a_{*} = argmin Gini\_index(D, a)

CART演算法同樣適用於對連續型特徵的處理,處理方式與C4.5演算法類似,不同之處在於選擇的方法為基尼指數,因此借鑑於式子(5)及式中符號意義,因此,式(7)可改: 在這裡插入圖片描述 Gini_index(D, a, T)是指樣本集D基於劃分點T二分劃分後的基尼指數。因此我們可選取Gini_index(D, a, T)最小化後的劃分點T作為屬性a的離散值。

CART決策樹演算法視覺化過程

例子採用的是鳶尾花資料集,特徵有4個,類別為3分類。

每個節點的輸出值含義如下:用了哪個特徵作為該節點劃分feature,採用該特徵劃分下的基尼指數gini,該節點上的樣本總數samples,歸屬該節點的類別。 在這裡插入圖片描述

python程式碼如下:

from sklearn import tree #匯入決策樹
from sklearn.datasets import load_iris #匯入datasets建立陣列
from sklearn.model_selection import train_test_split
import sklearn.metrics as metrics

iris = load_iris() #匯入資料
iris_data = iris.data #選擇訓練陣列
iris_target = iris.target #選擇對應標籤陣列
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_target, test_size=0.3)

clf = tree.DecisionTreeClassifier() #建立決策樹模型
clf = clf.fit(X_train, y_train) #擬合模型

y_prob = clf.predict_proba(X_test)
y_pred = clf.predict(X_test)
print("Testing accuracy", clf.score(X_test, y_test))
cnames = list(['setosa', 'versicolor', 'virginica'])
print(metrics.classification_report(y_test, y_pred, target_names=cnames))

#import graphviz #匯入決策樹視覺化模組
import pydotplus
from IPython.display import Image
data_feature_name = ['feature1', 'feature2', 'feature3', 'feature4']
data_target_name = ['setosa', 'versicolor', 'virginica']

dot_data = tree.export_graphviz(clf, feature_names=data_feature_name, class_names=data_target_name, out_file=None, filled=True, rounded=True, special_characters=True) #以DOT格式匯出決策樹
graph = pydotplus.graph_from_dot_data(dot_data)
img = Image(graph.create_png())
graph.write_png("iris.png")

隨機森林

隨機森林是由n顆決策樹構建,且決策樹之間是獨立同分布的,具體流程如下:

① 給定訓練集Training,測試集Testing,以及資料特徵的維數F; 需給定的引數:森林中的CART決策樹的數量T,每棵樹的最大深度d,隨機選擇每個節點需要使用到的特徵數量f(其中f<<F,一般情況下取f=Ff = \sqrt{F})。終止條件:到達節點上最少樣本數閾值s,節點上最小的基尼指數閾值m。(根據實際情況終止條件還有其他可以選擇,如上面決策樹基本流程部分的劃分停止準則) ② 對於第i顆樹(i∈{1,2,…,T})從訓練集Training中隨機地有放回的抽取與Training大小一樣的資料集構成T(i),從根節點開始訓練。 ③ 如果當前節點達到終止條件中節點上的樣本數小於s,則設定當前節點為葉節點,對於分類問題,則返回該節點的樣本資料集合中數量最多的那一類max_c作為當前節點的類別標籤,而概率p則為max_c的數量佔該節點所有樣本數的比重。對於CART決策樹演算法,採用了基尼指數作為選擇分裂屬性的標準(對於離散型特徵和連續型特徵的選擇方法已在上文中介紹,此處不再累贅),因此從f個特徵中找出基尼指數最小的特徵f(j)及其閾值th,小於閾值th的樣本被劃分到左節點,大於th被劃分到右節點;若基尼指數小於指定閾值m,則終止分裂,同樣返回類別標籤和概率p。 ④ 重複②③過程,直至所有節點被標記為葉節點。 ⑤ 重複②③④過程,生成T顆決策樹,即構建了一個隨機森林。

隨機森林分類效果(錯誤率)與兩個因素有關: ①森林中任意兩棵樹的相關性:相關性越大,錯誤率越大; ②森林中每棵樹的分類能力:每棵樹的分類能力越強,整個森林的錯誤率越低。

例子:Adult資料集

(根據人口普查資料預測收入是否超過5萬美元/年。也被稱為“人口普查收入”資料集。) 在這裡插入圖片描述

python程式碼如下:

# -*- coding: utf-8 -*-
from sklearn.ensemble import RandomForestClassifier #匯入必要的庫
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
import sklearn.metrics as metrics
from sklearn.model_selection import GridSearchCV
from sklearn import preprocessing
from sklearn.datasets import load_svmlight_file
import requests
from io import BytesIO

#load data 匯入資料
r = requests.get('''https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/binary/a9a''')
x, y = load_svmlight_file(f=BytesIO(r.content), n_features=123)
x = x.toarray()

#split data training and testing
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.3)

#randomForest
clf = RandomForestClassifier(n_estimators=50, max_depth=13, min_samples_split=50,
                                 min_samples_leaf=20, max_features=7, oob_score=True, random_state=10)
                                 #這裡的引數設定可以用搜索法在一定範圍內尋找最優引數
clf.fit(X_train, y_train.ravel())
#print(clf.oob_score_)#這裡的袋外資料可作為模型的測試集等,有較大的作用

y_predict = clf.predict(X_test)
y_prob = clf.predict_proba(X_test)
print("Accuracy:", accuracy_score(y_test, y_predict) )
target_names = ['class 0', 'class 1']
print(classification_report(y_test, y_predict, target_names=target_names))
print("AUC score(Train): %f" % metrics.roc_auc_score(y_test, y_predict))

參考文獻

[1]Zhou Z H, Feng J. Deep Forest: Towards An Alternative to Deep Neural Networks[J]. 2017. [2] 周志華. 機器學習[M]. 清華大學出版社, 2016. [3] 李航. 統計學習方法[M]. 清華大學出版社, 2012. [4]PeterHarrington. 機器學習實戰[M]. 人民郵電出版社, 2013. [5] Google、百度學術