(轉)Decision Tree


Decision Tree:Analysis

 

大家有沒有玩過猜猜看(Twenty Questions)的游戲?我在心里想一件物體,你可以用一些問題來確定我心里想的這個物體;如是不是植物?是否會飛?能游泳不?當你問完這些問題后,你就能得到這個物體的特征,然后猜出我心里想象的那個物體,看是否正確。

這個游戲很簡單,但是蘊含的思想卻是質朴的。每個問題都會將范圍減少,直到特征顯現,內蘊的思想就是Decision Tree算法。判定樹(Decision Tree)算法是機器學習中很重要的一種算法,有文章聲稱該算法在ML學習中最為常用的算法,你不需要明白高深的知識就能明白算法的運行原理。

 

Decision Tree包括以下幾個部分:

  • Root:根節點,Decision Tree使用了樹的概念,必然有Root屬性。
  • Decision Node:判定節點,該節點的數據會繼續根據數據屬性繼續進行判定。
  • Branch:從Decision Node迭代生成的子樹是子樹根節點的一個屬性判斷
  • End Node:也稱為Leaf Node,該節點實際上是做出決定的節點,對於樣本屬性的判斷到Leaf Node結束。 

Decision Tree分類:

Classification Tree:測試數據經過Classification Tree處理后,看結果歸屬於那個類別(Class)。

Regression Tree:如果測試數據的輸出是數值類型,可以考慮使用Regression Tree。

CART(Classification And Regression Tree)s算法是對上面兩種算法的術語涵蓋。用來回歸的樹(Regression Tree)和用來分類的樹(classification Tree)具有一定的相似性,不過其不同之處在於決定分裂(Split)的過程。

使用某些技術(Ensemble Methods),可以構建多個Decision Tree:

Bagging Decision Tree:創建多個Decision Tree,通過替換訓練集合,得到多個Decision Tree,最終得到一致的結果。

Random Forest Classification Decision Tree:使用多個Decision Tree,提升分類的准確率。

Boosted Trees:可以使用於回歸(Regression)和分類(Classification)的問題。

Rotation Forest:Decision Tree生成是從訓練集合特征屬性中隨機選取,使用PCA分析方法來構建Decision Tree。

 

Gini Impurity

該Gini度量是指隨機選擇集合中的元素,根據集合中label的分布將該元素賦予分類,該元素分類錯誤的幾率。Gini度量在CART算法中使用,其定義如下:

 

其中i為label標簽,fi為label為i標簽的比例(Fraction)

 

Information Gain

這個是比較常見的信息度量,在Information Theory中經常使用,基於熵(entropy)的概念。在ID3、C4.5、C5.0中使用。其定義如下:

 

i為label標簽,fi為label標簽的比例,和上面的定義一致。Information Gain的定義如下:

 

Entropy和Information Gain的定義還會在后面章節里用到,確保自己正確理解了Information Gain的定義。 本文最后會有個教程,幫助大家學習下Information Gain的概念。

 

Decision Tree的優點和缺點:

1:易於理解和解釋,簡單解釋就能明白Decision Tree的模型和運行原理,對於結果也比較容易解釋。

2:數據預處理少,其它算法需要處理Data Normalization、Dummy Variable、Blank Value都需要進行預處理。

3:能夠處理數值數據和分類數據,能夠使用Decision Tree和Regression Tree就是例證。

4:使用白盒(White Box)模型,使用白盒模型能夠在給定測試數據下解釋測試結果分類,NN(Neural Network)分類就難以解釋,至少沒有Decision Tree模型明晰。

5:易於使用統計數據驗證模型,能夠解釋模型的可用性和可信賴度

6:健壯,數據假設比較少,該模型僅僅依賴於觀察到的數據,歸於數據生成模型假設比較少。

7:在大數據量情況下也能很快給出結果,根據Decision Tree抽取出來的規則能夠很快的運用於測試數據,運行快速。

 

缺點:

1:最優Decision Tree是NP難題,所以使用的Decision-Tree算法都是基於啟發式(Heuristic)算法,如Greedy Algorithm等,在每個節點判斷都是根據局部最優解來進行操作。啟發式算法不能保證返回全局最優的Decision Tree。

2:容易產生過於復雜的樹,不能很好地獲得數據的通用模型,這個實際上是被稱為是Overfitting,剪枝技術能夠很好的避免這個問題。

