決策樹(decision tree)


決策樹

ID3,C4.5,CART,決策樹的生成,剪枝。

一、概述

        決策樹(decision tree)是一種基本的分類與回歸方法(這里是分類的決策樹)。決策樹模型呈樹形結構,在分類問題中,表示基於特征對實例進行分類的過程。它可以認為是if-then規則的集合,也可以認為是定義在特征空間與類空間上的條件概率分布。其主要優點是模型具有可讀性,分類速度快。學習時,利用訓練數據,根據損失函數最小化的原則建立決策樹模型。預測時,對新的數據利用決策樹模型進行分類。決策樹學習通常包括三個步驟:特征選擇決策樹的生成決策樹的修剪

  用決策樹分類,從根結點開始,對實例的某一特征進行測試,根據測試結果,將實例分配到其子結點;這時,每一個子結點對應着該特征的一個取值。如此遞歸地對實例進行測試並分配,直至達到葉結點。最后將實例分到葉結點的類中。

二、決策樹學習

  假設給定訓練數據集:

    D = { ( x1,y1 ),( x2,y2 ),......,( xN,yN ) }

  其中,xi  = (xi1,xi2,xi3,......,xin為輸入實例(特征向量),N為實例個數(樣本容量),n為特征個數,yi為類標記。

  學習的目標是根據給定的訓練數據集構建一個決策樹模型,使它能夠對實例進行正確的分類。

  決策樹學習的算法通常是一個遞歸地選擇最優特征,並根據該特征隊訓練數據進行分割,使得對各個子集有一個最好的分類的過程。這一過程對應着特征空間的划分和決策樹的構建。

  生成的決策樹可能對訓練數據有很好的分類能力,但對未知的測試數據卻未必有很好的分類能力,即可能發生過擬合現象。我們需要對已生成的樹自上而下進行剪枝,將樹變得更簡單,從而使它具有更好的泛化能力。具體地,就是去掉過於細分的葉結點,使其回退到父結點,甚至更高的結點,然后將父結點或更高的結點改為新的葉結點。

  可以看出,決策樹學習算法包含特征選擇決策樹的生成決策樹的剪枝過程。常用的算法有ID3C4.5CART

三、特征選擇

  特征選擇在於選取對訓練數據具有分類能力的最優特征。通常特征選擇的准則是信息增益信息增益比(信息增益率)。

1、熵(也稱香農熵)和條件熵

   香農熵這個名字來源於信息論之父克勞德•香農。定義為信息的期望值,表示隨機變量不確定性的度量。

  設X是一個取有有限個值的離散隨機變量,其概率分布為:

    P ( X = x) = pi, i = 1,2,3,…,n,

  則隨機變量X的熵定義為:

    H(X) = -∑ pi * log pi

  上試若取 log2 ,這時熵的單位為比特(bit) ;若取 ln ,這時熵的單位為納特(nat),一般取 log2。

  由定義知,熵只依賴於X的分布,而與X的取值無關,所以也可以將X的熵記作:

    H ( p ) = -∑ pi * log pi

  熵越大,隨機變量的不確定性就越大。

  條件熵 H(Y|X)表示一直隨機變量X的條件下隨機變量Y的不確定性。

    H ( Y | X ) = -∑ pi * H ( Y | X = x)       這里 pi = P( X = xi ) ,i = 1,2,...,n

  當熵和條件熵中的概率由數據估計(特別是極大似然估計)得到是,所對應的熵與條件熵分別稱為經驗熵經驗條件熵

2、信息增益(information gain)

  信息增益,表示由於特征A而使得數據集的分類的不確定性減少的程度。不同的特征往往有不同的信息增益,信息增益大的特征具有更強的分類能力。

  定義:特征A對訓練數據集D的信息增益 Gain(D,A),定義為集合D的經驗熵H(D)與特征A給定條件下的經驗熵H(D|A)之差,即: 

    Gain(D,A) = H ( D ) - H ( D | A )

  根據信息增益准則的特征選擇方法是:對訓練數據集(或子集)D,計算其每個特征的信息增益,並比較他們的大小,選擇信息增益最大的特征。

  設訓練數據集為D,|D|表示其樣本容量(樣本總數)。設有K個類Ck,k = 1,2,...,K,|Ck|為屬於類Ck的樣本個數,∑|Ck| = |D|。設特征A有n個不同的取值 { a1,a2,...,an},根據特征A的取值將D划分為n個子集 D1,D2,...,D,|Di|為Di的樣本個數,∑|Dk| = |D|。記子集Di中屬於類Ck的樣本的幾何為Dik,即 Dik  = Di ∩ Ck,|Dik|為Dik的樣本個數。於是有:

  信息增益的算法

    輸入:訓練數據集D和特征A;

    輸出:特征A對訓練數據集D的信心增益 Gain(D,A).

  (1)計算數據集D的經驗熵H(D)

    H(D) = -∑1 (|Ck| / |D|) * log2 (|Ck| / |D|)

  (2)計算特征A對數據集D的經驗條件熵 H(D | A)

    H ( D | A ) = ∑1 (|Di| / |D|) * H(Di) = -∑1 (|Di| / |D|) * 1 (|Dik| / |Di|) * log2  (|Dik| / |Di|)

  (3)計算信息增益

    Gain(D,A) = H ( D ) - H ( D | A )

3、信息增益比(增益率)

  以信息增益作為划分訓練數據集的特征,存在偏向於選擇取值較多的特征的問題。使用信息增益比可以對這一問題進行校正。

  定義:特征A對訓練數據集D的信息增益比 Gain_ratio(D,A) 定義為其信息增益Gain(D,A)與訓練數據集D關於特征A的值熵 HA(D) 之比,即:

    Gain_ratio(D,A) = Gain(D,A) / HA(D)

    其中,HA(D) =  -∑1 (|Di| / |D|) * log2 (|Di| / |D|),n是特征A取值的個數。

四、決策樹的生成

  決策樹生成算法:

    輸入:訓練數據集D,特征集A,閾值ε(預剪枝用,后剪枝不需要此項);

    輸出:決策樹T。

  (1)若D中所有樣本屬於同一類Ck,則T為但結點樹,並將Ck作為該結點的類標記,返回T;

  (2)若A = Ø,則T為單結點樹,並將D中樣本數最多的類Ck作為該結點的類標記,返回T;

  (3)否則,計算A中各個特征對D的信息增益或者信息增益比,選擇信息增益或信息增益比最大的特征 Ag

  (4)如果 A的信息增益或信息增益比小於閾值ε,則置T為但結點樹,並將D中樣本數最多的類Ck作為該結點的類標記,返回T;(后剪枝沒有這步)

  (5)如果Ag的每一種可能值ai,依 Ag = a將D分割為若干非空子集Di,將Di中樣本數最多的類作為標記,構建子結點,由結點及其子結點構成樹T,返回T;

  (6)對第i個子結點,以Di為訓練集,以A - { A} 為特征集,遞歸地調用步驟(1)~(5),得到子樹Ti,返回Ti 。

ID3算法

  在決策樹生成過程中,以信息增益為特征選擇的准則。

c4.5算法

  在決策樹生成過程中,以信息增益比為特征選擇的准則。

五、決策樹的剪枝

  剪枝是決策樹學習算法對付“過擬合”的主要手段。在決策樹學習中,為了盡可能正確分類訓練樣本,結點划分過程將不斷重復,有時會造成決策樹分支過多。因此,可通過主動去掉一些分支來降低過擬合的風險。

  決策樹剪枝的基本策略有“預剪枝”和“后剪枝”。

  預剪枝是指在決策樹生成過程中,對每個結點在划分前先進行估計,若當前結點的划分不能帶來決策樹泛化性能提升,則停止划分並將當前結點標記為葉結點;

  后剪枝則是先從訓練集生成一棵完整的決策樹,然后自底向上地對非葉結點進行考察,若將該結點對應的子樹替換為葉結點能帶來決策樹泛化性能提升,則該子樹替換為葉結點。

  如何判斷決策樹泛化性能是否提升呢?可以選擇一種性能評估方法(留出法、交叉驗證法、自助法等)。性能評估方法參看其他筆記。

六、CART算法(分類與回歸樹clssification and regression tree)

  CART同樣由特征選擇、樹的生成及剪枝組成,既可以用於分類也可以用於回歸。

  CART假設決策樹是二叉樹,內部結點特征的取值為“是”和“否”,左分支是取值“是”的分支,右分支是取值為“否”的分支。這樣的決策樹等價於遞歸地二分每個特征。

  CART算法由以下兩步組成:

    (1)決策樹生成:基於訓練數據集生成決策樹,生成的決策樹要盡量大;

    (2)決策樹剪枝:用驗證數據集對已生成的樹進行剪枝並選擇最優子樹,這時用損失函數最小作為剪枝的標准。

1、CART生成決策樹

  決策樹的生成就是遞歸地構建二叉樹的過程。對回歸樹用平方誤差最小化准則,對分類樹用基尼指數(Gini index)最小化准則,進行特征選擇,生成二叉樹。

(1)回歸樹的生成

  假設X與Y分別為輸入和輸出變量,並且Y是連續變量,給定訓練數據集

    D = { ( x1,y1 ),( x2,y2 ),......,( xN,yN ) }

  考慮如何生成回歸樹。

    一個回歸樹對應着輸入空間(即特征空間)的一個划分以及在划分的單元的輸出值。假設已將輸入空間划分為M個單元R1,R2,...,RM

  並且在每個單元Rm上有一個固定的輸出值cm,於是回歸樹模型可表示為:

    ƒ ( x ) =  ∑ cm I ( x ∈ Rm

  當輸入空間的划分確定時,可以用平方差  ∑( yi - ƒ ( xi ))來表示回歸樹對於訓練數據的預測誤差,用平方差最小的准則求解每個單元上的最優輸出值。

  假如使用特征 j 的取值 s 來將輸入空間划分為兩個區域,分別為:

    

  我們需要最小化損失函數,即:

    

  其中c1,c2分別為R1,R2區間內的輸出平均值。(此處與統計學習方法上的公式有所不同,在課本中里面的c1,c2都需要取最小值,但是,在確定的區間中,當c1,c2取區間輸出值的平均值時其平方會達到最小,為簡單起見,故而在此直接使用區間的輸出均值。)

  為了使平方誤差最小,我們需要依次對每個特征的每個取值進行遍歷,計算出當前每一個可能的切分點的誤差,最后選擇切分誤差最小的點將輸入空間切分為兩個部分,然后遞歸上述步驟,直到切分結束。此方法切分的樹稱為最小二乘回歸樹

  最小二乘回歸樹生成算法:

    輸入:訓練數據集 D;

    輸出:回歸樹 ƒ ( x ).

    在訓練數據集所在的輸入空間中,遞歸地將每個區域划分為兩個子區域並決定每個區域上的輸出值,構建二叉決策樹:

  (1) 選擇最優切分變量 j 與切分點 s,求解

    

    遍歷變量 j, 對固定的切分變量 j 掃描切分點 s,選擇使上式達到最小值的對(j,s)。

  (2)用選定的對(j,s)划分區域並決定相應 的輸出值:

     

  (3)繼續對兩個子區域調用步驟(1)、(2),直到滿足停止條件。

  (4)將輸入空間划分為M個區域 R1,R2,...,RM,生成決策樹:

    

    其中Cm是Rm上所有輸入實例 xi 對應的輸出 yi 的均值。

(2)分類樹的生成

  分類樹的生成用基尼指數選擇最優特征,同時決定改特征的最優二值切分點。

 基尼指數:

  分類問題中,假設有K個類,樣本點屬於第K類的概率為pk ,則概率分布基尼指數為:

  

 

  對於給定的樣本集合D,其基尼指數為:

    

  這里,ck 是 D 中屬於第 k類的樣本子集,K是類的個數。

  如果樣本集合根據特征A是否取某一可能值a,被分割成 D1和D2兩部分,即:

     

  則在特征A的條件下,集合D的基尼指數定義為:

 

  基尼指數 Gini(D)表示集合D的不確定性,基尼指數Gini(D,A)表示經 A = a 分割后集合D 的不確定性。基尼指數值越大,樣本集合的不確定性也就越大,這一點與熵相似。

 

 CART生成算法:

  輸入:訓練數據集D,停止計算的條件;

  輸出:CART決策樹。

  根據訓練數據集,從根結點開始,遞歸地對每個結點進行以下操作,構建二插決策樹:

  (1)設節點的訓練數據集為D,計算現有特征對該數據集的基尼指數。此時,對每一個特征A,對其可能取的每個值a,根據樣本點對A=a的測試為“是”或“否”將D分割成 D1和D2兩部分,計算 Gini(D,A)(見上面公式)。

  (2)在所有可能的特征A以及它們所有可能的值(切分點)a中,選擇基尼指數最小的特征及其對應的切分點作為最優特征與最優切分點。依最優特征與最優切分點,從現有結點生成兩個子結點,將訓練數據集依特征分配到兩個子結點中去。

  (3)對兩個子結點遞歸地調用(1)、(2),直到滿足停止條件。

  (4)生成CART決策樹。

  算法停止條件,是結點中的樣本個數小於預定閾值,或樣本集的基尼指數小於預定閾值(樣本基本屬於同一類),或者沒有更多特征。

2、CART剪枝

  決策樹常用的剪枝有事前剪枝(預剪枝)和事后剪枝(后剪枝),CART算法采用后剪枝,具體方法為代價復雜性剪枝法。可參考如下鏈接(累,有時間再補上自己的)

   剪枝參考:http://www.cnblogs.com/zhangchaoyang/articles/2709922.html

七、算法的代碼實現:

  代碼是照着《機器學習實戰》這本書敲的,只是實現了ID3樹的生成,很簡單。希望后面自己能實現其他的算法。

 
 1 # -*- coding: utf-8 -*-
 2 from math import log
 3 import operator
 4 
 5 '''計算給定數據集的熵'''
 6 def calcShannonEnt(dataSet):
 7     numEntries = len(dataSet)   #樣本總數
 8     labelCounts = {}    #創建一個字典,存放分類信息,各類含樣本數
 9     for featVec in dataSet:
10         currentLabel = featVec[-1]  #
11         if currentLabel not in labelCounts.keys(): #dic.keys() 返回字典所有的鍵
12             labelCounts[currentLabel] = 0
13         labelCounts[currentLabel] += 1
14     shannonEnt = 0.0
15     for key in labelCounts:
16         prob = float(labelCounts[key])/numEntries
17         shannonEnt -= prob * log(prob,2)
18     return shannonEnt
19 
20 '''創建數據集'''
21 def createDataSet():
22     dataSet = [
23             [1,1,'yes'],
24             [1,1,'yes'],
25             [1,0,'no'],
26             [0,1,'no'],
27             [0,1,'no']
28             ]
29     labels = ['without water','flippers']
30     return dataSet,labels
31 
32 '''划分數據集'''
33 def splitDataSet(dataSet,axis,value):#axis-划分數據集的特征,value-需要返回的特征的值
34     retDataSet = []
35     for featVec in dataSet:
36         if featVec[axis] == value:
37             reducedFeatVec = featVec[:axis]
38             reducedFeatVec.extend(featVec[axis + 1:]) #extend() 用於在列表末尾一次性追加另一個序列(用新列表擴展原來的列表)
39             retDataSet.append(reducedFeatVec)
40     return retDataSet
41 
42 '''選擇出信息增益大的特征'''
43 def chooseBestFeatureToSplit(dataSet):
44     numFeatures = len(dataSet[0]) - 1
45     baseEntropy = calcShannonEnt(dataSet) #
46     bestInfoGain = 0.0
47     bestFeature = -1
48     for i in range(numFeatures):
49         featList = [example[i] for example in dataSet]
50         uniqueVals = set(featList)
51         newEntropy = 0.0
52         for value in uniqueVals:
53             subDataSet = splitDataSet(dataSet,i,value)
54             prob = len(subDataSet) / float(len(dataSet))
55             newEntropy += prob * calcShannonEnt(subDataSet)
56         infoGain = baseEntropy - newEntropy
57         if(infoGain > bestInfoGain):
58             bestInfoGain = infoGain
59             bestFeature = i
60     return bestFeature
61 
62 '''返回次數出現最多的分類名稱'''
63 def majorityCnt(classList):
64     classCount = {}
65     for vote in classList:
66         if vote not in classCount.keys():
67             classCount[vote] = 0
68         classCount[vote] += 1
69     sortedClassCout = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
70     return sortedClassCout[0][0]
71 
72 '''生成決策樹'''
73 def createTree(dataSet,labels):
74     classList = [example[-1] for example in dataSet]
75     if classList.count(classList[0]) == len(classList):#類別完全相同則停止划分
76         return classList[0]
77     if len(dataSet[0]) == 1:    #遍歷完所有特征時,返回出現次數最多的類別
78         return majorityCnt(classList)
79     bestFeat = chooseBestFeatureToSplit(dataSet) #得到的是下標
80     bestFeatLabel = labels[bestFeat]    #取值:no water 或者 flippers
81     
82     myTree = {bestFeatLabel:{}}
83     del(labels[bestFeat])
84     featValues = [example[bestFeat] for example in dataSet]
85     uniqueVals = set(featValues)
86     for value in uniqueVals:
87         subLables = labels[:]
88         myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLables)
89     return myTree
View Code

參考文獻:《統計學習方法》-- 李航、《機器學習》(西瓜書)-- 周志華、《機器學習實戰》-- 李銳

這是我的第一篇博客,希望自己在學習過程中能堅持多記筆記多總結。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM