(數據科學學習手札23)決策樹分類原理詳解&Python與R實現

  做爲機器學習中可解釋性很是好的一種算法,決策樹(Decision Tree)是在已知各類狀況發生機率的基礎上,經過構成決策樹來求取淨現值的指望值大於等於零的機率,評價項目風險,判斷其可行性的決策分析方法,是直觀運用機率分析的一種圖解法。因爲這種決策分支畫成圖形很像一棵樹的枝幹,故稱決策樹。在機器學習中,決策樹是一個預測模型,他表明的是對象屬性與對象值之間的一種映射關係。html

 

1、初識決策樹node

  決策樹是一種樹形結構,通常的,一棵決策樹包含一個根結點,若干個內部結點和若干個葉結點:算法

葉結點:樹的一個方向的最末端,表示結果的輸出;app

根結點:初始樣本全體;dom

內部結點:每一個內部結點對應一個屬性測試(即一次決策)機器學習

從根結點——每一個葉結點,造成各條斷定序列;咱們的進行決策樹分類器訓練的學習目的是產生一棵泛化能力強,即處理未見示例能力強的決策樹,其基本流程遵循「分而治之」的策略:tcp

算法過程:函數

  Step1:輸入樣本集D{(x1,y1),(x2,y2),...,(xn,yn)},屬性集A{a1,a2,...,ad},全體樣本集儲存在根結點中;post

  Step2:從屬性集A中通過必定的規則(具體規則由算法決定)挑選出一個最佳屬性a1,全部樣本從根結點流向該決策結點,根據樣本在a1這個屬性上的取值,流向對應的方向(以下圖):
性能

在樣本集經過某個屬性判斷,肯定不一樣的流向後,會有如下幾種狀況:

    1.流向某個方向的全部樣本只存在一個類別y0,這時把這個方向標記爲葉結點,即最終從這個方向流出的樣本均可直接斷定爲類別y0

    2.經過當前屬性判斷後,某個方向沒有樣本流出,這一般是樣本量不夠多致使的樣本多樣性不足,這時能夠將這方向標記爲葉結點,將訓練集中各種別的比例做爲先驗機率,將全部從這個方向流出的新樣本都標記爲先驗機率最大的那個類別;

    3.在某個屬性判斷上,全部訓練樣本都取同一個值,和狀況2類似,也是在其餘可能方向上無訓練樣本流出,在對新樣本處理時方法同2;

  Step3:經過Step2的過程將全部屬性利用完以後,造成了一棵完整的樹,其每一個判斷路徑上都通過了全部屬性,這時對全部的葉結點規定輸出類別爲訓練過程當中到達該葉結點中的樣本中比例最大(即利用了先驗分佈)的那一類,至此,一棵決策樹訓練完成。

 

2、訓練過程屬性的選擇

如今咱們知道了決策樹的訓練過程,但對於哪個屬性放在第一位,哪一個放在第二位以此類推,還依然不知曉,這就是決策樹中很是重要也很是巧妙的一點——劃分選擇;

劃分選擇:決策樹學習的關鍵是如何選擇最優劃分屬性,咱們但願隨着劃分過程不斷進行,決策樹的分支結點所包含的樣本儘量屬於同一類別,即結點的純度(purity)愈來愈高,下面咱們介紹幾種不一樣的衡量樣本純度的規則,他們也分別產生了不一樣的決策樹算法:

1.信息增益

在定義信息增益以前,咱們先介紹如下概念:

信息熵(information entropy):

度量樣本集合純度最經常使用的一種指標,假定當前樣本集合D中第k類樣本所佔的比例爲pk(k=1,2,...,|y|),則D的信息熵定義爲:

Ent(D)越小,D的純度越高,其中|y|表示屬性的可能取值數,假定對離散屬性a有V個可能的取值{a1,a2,...,aV},使用a來對樣本集D進行劃分,產生V個分支結點,其中第v個分枝結點流入D中全部在屬性a取值爲aV的樣本,記做DV,則屬性aD進行劃分所得到的信息增益爲:

其中|DV|D中在a屬性取aV的樣本數量,則|DV| / |D|可看做在aV方向上的權重;

*原則:信息增益越大,意味着使用a屬性進行劃分所劃得的「純度提高」最大,即當前最優劃分爲:

 

2.增益率

有些時候,若樣本集中含有「編號」這種使得分支結點純度遠大於其餘有效屬性的非有效屬性(由於編號會將每個樣本獨立分開),致使各個編號的分支能變成葉結點(對應特殊狀況中的1),這樣的決策樹顯然不具備泛化能力,沒法對新樣本進行預測,即,這種狀況下信息增益準則對可取值數目較多的屬性有所偏好,爲減小這種偏好可能帶來的不利影響,下面引入:

C4.5算法:

不直接使用信息增益,而是使用「增益率」來選擇當前最優劃分屬性;

增益率定義爲:

其中,

叫作屬性a固有值,屬性a的可能取值數目越大(即V越大),則IV(a)的值一般會越大;與信息增益相比,增益率對屬性取值數目較少的屬性有偏好,所以C4.5算法並不直接以全部屬性的增益率做爲比較依據,而是有一個啓發式的過程:先選擇候選劃分屬性中信息增益高於平均水平的屬性,再從中選擇增益率最高的。

 

3.基尼係數

CART決策樹(Classfication and Regression Tree)使用基尼指數來選擇劃分屬性,則數據D的純度可用基尼值來度量:

Gini(D)反映了從數據集D中抽取兩個樣本,其類別標記不一致的機率,即Gini(D)越小,數據集D的純度越高,則對一個屬性a,其基尼指數爲:

因此在候選屬性集合A中,選擇當前剩餘屬性中使得劃分後基尼指數最小的做爲當前最優劃分屬性,即:

 

 3、剪枝處理

   在決策樹學習中,爲了儘量正確分類訓練樣本,結點劃分過程不斷重複,有時會形成決策樹分支過多,這時就可能因訓練集過分學習,以至於把訓練集自己的一些特色看成全部數據都具備的通常性質,從而致使過擬合。

  經過主動去掉一些分支來下降過擬合的風險的過程就叫作剪枝。

決策樹剪枝的基本策略:

  1.預剪枝(prepruning)

在決策樹生成過程當中,對每一個結點在劃分前先進行性能估計,若當前結點的劃分不能帶來決策樹泛化性能的提高,則中止劃分並將當前結點標記爲葉結點;

  2.後剪枝(post-pruning)

先從訓練集生成一棵完整的決策樹,而後自底向上地對非葉結點進行考察,若將該結點對應的子樹替換成葉結點能帶來決策樹泛化能力提高,則將該子樹替換成葉結點。

預剪枝:

  步驟:

  Step1:爲衡量泛化能力,利用留出法,劃分樣本集爲訓練集和驗證集;

  Step2:根據信息增益準則,選出a*做爲根結點下第一個非葉結點,分別訓練經過這一屬性進行分類的模型,和將該結點做爲葉結點的模型,比較這兩個模型在驗證集上的正確率,選擇更優的方案;

  Step3:重複Step2對全部屬性進行考察,直到最終決策樹完成;

*僅有一層劃分的決策樹稱爲「決策樹樁」(decision stump)

  原則:剪去(淘汰)正確率小於或等於當前正確率(即當前最高正確率)的分支操做;

  優勢:預剪枝使得決策樹的不少分支沒有展開,下降了模型過擬合的風險,還顯著減小了決策樹的訓練時間開銷和測試時間開銷;

  缺點:有些分支的當前劃分雖不能提高泛化能力,甚至可能致使泛化能力暫時降低,但在其基礎上進行的後續劃分卻有可能致使性能顯著提高;

     預剪枝基於「貪心」本質禁止這些分支展開,只關心當前性能表現,給預剪枝決策樹模型帶來了欠擬合的風險。

 

