1、背景及問題html
決策樹算法是爲了解決二分類問題出現的,是根據歷史經驗(或訓練集)來作判斷,生成決策結果(或樹狀圖)的過程。算法
/*請尊重做者勞動成果,轉載請標明原文連接:*/dom
/* http://www.javashuo.com/article/p-adkajldd-cp.html * /函數
2、決策樹原理學習
決策樹算法分爲兩個階段:構造和剪枝。測試
1.構造優化
什麼是構造呢?構造就是生成一棵完整的決策樹。簡單來講, 構造的過程就是選擇什麼屬性做爲節點的過程 ,那麼在構造過程當中,會存在三種節點:spa
1)根節點:就是樹的最頂端,最開始的那個節點。3d
2)內部節點:就是樹中間的那些節點,好比說「溫度」、「溼度」、「颳風」;code
3)葉節點:就是樹最底部的節點,也就是決策結果。
節點之間存在父子關係。好比根節點會有子節點,子節點會有子子節點,可是到了葉節點就中止了,葉節點不存在子節點。那麼在構造過程當中,你要解決三個重要的問題:
1)選擇哪一個屬性做爲根節點;
2)選擇哪些屬性做爲子節點;
3)何時中止並獲得目標狀態,即葉節點。
2.剪枝
決策樹構造出來以後是否是就萬事大吉了呢?也不盡然,咱們可能還須要對決策樹進行剪枝。剪枝就是給決策樹瘦身,這一步想實現的目標就是,不須要太多的判斷,一樣能夠獲得不錯的結果。之因此這麼作,是爲了防止「過擬合」(Overfitting)現象的發生。
既然要對決策樹進行剪枝,具體有哪些方法呢?通常來講,剪枝能夠分爲「預剪枝」(Pre-Pruning)和「後剪枝」(Post-Pruning)。
預剪枝是在決策樹構造時就進行剪枝。方法是在構造的過程當中對節點進行評估,若是對某個節點進行劃分,在驗證集中不能帶來準確性的提高,那麼對這個節點進行劃分就沒有意義,這時就會把當前節點做爲葉節點,不對其進行劃分。
後剪枝就是在生成決策樹以後再進行剪枝,一般會從決策樹的葉節點開始,逐層向上對每一個節點進行評估。若是剪掉這個節點子樹,與保留該節點子樹在分類準確性上差異不大,或者剪掉該節點子樹,能在驗證集中帶來準確性的提高,那麼就能夠把該節點子樹進行剪枝。方法是:用這個節點子樹的葉子節點來替代該節點,類標記爲這個節點子樹中最頻繁的那個類。
3、決策樹分類
現有打籃球的數據集,訓練數據以下:
咱們該如何構造一個判斷是否去打籃球的決策樹呢?再回顧一下決策樹的構造原理,在決策過程當中有三個重要的問題:將哪一個屬性做爲根節點?選擇哪些屬性做爲後繼節點?何時中止並獲得目標值?
顯然將哪一個屬性(天氣、溫度、溼度、颳風)做爲根節點是個關鍵問題,在這裏咱們先介紹兩個指標:純度和信息熵。
先來講一下純度。你能夠把決策樹的構造過程理解成爲尋找純淨劃分的過程。數學上,咱們能夠用純度來表示,純度換一種方式來解釋就是讓目標變量的分歧最小。
我在這裏舉個例子,假設有 3 個集合:
集合 1:6 次都去打籃球;
集合 2:4 次去打籃球,2 次不去打籃球;
集合 3:3 次去打籃球,3 次不去打籃球。
按照純度指標來講,集合 1> 集合 2> 集合 3。由於集合 1 的分歧最小,集合 3 的分歧最大。
而後咱們再來介紹信息熵(entropy)的概念, 它表示了信息的不肯定度。
在信息論中,隨機離散事件出現的機率存在着不肯定性。爲了衡量這種信息的不肯定性,信息學之父香農引入了信息熵的概念,並給出了計算信息熵的數學公式:
p(i|t) 表明了節點 t 爲分類 i 的機率,其中 log2 爲取以 2 爲底的對數。這裏咱們不是來介紹公式的,而是說存在一種度量,它能幫咱們反映出來這個信息的不肯定度。當不肯定性越大時,它所包含的信息量也就越大,信息熵也就越高。
我舉個簡單的例子,假設有 2 個集合
集合 1:5 次去打籃球,1 次不去打籃球;
集合 2:3 次去打籃球,3 次不去打籃球。
在集合 1 中,有 6 次決策,其中打籃球是 5 次,不打籃球是 1 次。那麼假設:類別 1 爲「打籃球」,即次數爲 5;類別 2 爲「不打籃球」,即次數爲 1。那麼節點劃分爲類別 1 的機率是 5/6,爲類別 2 的機率是 1/6,帶入上述信息熵公式能夠計算得出:
一樣,集合 2 中,也是一共 6 次決策,其中類別 1 中「打籃球」的次數是 3,類別 2「不打籃球」的次數也是 3,那麼信息熵爲多少呢?咱們能夠計算得出:
從上面的計算結果中能夠看出,信息熵越大,純度越低。當集合中的全部樣本均勻混合時,信息熵最大,純度最低。
咱們在構造決策樹的時候,會基於純度來構建。而經典的 「不純度」的指標有三種,分別是信息增益(ID3 算法)、信息增益率(C4.5 算法)以及基尼指數(Cart 算法)。
1.ID3
ID3 算法計算的是 信息增益 ,信息增益指的就是劃分能夠帶來純度的提升,信息熵的降低。它的計算公式,是父親節點的信息熵減去全部子節點的信息熵。在計算的過程當中,咱們會計算每一個子節點的歸一化信息熵,即按照每一個子節點在父節點中出現的機率,來計算這些子節點的信息熵。因此信息增益的公式能夠表示爲:
公式中 D 是父親節點,Di 是子節點,Gain(D,a) 中的 a 做爲 D 節點的屬性選擇。
假設天氣 = 晴的時候,會有 5 次去打籃球,5 次不打籃球。其中 D1 颳風 = 是,有 2 次打籃球,1 次不打籃球。D2 颳風 = 否,有 3 次打籃球,4 次不打籃球。那麼 a 表明節點的屬性,即天氣 = 晴。
針對這個例子,D 做爲節點的信息增益爲:
也就是 D 節點的信息熵 -2 個子節點的歸一化信息熵。2 個子節點歸一化信息熵 =3/10 的 D1 信息熵 +7/10 的 D2 信息熵。
咱們基於 ID3 的算法規則,完整地計算下咱們的訓練集,訓練集中一共有 7 條數據,3 個打籃球,4 個不打籃球,因此根節點的信息熵是:
若是你將天氣做爲屬性的劃分,會有三個葉子節點 D一、D2 和 D3,分別對應的是晴天、陰天和小雨。咱們用 + 表明去打籃球,- 表明不去打籃球。那麼第一條記錄,晴天不去打籃球,能夠記爲 1-,因而咱們能夠用下面的方式來記錄 D1,D2,D3: D1(天氣 = 晴天)={1-,2-,6+}
D2(天氣 = 陰天)={3+,7-}
D3(天氣 = 小雨)={4+,5-}
咱們先分別計算三個葉子節點的信息熵:
由於 D1 有 3 個記錄,D2 有 2 個記錄,D3 有 2 個記錄,因此 D 中的記錄一共是 3+2+2=7,即總數爲 7。因此 D1 在 D(父節點)中的機率是 3/7,D2 在父節點的機率是 2/7,D3 在父節點的機率是 2/7。那麼做爲子節點的歸一化信息熵 = 3/7*0.918+2/7*1.0+2/7*1.0=0.965。
由於咱們用 ID3 中的信息增益來構造決策樹,因此要計算每一個節點的信息增益。
天氣做爲屬性節點的信息增益爲,Gain(D , 天氣)=0.985-0.965=0.020。
同理咱們能夠計算出其餘屬性做爲根節點的信息增益,它們分別爲 :
Gain(D , 溫度)=0.128
Gain(D , 溼度)=0.020
Gain(D , 颳風)=0.020
咱們能看出來溫度做爲屬性的信息增益最大。由於 ID3 就是要將信息增益最大的節點做爲父節點,這樣能夠獲得純度高的決策樹,因此咱們將溫度做爲根節點。
而後咱們要將第一個葉節點,也就是 D1={1-,2-,3+,4+}進一步進行分裂,往下劃分,計算其不一樣屬性(天氣、溼度、颳風)做爲節點的信息增益,能夠獲得:
Gain(D , 天氣)=0
Gain(D , 溼度)=0
Gain(D , 颳風)=0.0615
咱們能看到颳風爲 D1 的節點均可以獲得最大的信息增益,這裏咱們選取颳風做爲節點。同理,咱們能夠按照上面的計算步驟獲得完整的決策樹。
因而咱們經過 ID3 算法獲得了一棵決策樹。ID3 的算法規則相對簡單,可解釋性強。一樣也存在缺陷,好比咱們會發現 ID3 算法傾向於選擇取值比較多的屬性。這樣,若是咱們把「編號」做爲一個屬性(通常狀況下不會這麼作,這裏只是舉個例子),那麼「編號」將會被選爲最優屬性 。但實際上「編號」是無關屬性的,它對「打籃球」的分類並無太大做用。
因此 ID3 有一個缺陷就是,有些屬性可能對分類任務沒有太大做用,可是他們仍然可能會被選爲最優屬性。這種缺陷不是每次都會發生,只是存在必定的機率。在大部分狀況下,ID3 都能生成不錯的決策樹分類。針對可能發生的缺陷,後人提出了新的算法進行改進。
2.C4.5
C4.5是在ID3的基礎上作了改進。 那麼 C4.5 都在哪些方面改進了 ID3 呢?
1)採用信息增益率
由於 ID3 在計算的時候,傾向於選擇取值多的屬性。爲了不這個問題,C4.5 採用信息增益率的方式來選擇屬性。信息增益率 = 信息增益 / 屬性熵,具體的計算公式這裏省略。
當屬性有不少值的時候,至關於被劃分紅了許多份,雖然信息增益變大了,可是對於 C4.5 來講,屬性熵也會變大,因此總體的信息增益率並不大。
2)採用悲觀剪枝
ID3 構造決策樹的時候,容易產生過擬合的狀況。在 C4.5 中,會在決策樹構造以後採用悲觀剪枝(PEP),這樣能夠提高決策樹的泛化能力。
悲觀剪枝是後剪枝技術中的一種,經過遞歸估算每一個內部節點的分類錯誤率,比較剪枝先後這個節點的分類錯誤率來決定是否對其進行剪枝。這種剪枝方法再也不須要一個單獨的測試數據集。
3)離散化處理連續屬性
C4.5 能夠處理連續屬性的狀況,對連續的屬性進行離散化的處理。好比打籃球存在的「溼度」屬性,不按照「高、中」劃分,而是按照溼度值進行計算,那麼溼度取什麼值都有可能。該怎麼選擇這個閾值呢, C4.5 選擇具備最高信息增益的劃分所對應的閾值。
4)處理缺失值
針對數據集不完整的狀況,C4.5 也能夠進行處理。
假如咱們獲得的是以下的數據,你會發現這個數據中存在兩點問題。第一個問題是,數據集中存在數值缺失的狀況,如何進行屬性選擇?第二個問題是,假設已經作了屬性劃分,可是樣本在這個屬性上有缺失值,該如何對樣本進行劃分?
咱們不考慮缺失的數值,能夠獲得溫度 D={2-,3+,4+,5-,6+,7-}。溫度 = 高:D1={2-,3+,4+} ;溫度 = 中:D2={6+,7-};溫度 = 低:D3={5-} 。這裏 + 號表明打籃球,- 號表明不打籃球。好比 ID=2 時,決策是不打籃球,咱們能夠記錄爲 2-。
因此三個葉節點的信息熵能夠結算爲:
這三個節點的歸一化信息熵爲 3/6*0.918+2/6*1.0+1/6*0=0.792。
針對將屬性選擇爲溫度的信息增益率爲:
Gain(D′, 溫度)=Ent(D′)-0.792=1.0-0.792=0.208
D′的樣本個數爲 6,而 D 的樣本個數爲 7,因此所佔權重比例爲 6/7,因此 Gain(D′,溫度) 所佔權重比例爲 6/7,因此:
Gain(D, 溫度)=6/7*0.208=0.178
這樣即便在溫度屬性的數值有缺失的狀況下,咱們依然能夠計算信息增益,並對屬性進行選擇。
3.CART
CART 算法,英文全稱叫作 Classification And Regression Tree,中文叫作分類迴歸樹。ID3 和 C4.5 算法能夠生成二叉樹或多叉樹,而 CART 只支持二叉樹。同時 CART 決策樹比較特殊,既能夠做分類樹,又能夠做迴歸樹。
那麼你首先須要瞭解的是,什麼是分類樹,什麼是迴歸樹呢?
我用下面的訓練數據舉個例子,你能看到不一樣職業的人,他們的年齡不一樣,學習時間也不一樣。若是我構造了一棵決策樹,想要基於數據判斷這我的的職業身份,這個就屬於分類樹,由於是從幾個分類中來作選擇。若是是給定了數據,想要預測這我的的年齡,那就屬於迴歸樹。
分類樹能夠處理離散數據,也就是數據種類有限的數據,它輸出的是樣本的類別,而回歸樹能夠對連續型的數值進行預測,也就是數據在某個區間內都有取值的可能,它輸出的是一個數值。
1)CART 分類樹的工做流程
CART 分類樹與 C4.5 算法相似,只是屬性選擇的指標採用的是基尼係數。
你可能在經濟學中聽過說基尼係數,它是用來衡量一個國家收入差距的經常使用指標。當基尼係數大於 0.4 的時候,說明財富差別懸殊。基尼係數在 0.2-0.4 之間說明分配合理,財富差距不大。
基尼係數自己反應了樣本的不肯定度。當基尼係數越小的時候,說明樣本之間的差別性小,不肯定程度低。分類的過程自己是一個不肯定度下降的過程,即純度的提高過程。因此 CART 算法在構造分類樹的時候,會選擇基尼係數最小的屬性做爲屬性的劃分。
咱們接下來詳解了解一下基尼係數。基尼係數很差懂,你最好跟着例子一塊兒手動計算下。
假設 t 爲節點,那麼該節點的 GINI 係數的計算公式爲:
這裏 p(Ck|t) 表示節點 t 屬於類別 Ck 的機率,節點 t 的基尼係數爲 1 減去各種別 Ck 機率平方和。
經過下面這個例子,咱們計算一下兩個集合的基尼係數分別爲多少:
集合 1:6 個都去打籃球;
集合 2:3 個去打籃球,3 個不去打籃球。
針對集合 1,全部人都去打籃球,因此 p(Ck|t)=1,所以 GINI(t)=1-1=0。
針對集合 2,有一半人去打籃球,而另外一半不去打籃球,因此,p(C1|t)=0.5,p(C2|t)=0.5,GINI(t)=1-(0.5*0.5+0.5*0.5)=0.5。
經過兩個基尼係數你能夠看出,集合 1 的基尼係數最小,也證實樣本最穩定,而集合 2 的樣本不穩定性更大。
在 CART 算法中,基於基尼係數對特徵屬性進行二元分裂,假設屬性 A 將節點 D 劃分紅了 D1 和 D2,以下圖所示:
節點 D 的基尼係數等於子節點 D1 和 D2 的歸一化基尼係數之和,用公式表示爲:
歸一化基尼係數表明的是每一個子節點的基尼係數乘以該節點佔總體父親節點 D 中的比例。
上面咱們已經計算了集合 D1 和集合 D2 的 GINI 係數,獲得:
因此節點 D 的基尼係數爲:
節點 D 被屬性 A 劃分後的基尼係數越大,樣本集合的不肯定性越大,也就是不純度越高。
2) 如何使用 CART 算法來建立分類樹
經過上面的講解你能夠知道,CART 分類樹其實是基於基尼係數來作屬性劃分的。在 Python 的 sklearn 中,若是咱們想要建立 CART 分類樹,能夠直接使用 DecisionTreeClassifier 這個類。建立這個類的時候,默認狀況下 criterion 這個參數等於 gini,也就是按照基尼係數來選擇屬性劃分,即默認採用的是 CART 分類樹。
下面,咱們來用 CART 分類樹,給 iris 數據集構造一棵分類決策樹。iris 這個數據集,我在 Python 可視化中講到過,實際上在 sklearn 中也自帶了這個數據集。基於 iris 數據集,構造 CART 分類樹的代碼以下:
1 # encoding=utf-8
2 from sklearn.model_selection import train_test_split 3 from sklearn.metrics import accuracy_score 4 from sklearn.tree import DecisionTreeClassifier 5 from sklearn.datasets import load_iris 6 # 準備數據集
7 iris=load_iris() 8 # 獲取特徵集和分類標識
9 features = iris.data 10 labels = iris.target 11 # 隨機抽取 33% 的數據做爲測試集,其他爲訓練集
12 train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size=0.33, random_state=0) 13 # 建立 CART 分類樹
14 clf = DecisionTreeClassifier(criterion='gini') 15 # 擬合構造 CART 分類樹
16 clf = clf.fit(train_features, train_labels) 17 # 用 CART 分類樹作預測
18 test_predict = clf.predict(test_features) 19 # 預測結果與測試集結果做比對
20 score = accuracy_score(test_labels, test_predict) 21 print("CART 分類樹準確率 %.4lf" % score)
運行結果:
1 CART 分類樹準確率 0.9600
3) CART 迴歸樹的工做流程
CART 迴歸樹劃分數據集的過程和分類樹的過程是同樣的,只是迴歸樹獲得的預測結果是連續值,並且評判「不純度」的指標不一樣。在 CART 分類樹中採用的是基尼係數做爲標準,那麼在 CART 迴歸樹中,如何評價「不純度」呢?實際上咱們要根據樣本的混亂程度,也就是樣本的離散程度來評價「不純度」。
樣本的離散程度具體的計算方式是,先計算全部樣本的均值,而後計算每一個樣本值到均值的差值。咱們假設 x 爲樣本的個體,均值爲 u。爲了統計樣本的離散程度,咱們能夠取差值的絕對值,或者方差。
其中差值的絕對值爲樣本值減去樣本均值的絕對值:
方差爲每一個樣本值減去樣本均值的平方和除以樣本個數:
因此這兩種節點劃分的標準,分別對應着兩種目標函數最優化的標準,即用最小絕對誤差(LAD),或者使用最小二乘誤差(LSD)。這兩種方式均可以讓咱們找到節點劃分的方法,一般使用最小二乘誤差的狀況更常見一些。
咱們能夠經過一個例子來看下如何建立一棵 CART 迴歸樹來作預測。
4)如何使用 CART 迴歸樹作預測
這裏咱們使用到 sklearn 自帶的波士頓房價數據集,該數據集給出了影響房價的一些指標,好比犯罪率,房產稅等,最後給出了房價。
根據這些指標,咱們使用 CART 迴歸樹對波士頓房價進行預測,代碼以下:
1 # encoding=utf-8
2 from sklearn.metrics import mean_squared_error 3 from sklearn.model_selection import train_test_split 4 from sklearn.datasets import load_boston 5 from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error 6 from sklearn.tree import DecisionTreeRegressor 7 # 準備數據集
8 boston=load_boston() 9 # 探索數據
10 print(boston.feature_names) 11 # 獲取特徵集和房價
12 features = boston.data 13 prices = boston.target 14 # 隨機抽取 33% 的數據做爲測試集,其他爲訓練集
15 train_features, test_features, train_price, test_price = train_test_split(features, prices, test_size=0.33) 16 # 建立 CART 迴歸樹
17 dtr=DecisionTreeRegressor() 18 # 擬合構造 CART 迴歸樹
19 dtr.fit(train_features, train_price) 20 # 預測測試集中的房價
21 predict_price = dtr.predict(test_features) 22 # 測試集的結果評價
23 print('迴歸樹二乘誤差均值:', mean_squared_error(test_price, predict_price)) 24 print('迴歸樹絕對值誤差均值:', mean_absolute_error(test_price, predict_price))
運行結果(每次運行結果可能會有不一樣):
1 ['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO' 'B' 'LSTAT'] 2 迴歸樹二乘誤差均值: 23.80784431137724
3 迴歸樹絕對值誤差均值: 3.040119760479042
5) CART 決策樹的剪枝
CART 決策樹的剪枝主要採用的是 CCP 方法,它是一種後剪枝的方法,英文全稱叫作 cost-complexity prune,中文叫作代價複雜度。這種剪枝方式用到一個指標叫作節點的表面偏差率增益值,以此做爲剪枝先後偏差的定義。用公式表示則是:
其中 Tt 表明以 t 爲根節點的子樹,C(Tt) 表示節點 t 的子樹沒被裁剪時子樹 Tt 的偏差,C(t) 表示節點 t 的子樹被剪枝後節點 t 的偏差,|Tt|代子樹 Tt 的葉子數,剪枝後,T 的葉子數減小了|Tt|-1。
因此節點的表面偏差率增益值等於節點 t 的子樹被剪枝後的偏差變化除以剪掉的葉子數量。
由於咱們但願剪枝先後偏差最小,因此咱們要尋找的就是最小α值對應的節點,把它剪掉。這時候生成了第一個子樹。重複上面的過程,繼續剪枝,直到最後只剩下根節點,即爲最後一個子樹。
獲得了剪枝後的子樹集合後,咱們須要用驗證集對全部子樹的偏差計算一遍。能夠經過計算每一個子樹的基尼指數或者平方偏差,取偏差最小的那個樹,獲得咱們想要的結果。
4、總結
最後咱們來整理下三種決策樹之間在屬性選擇標準上的差別:
ID3 算法,基於信息增益作判斷;
C4.5 算法,基於信息增益率作判斷;
CART 算法,分類樹是基於基尼係數作判斷。迴歸樹是基於誤差作判斷。