原文章為scikit-learn中"用戶指南"-->"監督學習的第十節:Decision Trees"######
決策樹(Decision Trees ,DTs)是一組用于分類和回歸的無參監督學習。它們的目標是創建一個模型,然后這個模型通過從數據特征學習出一套簡單的決策規則后,來預測出目標值。
以下方的實例為例子,決策樹從數據特征中近似地獲得一條由一組** 如果...就...否則... **這樣的決策規則組成的正弦曲線。隨著樹的深度增加,其決策規則和擬合模型就會變得越來越復雜。
決策樹的優點有:
- 能夠簡單的解釋和理解其原理,并且能夠以可視化的形式來顯示決策樹。
- 只需要對數據進行很少的準備工作。其他技術通常需要正則化后的數據,缺失的數據則需要添加假數據或者是刪除空值。不過要注意的是這個模塊還不支持處理缺失的數據。
- 樹的計算代價(例如預測數據)是用于訓練的樹中,數據點數量的對數。
- 能夠同時處理數值型和連續型的數據。其他技術通常只支持一種數據類型。可以查看算法文章以獲得更多的信息。
- 可以處理帶有多輸出的問題。
- 白盒模型。如果在模型中可以觀察到特定情況,那么就可以通過簡單的布爾邏輯條件來表達出這一情況。相比之下,黑盒模型(例如人造神經網絡)的結果可能就不是那么容易就可以表現出來了。
- 可以使用統計測試來驗證模型。這使得模型能夠更具有可靠性。
- 能夠良好的辨別出人造的非法數據。
然后其缺點也有:
- 決策樹學習器能夠為當前問題創建出一個超復雜的樹,但是這個樹的泛化能力卻很差,這種低效的現象稱之為過擬合。可以通過對葉子節點設置最小值或者是給樹的深度設置最大值這樣的一些"修剪機制"來避免過擬合現象(暫時還不支持直接設置,需要手動修剪)。
- 決策樹是不穩定的。可能會因為一個細微的改變而產生一個完全不同的樹。不過這個問題可以通過樹的整體方法來緩解。(即取多個生成樹的均值)
- 能否生成一個最優決策樹的問題在于,不管你是需要一個在多方面都是最優的決策樹,還是說僅僅只需要一個理論上最優的決策樹,他都是一個NP完全問題。所以在實際使用中的決策樹學習算法都是基于像啟發式算法般這樣的貪婪算法,使得在每個節點上都取其局部最優值。當然,使用這樣的算法并不能保證產生的樹是一個全局最優樹,所以可以讓一組訓練器訓練出多個樹,其中訓練器的特征和數據點都使用隨機獲取。然后再去評估這些樹以選出"全局最優樹"。
- 有一些概念使用決策樹并不能很好的去學習,像異或,奇偶性和復用器等問題。
- 決策樹學習器會在偏斜數據集(某一類的數據占了多數)中創建出一個偏斜樹。所以嚴重建議在擬合之前對數據集進行平衡。
1.10.1. 分類#
DecisionTreeClassifier 是一個有能力從數據集產生多元分類的分類器。跟其他分類器一樣,DecisionTreeClassifier需要兩個輸入數組:攜帶訓練數據的稀疏或密集的數組** X ,尺寸為[樣本數量, 特征數量],和一個為X提供類標簽的整數數組 Y **,尺寸為[樣本數量]。
>>> from sklearn import tree
>>> X = [[0, 0], [1, 1]]
>>> Y = [0, 1]
>>> clf = tree.DecisionTreeClassifier()
>>> clf = clf.fit(X, Y)
模型在擬合過后,這個模型就能夠用來預測樣本的類:
>>> clf.predict([[2., 2.]])
array([1])
另一種預測方法能夠輸出每個類的對應概率,其中這個概率指的是在同一個葉子節點中,訓練樣本對應各類的分數:
>>> clf.predict_proba([[2., 2.]])
array([[ 0., 1.]])
DecisionTreeClassifier是一個有能力處理二元分類(也就是只有[-1, 1]的標簽)和多元分類(標簽的范圍是[0, ..., K-1])的一個分類器。
在使用Sklearn的鳶尾花數據庫上,我們可以使用下列步驟來構造出一個樹:
>>> from sklearn.datasets import load_iris
>>> from sklearn import tree
>>> iris = load_iris()
>>> clf = tree.DecisionTreeClassifier()
>>> clf = clf.fit(iris.data, iris.target)
在這個模型經過訓練之后,我們可以使用 export_graphviz 導出函數來將其導出成 Graphviz 格式的文件。
然后下面代碼展示了如何在經過鳶尾花數據庫上的樹的導出:
>>> with open("iris.dot", 'w') as f:
... f = tree.export_graphviz(clf, out_file=f)
然后我們可以使用 Graphviz 的 dot 工具來創建一個PDF文件(或者是其他支持的文件格式): dot -Tpdf iris.dot -o iris.pdf。
>>> import os
>>> os.unlink('iris.dot')
另一種方法是,如果我們已經安裝了 pydotplus這個Python模塊,我們可以在Python中直接生成PDF文件(或者是其他受支持的文件格式):
>>> import pydotplus
>>> dot_data = tree.export_graphviz(clf, out_file=None)
>>> graph = pydotplus.graph_from_dot_data(dot_data)
>>> graph.write_pdf("iris.pdf")
export_graphviz導出器也支持一些"藝術性"的功能,包括為他們的分類添加顏色(或者是回歸中的值)或者是突出想要顯示變量 與 類名。IPython notebooks 同樣能夠使用 Image() 方法來渲染并在行內顯示。
>>> from IPython.display import Image
>>> dot_data = tree.export_graphviz(clf, out_file=None, feature_names=iris.feature_names, class_names=iris.target_names, filled=True, rounded=True, special_characters=True)
>>> graph = pydotplus.graph_from_dot_data(dot_data)
>>> Image(graph.create_png())
模型在擬合過后,這個模型就能夠用來預測樣本的類:
>>> clf.predict(iris.data[:1, :])
array([0])
另一種預測方法能夠輸出每個類的對應概率,其中這個概率指的是在同一個葉子節點中,訓練樣本對應各類的分數:
>>> clf.predict_proba(iris.data[:1, :])
array([[ 1., 0., 0.]])
決策樹同樣能夠用來處理回歸問題,使用DecisionTreeRegressor這個類.
跟分類中的設置一樣,這個類的擬合函數也需要兩個數組(** X 與 y ),只不過這里的 y **是浮點數數組而不是整數數組:
>>> from sklearn import tree
>>> X = [[0, 0], [2, 2]]
>>> y = [0.5, 2.5]
>>> clf = tree.DecisionTreeRegressor()
>>> clf = clf.fit(X, y)
>>> clf.predict([[1, 1]])
array([ 0.5])
示例
決策樹回歸
1.10.3. 多輸出值問題
一個多輸出值問題是一種會在預測結果中,產出多個輸出值的監督學習問題,其中他的** y 與之前的格式有所不同,變成一種尺寸為[樣本數量, 輸出值的數量]的二維數組。
當輸出值之間沒有什么關聯時,簡單的方法就是建立 n 個獨立的模型來進行預測,即使用這些模型去預測出 n 個輸出值。但是,因為這些輸出值都是擁有同一個輸入,所以你很難去說這些值之間是沒有任何關聯的,因此另一種更好的方式是去建立一個有能力同時預測出 n 個輸出值的模型。
這個方法有兩個優點,第一個是它只需要很少的訓練時間(相比之前的 n **個模型來說),因為它只有一個估計器。然后,生成結果的泛化精度也會有所增加。
對普通的決策樹來說,使用一種策略就能夠容易地讓它支持多輸出值的問題。
只需要進行下列的改變就可以了:
- 在葉子節點中將原來只儲存一個值改成存儲n個輸出值;
- 使用樹的一些分割標準來計算所有** n **個輸出值的平均減少量。
DecisionTreeClassifier 和 DecisionTreeRegressor這兩個模塊都內置提供了使用這種策略來處理多輸出值問題。如果一個決策樹在擬合一個尺寸為[樣本數量, 輸出值的數量]的輸出數組** Y ** ,那么估計結果將會是:
- 在使用** predict **時輸出 輸出值的數量
- 在使用** predict_prob **時輸出 一個關于類概率的輸出值的數量列表。
在回歸中使用多輸出樹的例子,可以從這篇 多輸出樹回歸 獲得參考。在這篇例子,** X 是一個單精度的實數值然后 Y 則是 X **的正弦值或余弦值。
在分類中使用多輸出樹的例子,可以從這篇 使用多值輸出估計器的臉部補全 獲得參考。在這篇例子里,X是上半臉的像素點,而Y則是對應臉部的下半臉。
例子
引用
- M. Dumont et al, Fast multi-class image annotation with random subwindows and multiple output randomized trees, International Conference on Computer Vision Theory and Applications 2009
1.10.4. 復雜度#
一般來說,構造一個二叉樹的時間復雜度是** O(樣本數量 * 特征數量 * log(樣本數量)) ,然后它的查詢時間是 O(log(樣本數量)) 。盡管這個樹的構造算法是盡量往平衡樹的方向來生成的,但是在事實上一般是做不到平衡的。所以這里假設這棵樹的子樹已經能夠近似的看成是一顆平衡樹了,然后每個節點的開銷則通過 O(特征數量) 搜索的組合來找出一個能夠極大的降低熵的特征。因此每個節點的開銷為 O(特征數量 · 樣本數量 · log(樣本數量)) ,然后可以導出一整棵樹的開銷(通過合計每個節點的開銷)為 O(特征數量 · 樣本數量^2 · log(樣本數量)) **
Scikit-learn實現了一個更有效的來構造決策樹的方法。一個原始的實現方式(也就是上面提到的那樣)會為每一個新的分割點,來沿著其特征以重新計算類標簽的直方圖(處理分類問題)或均值(處理回歸問題)。對所有的相關樣本的特征進行預分類和在訓練時固定類標簽的數量會把決策樹的每個節點的復雜度降低為** O(特征數量 · log(樣本數量)) ,然后整個樹的開銷則變成了 O(特征數量 · 樣本數量 · log(樣本數量)) **。在基于樹的算法里都有開啟這個方法的選項。在默認情況下是設置為梯度增強,因為這樣會加速訓練過程,不過把它關閉或者設置成其他算法則會降低訓練的速度,因為樹的訓練深度增加了。
1.10.5. 實用技巧#
- 在使用大量的特征時,決策樹會趨向于過擬合。例如高維(特征)空間中的少量樣本就通常會引起過擬合。所以設置一個合適的樣本數 / 特征數的比例很重要。
- 在事先先用一些降維方法(PCA, ICA, 或 特征選擇)對數據降下維,這樣會使得訓練出來的樹會更容易找到關鍵點。
- 在訓練的過程中使用** export 函數來可視化你的樹。可以先用 max_depth=3 **的初始深度來觀察樹要如何你的數據,然后再視情況來修改深度。
- 要注意樣本的數量,當樹的深度增加一層,數據量的需求就會加倍。使用** max_depth **參數來控制樹的深度以防止過擬合。
- 使用** min_samples_split 或者 min_samples_leaf 這兩個參數來控制每個葉子節點上面的樣本數量。小數量通常意味著樹會過擬合,而太大的數量則會使得樹難以從數據中進行學習。嘗試 min_samples_leaf=5 作為初始值。如果樣本數量的變化過于劇烈,可以對這兩個參數設置百分值。這兩個參數的主要區別在于 min_samples_leaf 會在每個葉子節點里保存一個最小數量的樣本,而 min_samples_split 能夠創建出一個很隨意的用來存放樣本的小節點,雖說 min_samples_split **在文獻里更常見一些。
- 在進行訓練之前先平衡一下你的數據集,防止樹中的部分標簽占據主導地位。可以通過為每一類的樣本都取同樣的大小來做到類平衡,又或者是把每一類的樣本權重(**
sample_weight )歸一化成一個統一的數值。但同時也要注意選擇基于權重的預修剪方式,例如 min_weight_fraction_leaf 方法就會比在不使用樣本權重的 min_samples_leaf **方法下有著更少的主導類。 - 如果樣本是帶有權重的,那這樣使用像** min_weight_fraction_leaf **這樣基于權重預修剪方法的話,會讓樹結構的優化過程變得簡單一點,它確保了每個樹節點都至少確定了一部分樣本權重。
- 一般默認情況下,所有的決策樹都是使用** np.float32 **數組。如果訓練集不適合這個格式的話,那么則會多復制出一份轉化為該格式的數據集副本。
- 如果輸入的矩陣** X 非常稀疏,那就建議在對它進行擬合使用 spares.csc_matrix 函數,而在預測之前使用 spares.csr_matrix **函數進行壓縮。(csc_matrix(column, 對列進行壓縮) ,csr_matrix(row, 對行進行壓縮))。相比于一個密集矩陣,在特征上包含許多零值的稀疏矩陣的訓練時間會比前者快上幾個數量級。
1.10.6. 樹的各種算法:ID3,C4.5,C5.0 和 CART#
決策樹究竟有哪些算法?他們之間又有什么不一樣?而SKlearn又實現了哪些算法呢?
ID3(Iterative Dichotomiser 3,迭代二叉樹 第3代)為羅斯·昆蘭在1986年所開發的。這個算法創建了一個多叉樹,并(以一種貪婪的方式)尋找每個節點上的分類特征,并為分類目標獲取出最大的信息收益。一般這種算法生成的樹都會"生長"到最大尺寸,所以經常需要一個修剪階段來提高其應用在新數據上的能力。
C4.5 是 ID3 算法的后繼者,它通過動態地定義離散參數(基于數值變量),將連續的屬性值轉化為一組間距離散來取消特征必須是分類型的這一約束條件。C4.5將經過訓練的樹(即經過ID3算法輸出的多叉樹)轉化為一個"如果...即..."的規則組。然后這些規則組將會通過評估后,以確定其排序位置。如果樹的準確度沒有提高,那么將會通過去除一些規則來完成修剪操作。
C5.0 是昆蘭根據其所有權下對該算法的最新版本。比起C4.5,它占用的內存更少,建立出的規則組規模更小,但精準度更高。
CART (Classification and Regression Trees,分類與回歸樹)跟 C4.5 相比很相似,但是其不同點在于它(在回歸問題)支持數值的目標變量和不需要計算規則組。CART 通過在每個節點上使用特征和閾值來產生出最大的信息收益。
scikit-learn 中使用的CART算法是經過優化后的版本。
1.10.7. 數學公式#
給定一個訓練向量 ** xi ∈ R^n, i = 1,..., ι ** 和 標簽向量** y ∈ R^ι **,然后決策樹遞歸地分割這兩組空間,使得擁有相同標簽的向量組組合在一起。
然后讓** m 點上的數據用 Q 表示。對每一個候選組 θ = (j, t_m) 關聯一個特征 j 和閾值 t_m ,然后將原來的數據 Q 分割為 Q_left(θ) 和 Q_right(θ) **兩個子集。
不純度** m 則通過不純度函數 H() **來計算出,而這一函數取決于要解決的任務來決定(分類或回歸)