後剪枝: 

  步驟:

  Step1:對於不經任何剪枝處理,僅依據某個信息純度評價方法最終造成的一棵完整的使用了全部屬性的決策樹,從其最靠後的非葉結點開始,分別訓練不剪去該結點和剪去該結點時的模型,比較泛化能力;

  Step2:若泛化能力獲得了提升,則採起相應的模型變動/維持原狀操做;

  Step3:重複上述過程直到全部非葉結點完成剪枝效果評估。

  原則:若剪枝後正確率獲得提升,則採起剪枝操做,不然不變;

  優勢:欠擬合風險很小,泛化能力每每優於預剪枝決策樹;

  缺點:後剪枝過程是在生成徹底決策樹以後進行的,而且需自底向上對樹中全部非葉結點進行逐一考察後,所以訓練時間開銷巨大。

 

以上就是決策樹算法的一些基本常識,下面咱們分別在Python和R中實現決策樹算法:

 

4、Python

  咱們利用sklearn模塊中的tree下屬的DecisionTreeClassifier()進行決策樹分類,關於其細節在sklearn的官網中有詳細介紹:http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier,下面咱們對其主要參數進行介紹:

criterion : 字符型,用來肯定劃分選擇依據的算法,有對應CART樹算法的「gini」和對應ID3算法的「entropy」,默認爲「gini」

splitter : 字符型,用來肯定選擇每一個屬性判斷結點的方式,依據的是criterion中肯定的指標數值,有對應最佳結點的「best」和對應隨機選擇的「random」,默認是「best」

max_depth :整型,用來肯定決策樹的最大深度(即最多的非葉結點數目規模),默認爲None,即不限制深度

min_samples_split :有兩種狀況,

  1.整型,這時該參數肯定用於分割非葉結點的最小樣本數,即若是小於該預設值,則該結點由於信息不足能夠直接根據先驗分佈生成爲葉結點輸出結果,默認值2;

  2.浮點型,這時該參數功能不變,只是肯定的min_samples_split變爲min_samples_split*n_samples,這裏表明百分比。

min_samples_leaf :有兩種狀況,

  1.整型,這時該參數肯定用於生成葉結點的最小樣本數,即小於該數值時不可生成葉結點,默認值爲1;

  2.浮點型,同min_samples_split

min_weight_fraction_leaf :浮點型,該參數用於肯定每一個樣品的權重,在最終在葉結點產生結果時起做用,主要用於類別不平衡時的再縮放操做,默認每一個樣品權重相等;

max_features : 該參數用於肯定每一次非葉結點屬性劃分時使用到的屬性數目(在信息增益和基尼指數的計算中起做用),默認使用所有屬性,有如下幾種狀況:

  1.整型,這時傳入的整數即爲每次分割時考慮的最大屬性數;

  2.浮點型,這時最大屬性數是該浮點參數*屬性總數;

  3.字符型,「auto」時,最大屬性數爲屬性總數開根號;「sqrt」時,同「auto」;「log2」時,最大屬性數爲屬性總數取對數;

  4.None,這時最大屬性數即爲屬性總數;

 max_leaf_nodes : 該參數用於肯定最終的決策樹模型的最大葉結點數量,默認爲無限制,即None

 class_weight :用於處理類別不平衡問題的權重,建議使用「balanced」,即自動根據先驗分佈賦權,默認爲None,即忽略權重,每一類同等看待

以上就是sklearn.tree.DecisionTreeClassifier的主要參數介紹,下面咱們以kaggle playground中的泰坦尼克號遇難者數據做爲演示數據對生還與否進行二分類:

數聽說明:

 代碼:

 

