決策樹演算法

newknight發表於2022-03-07

決策樹演算法是一種通用的機器學習演算法,既可以執行分類也可以執行迴歸任務,同時也是一種可以擬合複雜資料集的功能強大的演算法;

一、視覺化決策樹模型

通過以下程式碼,我們使用iris資料集構建一個決策樹模型,我們使用資料的後兩個維度並設定決策樹的最大深度為2,最後通過export出iris_tree.dot檔案;

DecisionTreeClassifier初始化中的random_state可以確保每次執行結果的不變性;

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_graphviz

iris = load_iris()
x = iris.data[:,2:]
y = iris.target

tree_clf = DecisionTreeClassifier(max_depth=2, random_state=12)
tree_clf.fit(x, y)
r = export_graphviz(tree_clf,
                out_file='iris_tree.dot',
                feature_names= iris.feature_names[2:],
                class_names= iris.target_names,
                rounded=True,
                filled=True
                )

然後通過命令列使用Graphviz軟體包中的dot命令列工具將生成的iris_tree.dot檔案轉換為圖片檔案

dot -Tpng iris_tree.dot -o iris_tree1.png

我們可以看到基於iris資料集生成的決策樹結構如下圖所示

image

二、決策樹節點結構分析

通過生成的決策樹結構圖片,可以看到非葉子節點都會有一個判斷條件,通過這個判斷條件來決定轉移到的子節點;

每個節點的samples記錄了該節點訓練使用的樣本數量;例如根節點輸入的訓練樣本有150個,最終符合判斷條件的50個樣本流入了左側的葉子節點,不符合根節點判斷條件的100個樣本流入右側的節點;

每個節點的value記錄了該節點參與訓練的樣本中每個類別的數量,例如其實輸入的150個樣本中每個分類都是50個;

每個節點的class記錄了該節點樣本所屬的類別;

每個節點的gini記錄了該節點的基尼不純度,其計算公式如下,其中pi,k代表第i個節點中每個分類所佔的比例;

\[G_{i} = 1 - \sum_{k=1}^{n} p_{i,k}^{2} \]

例如最後左下層的節點的基尼不純度為

\[G = 1 -(0/54)^{2} - (49/54)^{2} - (5/54)^{2} = 0.168 \]

處理使用基尼不純度進行測量之外,也可以使用資訊熵來度量節點樣本的有序性,由於兩者比較類似不進行詳細介紹;

我們可以通過兩個屬性petal length、petal width的二維平面更加清晰看到決策樹的決策邊界;在petal length = 2.75地方,將整個二維平面分成左右兩個區域,左側區域已經是純粹的class = setosa,右側區域通過petal width = 1.75分成上下兩部分;由於我們設定max_depth=2,則決策樹到此為止;

image

三、決策樹預測過程

通過生成的決策樹的結構圖,當我們接收到一朵新的iris花朵要進行預測的時候,只需要按照樹的結構從上到下依次進行判斷即可;對於我們先前生成的決策樹,首先會從決策樹的根節點開始,檢視新記錄的petal lenght是否小於等於2.45,如果小於則轉移到左側的節點,從而可以確認花朵的型別為setosa;如果petal length的長度大於2.45,則移動到右側節點,接著判斷petal width是否小於等於1.75,如果小於則轉移到當前節點的左子節點,此時新記錄的型別為versicolor,否則新記錄的型別是virginica型別;

通過二中對決策樹節點結構的分析,雖然每個葉子節點只對應某一個分類,但是最終的value卻可能有不同的分類,即葉子節點不可能都是純的;所以決策樹除了直接輸出輸入樣本對應的分類之外,也可以估算輸入樣本屬於特定分類的概率;

我們輸入樣本[5,1],通過決策樹的二維決策邊界平面圖,可以看到樣本落右下角的區域,通過生成的決策樹結構的圖片可以看到,其位於depth=1的左下角的節點,其value=[0, 49, 5]、samples=54,所以可以得到預測分類的概率為49/54=0.97,與以下計算輸出是相同的;

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_graphviz

iris = load_iris()
x = iris.data[:,2:]
y = iris.target

tree_clf = DecisionTreeClassifier(max_depth=2, random_state=12)
tree_clf.fit(x, y)
r = export_graphviz(tree_clf,
                out_file='iris_tree.dot',
                feature_names= iris.feature_names[2:],
                class_names= iris.target_names,
                rounded=True,
                filled=True
                )

print(tree_clf.predict_proba([[5, 1]]))
print(tree_clf.predict([[5, 1]]))


# [[0.         0.90740741 0.09259259]]
# [1]

四、決策樹決策特徵和條件的選擇

Scikit-Learn使用CART(分類和迴歸樹,Classfication and Regression Tree)演算法訓練決策樹,此演算法基於基尼不純度來衡量決策系統的純度;決策樹從本質上來說,就是要通過不同決策節點的分揀,盡最大的可能減少系統的不純程度,或者說盡快的最大程度的增加決策時通的純度;

對於某個特徵k及其閾值tk,基於其分裂的兩個子節點,分別計算基尼不純度並進行線性加和,並最小化這個相對子節點的基尼不純度;

\[G^{'}(k,t_{k}) = \frac{m_{left}}{m} G_{left} + \frac{m_{right}}{m} G_{right} \]

一旦CART演算法成功地將訓練集分為兩部分,它就會使用相同的邏輯將子集進行分割,然後再分割子集,以此類推。一旦達到最大深度(由超引數max_depth定義),或者找不到可減少不純度的分割,它將停止遞迴。其他一些超引數(稍後描述)可以控制其他一些停止條件(min_samples_split、min_samples_leaf、min_weight_fraction_leaf和max_leaf_nodes)。

五、決策樹計算複雜度

對新樣本進行預測需要從根節點開始遍歷決策樹,由於每個節點僅僅需要檢測節點對應的一個特徵值,只跟決策時的層數有關,而與樣本的維度數量沒有關係,故時間複雜度為,其中m為訓練樣本的數量

\[O(log_{2}(m)) \]

訓練演算法需要比較每個節點上所有樣本上的所有特徵(如果設定了max_features,則更少)。比較每個節點上所有樣本的所有特徵會導致訓練複雜度為O(n×m log2(m))。對於小訓練集(少於幾千個例項),Scikit-Learn可以通過對資料進行預排序(設定presort=True)來加快訓練速度,但是這樣做會大大降低大訓練集的訓練速度。

六、避免決策樹過擬合

決策樹基本上對訓練資料沒有任何的預先假設(比如線性模型就正好相反,它顯然假設資料是線性的)。如果不加以限制,樹的結構將跟隨訓練集變化,嚴密擬合,並且很可能過擬合;為避免過擬合,需要在訓練過程中降低決策樹的自由度;DecisionTreeClassifier類除了max_depth引數外,同樣可以通過min_samples_split(分裂前節點必須有的最小樣本數)、min_samples_leaf(葉節點必須有的最小樣本數量)、min_weight_fraction_leaf(與min_samples_leaf一樣,但表現為加權例項總數的佔比)、max_leaf_nodes(最大葉節點數量),以及max_features(分裂每個節點評估的最大特徵數量)來限制決策樹的形狀;

相關文章