作為機器學習中可解釋性非常好的一種算法,決策樹(Decision Tree)是在已知各種情況發生概率的基礎上,通過構成決策樹來求取凈現值的期望值大於等於零的概率,評價項目風險,判斷其可行性的決策分析方法,是直觀運用概率分析的一種圖解法。由於這種決策分支畫成圖形很像一棵樹的枝干,故稱決策樹。在機器學習中,決策樹是一個預測模型,他代表的是對象屬性與對象值之間的一種映射關系。
一、初識決策樹
決策樹是一種樹形結構,一般的,一棵決策樹包含一個根結點,若干個內部結點和若干個葉結點:
葉結點:樹的一個方向的最末端,表示結果的輸出;
根結點:初始樣本全體;
內部結點:每個內部結點對應一個屬性測試(即一次決策)
從根結點——每個葉結點,形成各條判定序列;我們的進行決策樹分類器訓練的學習目的是產生一棵泛化能力強,即處理未見示例能力強的決策樹,其基本流程遵循“分而治之”的策略:
算法過程:
Step1:輸入樣本集D{(x1,y1),(x2,y2),...,(xn,yn)},屬性集A{a1,a2,...,ad},全體樣本集儲存在根結點中;
Step2:從屬性集A中經過一定的規則(具體規則由算法決定)挑選出一個最佳屬性a1,所有樣本從根結點流向該決策結點,根據樣本在a1這個屬性上的取值,流向對應的方向(如下圖):
在樣本集通過某個屬性判斷,確定不同的流向后,會有以下幾種情況:
1.流向某個方向的所有樣本只存在一個類別y0,這時把這個方向標記為葉結點,即最終從這個方向流出的樣本都可直接判定為類別y0;
2.通過當前屬性判斷后,某個方向沒有樣本流出,這通常是樣本量不夠多導致的樣本多樣性不足,這時可以將這方向標記為葉結點,將訓練集中各類別的比例作為先驗概率,將所有從這個方向流出的新樣本都標記為先驗概率最大的那個類別;
3.在某個屬性判斷上,所有訓練樣本都取同一個值,和情況2相似,也是在其他可能方向上無訓練樣本流出,在對新樣本處理時方法同2;
Step3:通過Step2的過程將所有屬性利用完之后,形成了一棵完整的樹,其每個判斷路徑上都經過了所有屬性,這時對所有的葉結點規定輸出類別為訓練過程中到達該葉結點中的樣本中比例最大(即利用了先驗分布)的那一類,至此,一棵決策樹訓練完成。
二、訓練過程屬性的選擇
現在我們知道了決策樹的訓練過程,但對於哪一個屬性放在第一位,哪個放在第二位以此類推,還依然不知曉,這就是決策樹中非常重要也非常巧妙的一點——划分選擇;
划分選擇:決策樹學習的關鍵是如何選擇最優划分屬性,我們希望隨着划分過程不斷進行,決策樹的分支結點所包含的樣本盡可能屬於同一類別,即結點的純度(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,則屬性a對D進行划分所獲得的信息增益為:
其中|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中,選擇當前剩余屬性中使得划分后基尼指數最小的作為當前最優划分屬性,即:
三、剪枝處理
在決策樹學習中,為了盡可能正確分類訓練樣本,結點划分過程不斷重復,有時會造成決策樹分支過多,這時就可能因訓練集過度學習,以致於把訓練集本身的一些特點當作所有數據都具有的一般性質,從而導致過擬合。
通過主動去掉一些分支來降低過擬合的風險的過程就叫做剪枝。
決策樹剪枝的基本策略:
1.預剪枝(prepruning)
在決策樹生成過程中,對每個結點在划分前先進行性能估計,若當前結點的划分不能帶來決策樹泛化性能的提升,則停止划分並將當前結點標記為葉結點;
2.后剪枝(post-pruning)
先從訓練集生成一棵完整的決策樹,然后自底向上地對非葉結點進行考察,若將該結點對應的子樹替換成葉結點能帶來決策樹泛化能力提升,則將該子樹替換成葉結點。
預剪枝:
步驟:
Step1:為衡量泛化能力,利用留出法,划分樣本集為訓練集和驗證集;
Step2:根據信息增益准則,選出a*作為根結點下第一個非葉結點,分別訓練通過這一屬性進行分類的模型,和將該結點作為葉結點的模型,比較這兩個模型在驗證集上的正確率,選擇更優的方案;
Step3:重復Step2對所有屬性進行考察,直到最終決策樹完成;
*僅有一層划分的決策樹稱為“決策樹樁”(decision stump)
原則:剪去(淘汰)正確率小於或等於當前正確率(即當前最高正確率)的分支操作;
優點:預剪枝使得決策樹的很多分支沒有展開,降低了模型過擬合的風險,還顯著減少了決策樹的訓練時間開銷和測試時間開銷;
缺點:有些分支的當前划分雖不能提升泛化能力,甚至可能導致泛化能力暫時下降,但在其基礎上進行的后續划分卻有可能導致性能顯著提升;
預剪枝基於“貪心”本質禁止這些分支展開,只關心當前性能表現,給預剪枝決策樹模型帶來了欠擬合的風險。
后剪枝:
步驟:
Step1:對於不經任何剪枝處理,僅依據某個信息純度評價方法最終形成的一棵完整的使用了所有屬性的決策樹,從其最靠后的非葉結點開始,分別訓練不剪去該結點和剪去該結點時的模型,比較泛化能力;
Step2:若泛化能力得到了提高,則采取相應的模型變更/維持原狀操作;
Step3:重復上述過程直到所有非葉結點完成剪枝效果評估。
原則:若剪枝后正確率得到提高,則采取剪枝操作,否則不變;
優點:欠擬合風險很小,泛化能力往往優於預剪枝決策樹;
缺點:后剪枝過程是在生成完全決策樹之后進行的,並且需自底向上對樹中所有非葉結點進行逐一考察后,因此訓練時間開銷巨大。
以上就是決策樹算法的一些基本常識,下面我們分別在Python和R中實現決策樹算法:
四、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()
五、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
決策樹結構:
可以看出,決策樹在該數據集上的預測效果非常不錯,且只使用了有效的數據,節省了計算時間成本。
六、關於決策樹實踐中的建議
本節內容選自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來對所有類別進行再縮放;
以上就是決策樹的基本知識,若有筆誤,請指出。