from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split import pandas as pd import numpy as np '''讀入數據''' raw_train_data = pd.read_csv('train.csv') train = raw_train_data.dropna() target_train = train['Survived'].tolist() #Ticket class
pclass = train['Pclass'].tolist() sex = train['Sex'].tolist() Sex = [] for i in range(len(sex)): if sex[i] == 'male': Sex.append(1) else: Sex.append(0) age = train['Age'].tolist() #在船上兄弟姐妹的數量
SibSp = train['SibSp'].tolist() #在船上父母或孩子的數量
Parch = train['Parch'].tolist() Fare = train['Fare'].tolist() #登船的港口
Embarked = train['Embarked'].tolist() sabor_C = [] sabor_Q = [] #爲登船港口設置啞變量
for i in range(len(Embarked)): if Embarked[i] == 'C': sabor_C.append(1) sabor_Q.append(0) elif Embarked[i] == 'Q': sabor_Q.append(1) sabor_C.append(0) else: sabor_Q.append(0) sabor_C.append(0) '''定義自變量與目標''' train_ = np.array([Sex,age,sabor_C,sabor_Q]).T target_ = np.array(target_train) '''重複屢次隨機分割樣本集的訓練取正確率平均值''' S = [] for i in range(1000): X_train, X_test, y_train, y_test = train_test_split(train_, target_, test_size=0.3) clf = DecisionTreeClassifier(class_weight='balanced',max_depth=2) clf = clf.fit(X_train,y_train) S.append(clf.score(X_test,y_test)) '''打印結果'''
print('平均正確率:'+str(np.mean(S)))

 

訓練效果:

 利用訓練好的模型不只能夠輸出新樣本值類別的預測值,還能夠輸出對應的機率,這個機率便是葉結點的先驗分佈:

'''打印對應預測類別的機率''' clf.predict_proba(X_test)

 咱們都知道決策樹是利用與各座標軸平行的線段拼湊而成組成決策邊界來劃分樣本點,在自變量個數很少時咱們能夠繪製出決策邊界的示意圖來增強可解釋性,下面以鳶尾花數據爲例:

print(__doc__) import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.tree import DecisionTreeClassifier # Parameters
n_classes = 3 plot_colors = "ryb" plot_step = 0.02

# Load data
iris = load_iris() for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]): # 對變量中全部兩兩變量間進行匹配
    X = iris.data[:, pair] y = iris.target # Train
    clf = DecisionTreeClassifier().fit(X, y) # 繪製決策邊界
    plt.subplot(2, 3, pairidx + 1) x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step), np.arange(y_min, y_max, plot_step)) plt.tight_layout(h_pad=0.5, w_pad=0.5, pad=2.5) Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) cs = plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu) plt.xlabel(iris.feature_names[pair[0]]) plt.ylabel(iris.feature_names[pair[1]]) # 繪製訓練樣本點
    for i, color in zip(range(n_classes), plot_colors): idx = np.where(y == i) plt.scatter(X[idx, 0], X[idx, 1], c=color, label=iris.target_names[i], cmap=plt.cm.RdYlBu, edgecolor='black', s=15) plt.suptitle("Decision surface of a decision tree using paired features") plt.legend(loc='lower right', borderpad=0, handletextpad=0) plt.axis("tight") plt.show()

 

 

5、R

在R中使用決策樹相關算法有一個很大的方便之處,就是在對決策樹可視化的時候,咱們都知道決策樹是一種解釋性很強的機器學習算法,這是它被普遍使用的一個緣由之一,在R中繪製決策樹很是方便;在R中,一棵決策樹的初步生成與剪枝是使用兩個不一樣的函數進行操做的,咱們這裏使用rpart包來建立分類樹,其中rpart()函數建立決策樹,prune()函數用來進行樹的剪枝,具體參數以下:

對rpart():

formula:這是R中不少算法的輸入格式,用~鏈接左端的target列名稱和右端的自變量列名稱;

data:輸入數據框的名稱;

weights:可選的自定義類別權重,主要在類別不平衡時使用,相似邏輯分類中的再縮放;

na.action:對缺失值進行處理,默認刪去target列缺失的樣本,但保留自變量存在缺失的樣本(決策樹中對缺失值較爲寬容,有對應的處理方法)

parms:默認爲「gini」指數,即CART決策樹分割結點的方法;

