1. 程式人生 > >決策樹算法

決策樹算法

adding 方法 直接 情況 方向 同時 tps pip class

調頻96.8有一種遊戲:遊戲中,出題者寫下一件東西,其他人需要猜出這件東西是什麽。當然,如果遊戲規則僅此而已的話,幾乎是無法猜出來的,因為問題的規模太大了。為了降低遊戲的難度,答題者可以向出題者問問題,而出題者必須準確回答是或者否,答題者依據回答提出下一個問題,如果能夠在指定次數內確定謎底,即為勝出。

我們先實驗一下,現在我已經寫下了一個物體,而你和我的問答記錄如下:

  • 是男的嗎?Y

  • 是亞洲人嗎?Y

  • 是中國人嗎?N

  • 是印度人嗎?Y

  • ……

在上面的遊戲中,我們針對性的提出問題,每一個問題都可以將我們的答案範圍縮小,在提問中和回答者有相同知識背景的前提下,得出答案的難度比我們想象的要小很多。

技術分享

在每一個節點,依據問題答案,可以將答案劃分為左右兩個分支,左分支代表的是Yes,右分支代表的是No,雖然為了簡化,我們只畫出了其中的一條路徑,但是也可以明顯看出這是一個樹形結構,這便是決策樹的原型。

決策樹算法

我們面對的樣本通常具有很多個特征,正所謂對事物的判斷不能只從一個角度,那如何結合不同的特征呢?決策樹算法的思想是,先從一個特征入手,就如同我們上面的遊戲中一樣,既然無法直接分類,那就先根據一個特征進行分類,雖然分類結果達不到理想效果,但是通過這次分類,我們的問題規模變小了,同時分類後的子集相比原來的樣本集更加易於分類了。然後針對上一次分類後的樣本子集,重復這個過程。在理想的情況下,經過多層的決策分類,我們將得到完全純凈的子集,也就是每一個子集中的樣本都屬於同一個分類。 由這個分類的過程形成一個樹形的判決模型,樹的每一個非葉子節點都是一個特征分割點,葉子節點是最終的決策分類。

上面我們介紹決策樹算法的思想,可以簡單歸納為如下兩點:

  • 每次選擇其中一個特征對樣本集進行分類

  • 對分類後的子集遞歸進行步驟1

在第一個步驟中,我們需要考慮的一個最重要的策略是,選取什麽樣的特征可以實現最好的分類效果,而所謂的分類效果好壞,必然也需要一個評價的指標。

直觀來說就是集合中樣本所屬類別比較集中,最理想的是樣本都屬於同一個分類。樣本集的純度可以用熵來進行衡量。

在信息論中,熵代表了一個系統的混亂程度,熵越大,說明我們的數據集純度越低,當我們的數據集都是同一個類別的時候,熵為0,熵的計算公式如下:

技術分享

其中,P(xi)表示概率,b在此處取2。比如拋硬幣的時候,正面的概率就是1/2,反面的概率也是1/2,那麽這個過程的熵為:

技術分享

可見,由於拋硬幣是一個完全隨機事件,其結果正面和反面是等概率的,所以具有很高的熵。

假如我們觀察的是硬幣最終飛行的方向,那麽硬幣最後往下落的概率是1,往天上飛的概率是0,帶入上面的公式中,可以得到這個過程的熵為0,所以,熵越小,結果的可預測性就越強。在決策樹的生成過程中,我們的目標就是要劃分後的子集中其熵最小,這樣後續的的叠代中,就更容易對其進行分類。

既然是遞歸過程,那麽就需要制定遞歸的停止規則

在兩種情況下我們停止進一步對子集進行劃分,其一是劃分已經達到可以理想效果了,另外一種就是進一步劃分收效甚微,不值得再繼續了。

用專業術語總結終止條件有以下幾個:

  1. 子集的熵達到閾值

  2. 子集規模夠小

  3. 進一步劃分的增益小於閾值

其中,條件3中的增益代表的是一次劃分對數據純度的提升效果,也就是劃分以後,熵減少越多,說明增益越大,那麽這次劃分也就越有價值,增益的計算公式如下:

技術分享

上述公式可以理解為:計算這次劃分之後兩個子集的熵之和相對劃分之前的熵減少了多少,需要註意的是,計算子集的熵之和需要乘上各個子集的權重,權重的計算方法是子集的規模占分割前父集的比重,比如劃分前熵為e,劃分為子集A和B,大小分別為m和n,熵分別為e1和e2,那麽增益就是e - m/(m + n) * e1 - n/(m + n) * e2。

決策樹算法實現

有了上述概念,我們就可以開始開始決策樹的訓練了,訓練過程分為:

  1. 選取特征,分割樣本集

  2. 計算增益,如果增益夠大,將分割後的樣本集作為決策樹的子節點,否則停止分割

  3. 遞歸執行上兩步

上述步驟是依照ID3的算法思想(依據信息增益進行特征選取和分裂),除此之外還有C4.5以及CART等決策樹算法。

class DecisionTree(object):
    def fit(self, X, y):
        # 依據輸入樣本生成決策樹
        self.root = self._build_tree(X, y)
 
    def _build_tree(self, X, y, current_depth=0):
        #1. 選取最佳分割特征,生成左右節點
        #2. 針對左右節點遞歸生成子樹
      
    def predict_value(self, x, tree=None):
        # 將輸入樣本傳入決策樹中,自頂向下進行判定
        # 到達葉子節點即為預測值

在上述代碼中,實現決策樹的關鍵是遞歸構造子樹的過程,為了實現這個過程,我們需要做好三件事:分別是節點的定義最佳分割特征的選擇遞歸生成子樹。  

作者:ZPPenny
鏈接:http://www.jianshu.com/p/c4d0837e9439
來源:簡書

總結

決策樹是一種簡單常用的分類器,通過訓練好的決策樹可以實現對未知的數據進行高效分類。

決策樹模型具有較好的可讀性和描述性,有助於輔助人工分析;

決策樹的分類效率高,一次構建後可以反復使用,而且每一次預測的計算次數不超過決策樹的深度。

決策樹也有其缺點:

對於連續的特征,比較難以處理。

對於多分類問題,計算量和準確率都不理想。

在實際應用中,由於其最底層葉子節點是通過父節點中的單一規則生成的,所以通過手動修改樣本特征比較容易欺騙分類器,比如在攔擊郵件識別系統中,用戶可能通過修改某一個關鍵特征,就可以騙過垃圾郵件識別系統。從實現上來講,由於樹的生成采用的是遞歸,隨著樣本規模的增大,計算量以及內存消耗會變得越來越大。

過擬合也是決策樹面臨的一個問題,完全訓練的決策樹(未進行剪紙,未限制Gain的閾值)能夠100%準確地預測訓練樣本,因為其是對訓練樣本的完全擬合,但是,對與訓練樣本以外的樣本,其預測效果可能會不理想,這就是過擬合。

解決決策樹的過擬合,除了上文說到的通過設置Gain的閾值作為停止條件之外,通常還需要對決策樹進行剪枝,常用的剪枝策略有

  1.Pessimistic Error Pruning:悲觀錯誤剪枝

  2.Minimum Error Pruning:最小誤差剪枝

  3.Cost-Complexity Pruning:代價復雜剪枝

  4. Error-Based Pruning:基於錯誤的剪枝,即對每一個節點,都用一組測試數據集進行測試,如果分裂之後,能夠降低錯誤率,再繼續分裂為兩棵子樹,否則直接作為葉子節點。

  5. Critical Value Pruning:關鍵值剪枝,這就是上文中提到的設置Gain的閾值作為停止條件。

以最簡單的方式展示了ID3決策樹的實現方式,如果想要了解不同類型的決策樹的差別,可以參考這個鏈接。
另外,關於各種機器學習算法的實現,強烈推薦參考Github倉庫ML-From-Scratch,下載代碼之後,通過pip install -r requirements.txt安裝依賴庫即可運行代碼。

決策樹算法