3:Decision Tree的表示能力有限,只能表示有限的數據操作,如XOR、Parity、Multiplexer等操作就不易表示;導致Decision Tree變得特別大,解決方法可以使用改變問題域的表示(Propositionalisation),或者使用更加復雜的表示方法,如Statistical Relational Learning、Inductive Logic Programming等。

4:對於包含分類變量(Categorical Variable)的數據,使用Information Gain分裂會造成較大的偏差

 

Extension:

Decision Tree使用And或者是Conjunction來在Decision Node進行判斷,逐步得到結果;Decision Graph則是使用Or或者Disjunction來連接路徑(判斷條件)。在Decision Graph中,使用MML(Minimum Message length)來對Path進行處理,更細節的內容請自行查找。

關於Decision Tree的實現比較多,其中Weka中有一部分Decision Tree的經典算法實現,大家可以將weka的源碼下載下來,仔細閱讀下。

 

Decision Tree:ID3、C4.5

 

ID3(Iterative Dichotomiser 3)算法是判定樹算法(Decision Tree Learning)的典型代表算法,由Ross Quinlan在1975年提出。ID3是作為C4.5的先驅,在Machine Learning和Natural Language Processing中使用廣泛。該分類算法的核心是Entropy理論,屬於數學的范疇。Entropy Theory是信息論中的名詞,在上篇文章中http://isilic.iteye.com/blog/1841339有介紹,不過關於信息熵還有一些更深一些的東西。

信息熵

信息熵是指:一組數據所包含的信息量,使用概率來度量。數據包含的信息越有序,所包含的信息越低;數據包含的信息越雜,包含的信息越高。例如在極端情況下,如果數據中的信息都是0,或者都是1,那么熵值為0,因為你從這些數據中得不到任何信息,或者說這組數據給出的信息是確定的。如果數據時均勻分布,那么他的熵最大,因為你根據數據不能知曉那種情況發生的可能性比較大。

計算熵的公式為:

 

熵值和概率的關系如下,這個是二值情況下的概率與熵值關系,其中n=2:

 

實際上,信息熵表示的是信息的不確定性。當概率相同時,不確定性越大,因為所有的信息概率相同,你不能確定哪個信息出現的可能性更大;當某類別發生的概率為0或者1時,給出的結果是確定的(出現或者不出現、發生或者不發生)。這樣的解釋會不會更清楚點。

 

Information Gain(IG),信息增益和信息熵描述的信息是一致的;描述的是對於數據集合S,將其按照其屬性A切分后,獲得的信息增益值。注意IG描述的是信息的增益值,當不確定性越大時,信息增益值應該是越小,反之亦然,是負相關的關系。信息增益的公式如下:

 

在ID3中,使用信息增益(IG)或者熵(Entropy)值來確定使用哪個屬性進行判定屬性,可以說是ID3算法的關鍵和精髓所在。ID3算法的偽碼如下:

 

ID3算法的原理還是比較簡單的,其理論基礎是Entropy理論和Information Gain理論,只要深入理解了這個內容,ID3算法就不是問題。其實Machine Learning的基礎是統計、概率、幾何知識的考量,數學基礎好的話,會在學習過程中感覺輕松些。

 

在Machine Learning in Action中有個章節是介紹ID3算法的,並且有完整的實現。我將代碼拿過來,感興趣的可以結合理論學習一下。 

 