control:這是一個很是重要的參數集合,與Python在主體函數中賦參不一樣,rpart中關於決策樹的調參都集合在這個control參數中,control的賦值格式爲control=rpart.control(),對於rpart.control能夠調節的參數以下:

  minspilt:整數,默認爲20,表示對節點中樣本進行劃分的最小樣本數,小於這個數目則直接根據先驗分佈生成葉結點;

  minbucket:整數,默認值爲round(minspilt/3),表示一個閾值,若當前結點樣本數小於這個閾值,則生成葉結點;

  cp:複雜度,默認0.01;

  maxcompete:在每次節點劃分中選擇使用的變量個數,默認爲4,用於計算信息增益指標;

  xval:交叉驗證的數量,默認10,即十折交叉驗證;

  maxdepth:控制決策樹的最大深度,這個最大深度指的是全部葉結點中距離根結點最遠的距值,因此決策樹樁深度爲0;

 對prune():

tree:指定先前保存生成好的決策樹的變量名;

cp:複雜度,默認0.1,用於決定對決策樹裁剪的程度;

下面咱們以Fisher的鳶尾花數據進行決策樹分類演示:

> rm(list=ls()) > library(rpart.plot) > library(rpart) > #掛載數據
> data(iris) > data <- iris > #留出法抽出0.8的訓練集與0.2的測試集
> sam <- sample(1:150,120) > train_data <- data[sam,] > test_data <- data[-sam,] > #訓練決策樹
> dtree <- rpart(Species~.,data=train_data) > #繪製決策樹複雜度變化狀況
> plotcp(dtree) > #進行剪枝,這裏設置複雜度閾值爲0.01
> dtree.pruned <- prune(dtree, cp=0.01) > #繪製剪枝後的決策樹模型結構
> prp(dtree.pruned,type=0) > title('Decision Tree for Iris Data') > #對驗證集代入訓練好的模型進行預測
> dtree.pred <- predict(dtree.pruned,test_data[,1:4],type='class') > #打印混淆矩陣
> (dtree.perf <- table(test_data[,5],dtree.pred)) dtree.pred setosa versicolor virginica setosa 10 0 0 versicolor 0 10         2 virginica 0 0 8

決策樹結構:

 

 能夠看出,決策樹在該數據集上的預測效果很是不錯,且只使用了有效的數據,節省了計算時間成本。

 

6、關於決策樹實踐中的建議

本節內容選自sklearn官網決策樹頁面:http://scikit-learn.org/stable/modules/tree.html#tree-classification,由筆者自行摘抄翻譯:

  1.決策樹在應對高維數據時很容易過擬合,所以保持自變量個數和樣本個數間的比例很是重要,其實無論是對什麼預測算法,當樣本個數接近自變量個數時都容易發生過擬合;

  2.能夠考慮對自變量進行維數約簡,或進行特徵選擇,以最大程度保留較少更有說服力的自變量,以儘可能減小過擬合的風險;

  3.多使用決策樹結構的可視化方法,能夠幫助你理解你當前樹的生長效果,也能夠更好的與現實業務聯繫起來進行解釋;

  4.樹的深度(即距離最遠的葉結點對應距離根結點的距離)初始化設置爲3較好,再逐步增加該參數,觀察訓練效果的變化狀況,以做出最好的選擇,以及控制過擬合狀況;

  5.使用min_samples_spilt或與其等價的參數來控制生成葉結點時的樣本個數下限,由於一般該參數越小,樹過擬合的風險都越大,所以儘早生成葉結點能夠緩解對樣本數據獨特性的放大,進而減小過擬合風險;

  6.在訓練以前儘可能平衡類別間的比例,以免訓練結果由於類別的嚴重不平衡而產生虛假結果(好比樣本中9個正例1個反例,訓練出的模型所有歸類爲正例也能取得90%的正確率,但這不可靠),或者調節sample_weight來對全部類別進行再縮放;

 

以上就是決策樹的基本知識,如有筆誤,請指出。

相關文章
相關標籤/搜索