然后最小化不純度的參數

然后對** Q_left(θ) 和 Q_right(θ) 遞歸這一過程,至到到達最大深度,N_m < min_samples** 或 ** N_m = 1**。
1.10.7.1. 分類標準##
如果目標是一個在取值為[0, K-1]的分類型輸出,則在節點** m 上,可以表示在區域 R_m 上有 N_m **個觀測值,使得
為** k 類在節點 m **中與觀測值的比例。
然后常見的計算不純度的方法有:基尼

交叉熵
和 錯分類
1.10.7.2. 回歸標準##
如果目標是一個連續值,那么在節點** m 上,可以表示在區域 R_m 里有 N_m **個觀測值,然后常見的最小化方式是使用均方誤差

引用
- https://en.wikipedia.org/wiki/Decision_tree_learning
- https://en.wikipedia.org/wiki/Predictive_analytics
- L. Breiman, J. Friedman, R. Olshen, and C. Stone. Classification and Regression Trees. Wadsworth, Belmont, CA, 1984.
- J.R. Quinlan. C4. 5: programs for machine learning. Morgan Kaufmann, 1993.
- T. Hastie, R. Tibshirani and J. Friedman. Elements of Statistical Learning, Springer, 2009.
(在嘗試翻譯這篇文檔的時候難免會因為各種問題而出現錯翻,如果發現的話,煩請指出,謝謝> <)