Python代碼  
  1. from math import log  
  2. import operator  
  3.   
  4. def createDataSet():  
  5.     dataSet = [[1, 1, 'yes'],  
  6.                [1, 1, 'yes'],  
  7.                [1, 0, 'no'],  
  8.                [0, 1, 'no'],  
  9.                [0, 1, 'no']]  
  10.     labels = ['no surfacing','flippers']  
  11.     #change to discrete values  
  12.     return dataSet, labels  
  13.   
  14. def calcShannonEnt(dataSet):  
  15.     numEntries = len(dataSet)  
  16.     labelCounts = {}  
  17.     for featVec in dataSet: #the the number of unique elements and their occurance  
  18.         currentLabel = featVec[-1]  
  19.         if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0  
  20.         labelCounts[currentLabel] += 1  
  21.     shannonEnt = 0.0  
  22.     for key in labelCounts:  
  23.         prob = float(labelCounts[key])/numEntries  
  24.         shannonEnt -= prob * log(prob,2) #log base 2  
  25.     return shannonEnt  
  26.   
  27. def splitDataSet(dataSet, axis, value):  
  28.     retDataSet = []  
  29.     for featVec in dataSet:  
  30.         if featVec[axis] == value:  
  31.             reducedFeatVec = featVec[:axis]     #chop out axis used for splitting  
  32.             reducedFeatVec.extend(featVec[axis+1:])  
  33.             retDataSet.append(reducedFeatVec)  
  34.     return retDataSet  
  35.   
  36. def chooseBestFeatureToSplit(dataSet):  
  37.     numFeatures = len(dataSet[0]) - 1      #the last column is used for the labels  
  38.     baseEntropy = calcShannonEnt(dataSet)  
  39.     bestInfoGain = 0.0; bestFeature = -1  
  40.     for i in range(numFeatures):        #iterate over all the features  
  41.         featList = [example[i] for example in dataSet]#create a list of all the examples of this feature  
  42.         uniqueVals = set(featList)       #get a set of unique values  
  43.         newEntropy = 0.0  
  44.         for value in uniqueVals:  
  45.             subDataSet = splitDataSet(dataSet, i, value)  
  46.             prob = len(subDataSet)/float(len(dataSet))  
  47.             newEntropy += prob * calcShannonEnt(subDataSet)  
  48.         infoGain = baseEntropy - newEntropy     #calculate the info gain; ie reduction in entropy  
  49.         if (infoGain > bestInfoGain):       #compare this to the best gain so far  
  50.             bestInfoGain = infoGain         #if better than current best, set to best  
  51.             bestFeature = i  
  52.     return bestFeature                      #returns an integer  
  53.   
  54. def majorityCnt(classList):  
  55.     classCount={}  
  56.     for vote in classList:  
  57.         if vote not in classCount.keys(): classCount[vote] = 0  
  58.         classCount[vote] += 1  
  59.     sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)  
  60.     return sortedClassCount[0][0]  
  61.   
  62. def createTree(dataSet,labels):  
  63.     classList = [example[-1] for example in dataSet]  
  64.     if classList.count(classList[0]) == len(classList):  
  65.         return classList[0]#stop splitting when all of the classes are equal  
  66.     if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet  
  67.         return majorityCnt(classList)  
  68.     bestFeat = chooseBestFeatureToSplit(dataSet)  
  69.     bestFeatLabel = labels[bestFeat]  
  70.     myTree = {bestFeatLabel:{}}  
  71.     del(labels[bestFeat])  
  72.     featValues = [example[bestFeat] for example in dataSet]  
  73.     uniqueVals = set(featValues)  
  74.     for value in uniqueVals:  
  75.         subLabels = labels[:]       #copy all of labels, so trees don't mess up existing labels  
  76.         myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)  
  77.     return myTree  
  78.   
  79. def classify(inputTree,featLabels,testVec):  
  80.     firstStr = inputTree.keys()[0]  
  81.     secondDict = inputTree[firstStr]  
  82.     featIndex = featLabels.index(firstStr)  
  83.     key = testVec[featIndex]  
  84.     valueOfFeat = secondDict[key]  
  85.     if isinstance(valueOfFeat, dict):  
  86.         classLabel = classify(valueOfFeat, featLabels, testVec)  
  87.     else: classLabel = valueOfFeat  
  88.     return classLabel  
  89.   
  90. def storeTree(inputTree,filename):  
  91.     import pickle  
  92.     fw = open(filename,'w')  
  93.     pickle.dump(inputTree,fw)  
  94.     fw.close()  
  95.   
  96. def grabTree(filename):  
  97.     import pickle  
  98.     fr = open(filename)  
  99.     return pickle.load(fr)  

 代碼還算清晰,chooseBestFeatureToSplit和calcShannonEnt這兩個方法是ID3算法的核心,請仔細閱讀代碼,確定自己真正理解了熵理論和信息增益理論。

  

ID3算法存在的缺點: 

1:ID3算法在選擇根節點和內部節點中的分支屬性時,采用信息增益作為評價標准。信息增益的缺點是傾向於選擇取值較多是屬性在有些情況下這類屬性可能不會提供太多有價值的信息。 

2:ID3算法只能對描述屬性為離散型屬性的數據集構造決策樹 

 

ID3算法的局限是它的屬性只能取離散值,為了使決策樹能應用於連續屬性值情況,Quinlan給出了ID3的一個擴展算法:即C4.5算法。C4.5算法是ID3的改進,其中離散屬性值的選擇依據同ID3;它對於實值變量的處理采用多重分支。C4.5算法能實現基於規則的剪枝。因為算法生成的每個葉子都和一條規則相關聯,這個規則可以從樹的根節點直到葉子節點的路徑上以邏輯合取式的形式讀出。

 

C4.5算法之所以是最常用的決策樹算法,是因為它繼承了ID3算法的所有優點並對ID3算的進行了改進和補充。C4.5算法采用信息增益率作為選擇分支屬性的標准,並克服了ID3算法中信息增益選擇屬性時偏向選擇取值多的屬性的不足,並能夠完成對連續屬性離散化是處理;還能夠對不完整數據進行處理。C4.5算法屬於基於信息論Information Theory的方法,以信息論為基礎,以信息熵和信息增益度為衡量標准,從而實現對數據的歸納和分類。 

C4.5算法主要做出了以下方面的改進 

1:可以處理連續數值型屬性 

對於離散值,C4.5和ID3的處理方法相同,對於某個屬性的值連續時,假設這這個節點上的數據集合樣本為total,C4.5算法進行如下處理: 

  • 將樣本數據該屬性A上的具體數值按照升序排列,得到屬性序列值:{A1,A2,A3,...,Atotal}
  • 在上一步生成的序列值中生成total-1個分割點。第i個分割點的取值為Ai和Ai+1的均值,每個分割點都將屬性序列划分為兩個子集。
  • 計算每個分割點的信息增益(Information Gain),得到total-1個信息增益。
  • 對分裂點的信息增益進行修正:減去log2(N-1)/|D|,其中N為可能的分裂點個數,D為數據集合大小。
  • 選擇修正后的信息增益值最大的分類點作為該屬性的最佳分類點
  • 計算最佳分裂點的信息增益率(Gain Ratio)作為該屬性的Gain Ratio
  • 選擇Gain Ratio最大的屬性作為分類屬性。

 其中第4、5步驟Quinlan在93年的的算法中沒有體現,在96年發表文章對該算法進行改進,改進的論文可以參考這里:http://www.cs.cmu.edu/afs/cs/project/jair/pub/volume4/quinlan96a.pdf。 

 

2:用信息增益率(Information Gain Ratio)來選擇屬性 

克服了用信息增益來選擇屬性時偏向選擇值多的屬性的不足。信息增益率定義為: 

 

其中Gain(S,A)和ID3算法中的信息增益計算相同,而SplitInfo(S,A)代表了按照屬性A分裂樣本集合S的廣度和均勻性。

 

其中Si表示根據屬性A分割S而成的樣本子集。 

 

3:后剪枝策略 

Decision Tree很容易產生Overfitting,剪枝能夠避免樹高度無限制增長,避免過度擬合數據。剪枝算法比較復雜,我自己還沒有學習清楚,希望大家能提供學習這個剪枝策略方法的建議。 

 

4:缺失值處理 

對於某些采樣數據,可能會缺少屬性值。在這種情況下,處理缺少屬性值的通常做法是賦予該屬性的常見值,或者屬性均值。另外一種比較好的方法是為該屬性的每個可能值賦予一個概率,即將該屬性以概率形式賦值。例如給定Boolean屬性B,已知采樣數據有12個B=0和88個B=1實例,那么在賦值過程中,B屬性的缺失值被賦值為B(0)=0.12、B(1)=0.88;所以屬性B的缺失值以12%概率被分到False的分支,以88%概率被分到True的分支。這種處理的目的是計算信息增益,使得這種屬性值缺失的樣本也能處理。

 

我們看下C4.5算法的偽碼: 

偽碼代碼  
  1. Function C4.5(R:包含連續屬性的無類別屬性集合,C:類別屬性,S:訓練集)    
  2. Begin    
  3.    If S為空,返回一個值為Failure的單個節點;    
  4.    If S是由相同類別屬性值的記錄組成,    
  5.       返回一個帶有該值的單個節點;    
  6.    If R為空,則返回一個單節點,其值為在S的記錄中找出的頻率最高的類別屬性值;    
  7.    [注意未出現錯誤則意味着是不適合分類的記錄];    
  8.   For 所有的屬性R(Ri) Do    
  9.         If 屬性Ri為連續屬性,則    
  10.         Begin    
  11.            sort(Ri屬性值)  
  12.            將Ri的最小值賦給A1:    
  13.              將Ri的最大值賦給Am;    
  14.            For j From 1 To m-1 Do Aj=(A1+Aj+1)/2;    
  15.            將Ri點的基於Aj(1<=j<=m-1划分的最大信息增益屬性(Ri,S)賦給A;    
  16.         End;    
  17.   將R中屬性之間具有最大信息增益的屬性(D,S)賦給D;    
  18.   將屬性D的值賦給{dj/j=1,2...m};    
  19.   將分別由對應於D的值為dj的記錄組成的S的子集賦給{sj/j=1,2...m};    
  20.   返回一棵樹,其根標記為D;樹枝標記為d1,d2...dm;    
  21.   再分別構造以下樹:    
  22.   C4.5(R-{D},C,S1),C4.5(R-{D},C,S2)...C4.5(R-{D},C,Sm);    
  23. End C4.5  

 該算法流程是我從網上移過來的,對於其中和本文描述不一致的地方做了修改。原文參考這里:http://blog.sina.com.cn/s/blog_73621a3201017g7k.html

C4.5的主要點在於對於ID3的改進,可以說上面提到的四點對於Decision Tree算法來說是非常關鍵的,理解了這幾個改進點就算是掌握了C4.5算法的根本。

 

C4.5的缺點:

1:算法低效,在構造樹的過程中,需要對數據集進行多次的順序掃描和排序,因而導致算法的低效

2:內存受限,適合於能夠駐留於內存的數據集,當訓練集大得無法在內存容納時程序無法運行。

 

在weka開源軟件中有個weka.classifiers.trees.J48實現了C4.5算法,可以結合該代碼學習理論知識。

 

最后提示下C5.0算法,C5.0算法是在C4.5算法基礎上的改進,wiki上表示該算法應該歸屬於商業應用,所以沒有C4.5討論廣泛。相比較於C4.5,C5.0算法的改進點在於:

1:速度,C5.0有好幾個數量級別上的速度提升

2:內存使用,比C4.5使用更加有效

3:生成Decision Tree更小(Occam's Razor),能避免過度擬合

4:支持Boosting

5:支持權重,對於不同的樣本賦予不同的權重值

6:Winnowing:支持自動去除沒有幫助的屬性

這個是作者的聲明。尤其是前兩點,剛好是克服了C4.5算法的劣勢,是C4.5算法的巨大提升,算法源碼在http://rulequest.com/download.html這里有下載,大家可以學習下作者聲稱的優勢都是怎么實現的。在這里http://www.rulequest.com/see5-comparison.html有C5.0和C4.5的比較,大家也可以看下,從文章來看,C5.0確實是在很多方面都超過了C4.5。

 

本文關於Decision Tree的學習到此,本文介紹了最為簡單和基礎的ID3算法;隨后學習了C4.5相比於ID3的優勢及實現思路;最后比較了C5.0的改進;對於Decision Tree算法有了基本的認識,為后面學習Decision Tree算法提供了良好的基礎。

 

Decision Tree:CART、剪枝

 

決策樹的重要性和入門可以參考前面兩篇文章,尤其是入門的ID3算法:

http://isilic.iteye.com/blog/1841339http://isilic.iteye.com/blog/1844097

Classification And Regression Tree(CART)也是決策樹的一種,並且是非常重要的決策樹。除去上文提到的C4.5外,CART算法也在Top Ten Machine Learning Algorithm中,可見決策樹的重要性和CART算法的重要性。

 

CART的特性主要有下面三個,其實這些特性都不完全算是CART的特性,只是在CART算法中使用,並且作為算法的重要基礎:

1:二分(Binary Split):在每次判斷過程中,都是對觀察變量進行二分。

2:單變量分割(Split Based on One Variable):每次最優划分都是針對單個變量。

3:剪枝策略:CART算法的關鍵點,也是整個Tree-Based算法的關鍵步驟。

 

CART能處理Classification Tree和Regression Tree,在建樹過程中有不一樣的地方,我們分別來看下。

我們先看Classification Tree的建樹過程:

ID3,C4.5算法是利用熵理論和信息增益(率)來決定屬性分割策略;CART則是利用Gini Index(GINI 指數)來定義衡量划分的好壞。和熵類似,數據內包含的類別越雜亂,GINI指數就越大,有沒有感覺跟熵的概念類似。下面我們來學習下Gini Index的內容:

 

其中Pj是類j在T中的相對頻率,當類j在T中是傾斜時,gini(T)才會最小。

比較熵理論和gini指數,讓T中各類別出現的概率一致時,熵最大,Gini指數也是最大,但是兩者的逼近速度是不一樣的,從下圖中可以看出來:

 

其中MisClassification Rate也是一種衡量,感興趣的同學可以自行學習下。

 

衡量出某個屬性的Gini指數后,可以得到Gini Split數據如下:

 

這個有沒有和信息增益相似,這個可以稱為是Gini信息增益,在CART中,選擇其中最小的Gini信息增益作為結點划分決策樹。

對於CART,i=2,可以得到在Binary Split情況下的Gini信息增益:

 

在weka中有信息增益weka.attributeSelection.InfoGainAttributeEval和信息增益率 weka.attributeSelection.GainRatioAttributeEval 的實現,可以看下實現原理。

 

Regression Tree的建樹過程。

對於回歸樹,沒有分類數據,只有根據觀察數據得出的值,注意觀察值取值是連續,在這種情況下Classification Tree的最優划分規則就無能為力。

在這種情況下,回歸樹可以使用 最小剩余方差(Squared Residuals Minimization)來決定Regression Tree的最優划分,該划分准則是期望划分之后的子樹誤差方差最小:

 

決策樹停止生長的條件,這個可以看做是預剪枝過程:

樹不能無限增長,我們可以設定條件,當樹達到某個停止條件時,停止樹增長,常用的停止條件(Stopping Criteria)有如下幾個:

  • 1:子樹上的樣本數據都歸屬於同一個類別
  • 2:達到最大數深度(Maximum Tree Depth)
  • 3:子樹節點的樣本數量要少於某個門限值,或者小於一定的比例
  • 4:子樹節點再按照最優划分標准切分,其子樹的樣本數量小於某個門限值,或者小於一定的比例值
  • 5:最優划分(Split)標增益小於某個門限值,如誤差值等

這些方法都可以讓樹提前停止增長,防止樹無限制生成,避免一定程度上的過擬合。

 

剪枝理論,決策樹的剪枝在上一節中沒有仔細講,趁這個機會學習了剪枝的基礎理論,這里會詳細學習。

決策樹為什么(WHY)要剪枝?原因是避免決策樹過擬合(Overfitting)樣本。前面的算法生成的決策樹非常詳細並且龐大,每個屬性都被詳細地加以考慮,決策樹的樹葉節點所覆蓋的訓練樣本都是“純”的。因此用這個決策樹來對訓練樣本進行分類的話,你會發現對於訓練樣本而言,這個樹表現完好,誤差率極低且能夠正確得對訓練樣本集中的樣本進行分類。訓練樣本中的錯誤數據也會被決策樹學習,成為決策樹的部分,但是對於測試數據的表現就沒有想象的那么好,或者極差,這就是所謂的過擬合(Overfitting)問題。Quinlan教授試驗,在數據集中,過擬合的決策樹的錯誤率比經過簡化的決策樹的錯誤率要高。

 

現在問題就在於,如何(HOW)在原生的過擬合決策樹的基礎上,生成簡化版的決策樹?可以通過剪枝的方法來簡化過擬合的決策樹。剪枝可以分為兩種:預剪枝(Pre-Pruning)和后剪枝(Post-Pruning),下面我們來詳細學習下這兩種方法:

PrePrune:預剪枝,及早的停止樹增長,方法可以參考見上面樹停止增長的方法。

PostPrune:后剪枝,在已生成過擬合決策樹上進行剪枝,可以得到簡化版的剪枝決策樹。

其實剪枝的准則是如何確定決策樹的規模,可以參考的剪枝思路有以下幾個:

1:使用訓練集合(Training Set)和驗證集合(Validation Set),來評估剪枝方法在修剪結點上的效用

2:使用所有的訓練集合進行訓練,但是用統計測試來估計修剪特定結點是否會改善訓練集合外的數據的評估性能,如使用Chi-Square(Quinlan,1986)測試來進一步擴展結點是否能改善整個分類數據的性能,還是僅僅改善了當前訓練集合數據上的性能。

3:使用明確的標准來衡量訓練樣例和決策樹的復雜度,當編碼長度最小時,停止樹增長,如MDL(Minimum Description Length)准則。

 

我們先看下使用思路一來解決問題的集中后剪枝方法:

Reduced-Error Pruning(REP,錯誤率降低剪枝)

該剪枝方法考慮將書上的每個節點作為修剪的候選對象,決定是否修剪這個結點有如下步驟組成:

1:刪除以此結點為根的子樹

2:使其成為葉子結點

3:賦予該結點關聯的訓練數據的最常見分類

4:當修剪后的樹對於驗證集合的性能不會比原來的樹差時,才真正刪除該結點

因為訓練集合的過擬合,使得驗證集合數據能夠對其進行修正,反復進行上面的操作,從底向上的處理結點,刪除那些能夠最大限度的提高驗證集合的精度的結點,直到進一步修剪有害為止(有害是指修剪會減低驗證集合的精度)

REP是最簡單的后剪枝方法之一,不過在數據量比較少的情況下,REP方法趨於過擬合而較少使用。這是因為訓練數據集合中的特性在剪枝過程中被忽略,所以在驗證數據集合比訓練數據集合小的多時,要注意這個問題。

盡管REP有這個缺點,不過REP仍然作為一種基准來評價其它剪枝算法的性能。它對於兩階段決策樹學習方法的優點和缺點提供了了一個很好的學習思路。由於驗證集合沒有參與決策樹的創建,所以用REP剪枝后的決策樹對於測試樣例的偏差要好很多,能夠解決一定程度的過擬合問題。

 

Pessimistic Error Pruning(PEP,悲觀剪枝)

先計算規則在它應用的訓練樣例上的精度,然后假定此估計精度為二項式分布,並計算它的標准差。對於給定的置信區間,采用下界估計作為規則性能的度量。這樣做的結果,是對於大的數據集合,該剪枝策略能夠非常接近觀察精度,隨着數據集合的減小,離觀察精度越來越遠。該剪枝方法盡管不是統計有效的,但是在實踐中有效。

PEP為了提高對測試集合的預測可靠性,PEP對誤差估計增加了連續性校正(Continuity Correction)。PEP方法認為,如果: 

成立,則Tt應該被剪枝,上式中: 

 

 

其中,e(t)為結點t出的誤差;i為覆蓋Tt的葉子結點;Nt為子樹Tt的葉子樹;n(t)為在結點t處的訓練集合數量。PEP采用自頂向下的方式,如果某個非葉子結點符合上面的不等式,就裁剪掉該葉子結點。該算法被認為是當前決策樹后剪枝算法中經度比較高的算法之一,但是餓存在有缺陷。首先,PEP算法是唯一使用Top-Down剪枝策略,這種策略會導致與先剪枝出現同樣的問題,將該結點的某子節點不需要被剪枝時被剪掉;另外PEP方法會有剪枝失敗的情況出現。

雖然PEP方法存在一些局限性,但是在實際應用中表現出了較高的精度,。兩外PEP方法不需要分離訓練集合和驗證機和,對於數據量比較少的情況比較有利。再者其剪枝策略比其它方法相比效率更高,速度更快。因為在剪枝過程中,樹中的每顆子樹最多需要訪問一次,在最壞的情況下,它的計算時間復雜度也只和非剪枝樹的非葉子節點數目成線性關系。

可能有同學會對上面的那個不等式有疑問,可以參考這篇文章http://blog.sina.com.cn/s/blog_68ffc7a40100urn3.html 中關於PEP剪枝部分內容。PEP方法實際上是將結點誤差數目看做二項式分布,根據期望和方差得到的結果。

 

Cost-Complexity Pruning(CCP、代價復雜度)

CCP方法包含兩個步驟:

1:從原始決策樹T0開始生成一個子樹序列{T0、T1、T2、...、Tn},其中Ti+1是從Ti總產生,Tn為根節點

2:從子樹序列中,根據樹的真實誤差估計選擇最佳決策樹。

在步驟一中,生成子樹序列{T0、T1、T2、...、Tn}的基本思想是從T0開始,裁剪Ti中關於訓練數據集合誤差增加最小的分支來得到Ti+1。實際上當一棵樹T在結點t出剪枝時,它的誤差增加直觀上認為是:

 

其中R(t)為在結點t的子樹被裁剪后結點t的誤差,R(Tt)為在結點t的子樹沒被裁剪時子樹T的誤差。不過剪枝后T的葉子樹減少了|L(Ti)|-1,其中|L(Ti)|為子樹Ti的葉子樹,也就是說T的復雜性降低。因此考慮到樹的復雜性因素,樹分支被裁剪后誤差增加率可以由下式決定:

 Ti+1就是選擇Ti中具有最小\alpha值所對應的剪枝樹

如何從第一步驟產生的子樹序列{T0、T1、T2、...、Tn}中選擇出最佳決策樹是CCP方法的第二步驟的關鍵。通常可以采用V-交叉驗證(V-fold Cross-Validation)和基於獨立剪枝數據集兩種方法,這兩種方法可以參考(Classification And Regression Trees,Breiman et.al)。當使用基於獨立數據集剪枝時,和REP方法相比,CCP選擇出來的最有決策樹,不是從原始決策樹T的所有可能子樹中得到,所以有可能會找到到最有決策樹。

 

其它如Minimum Error Pruning(MEP),Critical Value Pruning(CVP),Optimal Pruning(OPP),Cost-Sensitive Decision Tree Pruning(CSDTP)等方法,這些剪枝方法各有利弊,關注不同的優化點,感興趣的同學可以學習下。

 

剪枝過程特別重要,所以在最優決策樹生成過程中占有重要地位。有研究表明,剪枝過程的重要性要比樹生成過程更為重要,對於不同的划分標准生成的最大樹(Maximum Tree),在剪枝之后都能夠保留最重要的屬性划分,差別不大。反而是剪枝方法對於最優樹的生成更為關鍵。重點理解這些剪枝方法的理論,對於最終最優樹的生成是有好處的,其中上篇文章http://isilic.iteye.com/blog/1844097中C4.5使用了PEP剪枝方法,本文的CART使用了CCP的剪枝方法,實際上,不應該對於算法使用的剪枝方法過於追根究底,而是應該對於剪枝過程理解透徹,對於在何種場景下對於不同的數據類型使用何種的剪枝方法能夠獲得最優樹的選擇,才是真正理解了剪枝的理論和重要性。

 

下面再回到CART算法,看下算法的優點:

1:沒有分布假設、沒有數據同質性(Homogeneity)要求

2:觀測值屬性可以是分類、離散、連續的混合。

4:對異常值(Outlier)值不敏感,異常值一般會被處理掉

5:在面對缺失值、變量多等問題時,CART顯得給長穩健(ROBUST)

 

缺點:

1:非基於概率模型,很難對決策樹的結果的准確程度做度量

 

CART算法的內容就到這里,再擴充下Decision Tree算法的內容。上面的這些決策樹算法都是單變量(Univariate)算法,實際上還有多變量(MultiVariate)算法,只是使用比較少。在Machine Learning In Action第九章中有關於Tree-Based Regression,是講解CART中Regression樹的內容。尤其是在文章中間提到了多變量回歸樹算法,每次的判定點有兩個變量組成,對於本文的內容是個非常好的擴充,可以學習下。另外要提一下的是該章節中講解的CART生成比較簡單,尤其是生成最大樹的方法;不過樹生成和數剪枝(包括預剪枝和后剪枝)都有體現,算是分不錯的代碼文檔;感興趣的可以直接在這份代碼上修改。

 

后面附加了份關於決策樹的概論內容綜述,地址參見:http://www.ise.bgu.ac.il/faculty/liorr/hbchap9.pdf 。內容比較全,包括划分標准、剪枝方法,不過不夠深入,只有公式和簡單的介紹,如果有基礎的話話,倒是份不錯的文檔。如果不能觀看的話,文后有附件。

另外在百度文庫上有個PPT不錯:http://wenku.baidu.com/view/415c3cc19ec3d5bbfd0a7464.html,供參考。

 

其實本文的重點是剪枝的內容,除去這點,CART算法使用的GINI指數內容也是本文的重點。

本文內容結束。 

 

以上內容轉自:http://isilic.iteye.com/blog/1841339

http://isilic.iteye.com/blog/1844097

http://isilic.iteye.com/blog/1846726


免責聲明!

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



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