引言
決策樹從本質上是從訓練數據集上訓練處一組分類規則,完全依據訓練數據,所得規則容易發生過擬合,這也是決策樹的缺點,不過可以通過決策樹的剪枝,來提高決策樹的泛化能力。
由此,決策樹的創建可包括三部分:特征選擇、決策樹的生成以及決策樹的剪枝;決策樹的應用包括:分類、回歸以及特征選擇。
決策樹最經典的算法包括:ID3、C4.5以及CART算法,ID3與C4.5算法相似,C4.5在特征選擇時選用的信息准則是信息增益比,而ID3用的是信息增益;因為信息增益偏向於選擇具有較多可能取值的特征
基於信息論的特征選擇
注意:熵表示隨機變量的不確定性,熵值越大表示隨機變量含有的信息越少,變量的不確定性越大。
1) 香儂定義一個數據的信息可按下式計算 (此處是以2為底的對數):
2)熵表示一個數據集合信息的期望,可按下式計算:(該式不理解,可想象下,求變量期望的公式,p(xi) 為變量 xi以及信息 l(xi) 的概率,概率乘以變量(信息)即為變量(信息)的期望)
3)特征 AA 對數據集 DD 的信息增益為:
上式中,設訓練數據集為D,其樣本容量為|D|,即樣本個數,設共有K個類Ck,k=1,2,...,K Ck,k=1,2,...,K , |Ck| 為Ck的樣本個數,
根據特征A 的取值將 D 划分為n個子集D1,D2,...,Dn, |Di|為 Di的樣本數,Dik=Di⋂Ck ,|Dik|為 Dik的樣本個數.
如下表和圖所示:
feature1(A) | feature1 | feature3 | labels |
---|---|---|---|
a1 | b1 | c1 | y |
a1 | b2 | c2 | n |
a1 | b1 | c2 | n |
a1 | b1 | c2 | n |
a2 | b1 | c1 | y |
a2 | b2 | c2 | y |
a2 | b1 | c1 | n |
python實現:
1 def calcShannonEnt(dataset):#計算熵 2 numSamples = len(dataset) 3 labelCounts = {} 4 for allFeatureVector in dataset: 5 currentLabel = allFeatureVector[-1] 6 if currentLabel not in labelCounts.keys(): 7 labelCounts[currentLabel] = 0 8 labelCounts[currentLabel] += 1 9 entropy = 0.0 10 for key in labelCounts: 11 property = float(labelCounts[key])/numSamples 12 entropy -= property * log(property,2) 13 return entropy 14 def BestFeatToGetSubdataset(dataset): 15 #下邊這句實現:除去最后一列類別標簽列剩余的列數即為特征個數 16 numFeature = len(dataset[0]) - 1 17 baseEntropy = calcShannonEnt(dataset) 18 bestInfoGain = 0.0; bestFeature = -1 19 for i in range(numFeature):#i表示該函數傳入的數據集中每個特征 20 # 下邊這句實現抽取特征i在數據集中的所有取值 21 feat_i_values = [example[i] for example in dataset] 22 uniqueValues = set(feat_i_values) 23 feat_i_entropy = 0.0 24 for value in uniqueValues: 25 subDataset = getSubDataset(dataset,i,value) 26 #下邊這句計算pi,實現計算信息增益最大的特征 27 prob_i = len(subDataset)/float(len(dataset)) 28 feat_i_entropy += prob_i * calcShannonEnt(subDataset) 29 infoGain_i = baseEntropy - feat_i_entropy 30 if (infoGain_i > bestInfoGain): 31 bestInfoGain = infoGain_i 32 bestFeature = i 33 return bestFeature
決策樹生成
決策樹生成可用下邊的流程圖表示:
ID3算法python實現代碼:
1 # -*- coding: utf-8 -*- 2 from math import log 3 import operator 4 import pickle 5 ''' 6 輸入:原始數據集、子數據集(最后一列為類別標簽,其他為特征列) 7 功能:計算原始數據集、子數據集(某一特征取值下對應的數據集)的香農熵 8 輸出:float型數值(數據集的熵值) 9 ''' 10 def calcShannonEnt(dataset): 11 numSamples = len(dataset) 12 labelCounts = {} 13 for allFeatureVector in dataset: 14 currentLabel = allFeatureVector[-1] 15 if currentLabel not in labelCounts.keys(): 16 labelCounts[currentLabel] = 0 17 labelCounts[currentLabel] += 1 18 entropy = 0.0 19 for key in labelCounts: 20 property = float(labelCounts[key])/numSamples 21 entropy -= property * log(property,2) 22 return entropy 23 24 ''' 25 輸入:無 26 功能:封裝原始數據集 27 輸出:數據集、特征標簽 28 ''' 29 def creatDataSet(): 30 dataset = [[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,0,'no']] 31 labels = ['no surfacing','flippers'] 32 return dataset,labels 33 34 ''' 35 輸入:數據集、數據集中的某一特征所在列的索引、該特征某一可能取值(例如,(原始數據集、0,1 )) 36 功能:取出在該特征取值下的子數據集(子集不包含該特征) 37 輸出:子數據集 38 ''' 39 def getSubDataset(dataset,colIndex,value): 40 subDataset = [] #用於存儲子數據集 41 for rowVector in dataset: 42 if rowVector[colIndex] == value: 43 #下邊兩句實現抽取除第colIndex列特征的其他特征取值 44 subRowVector = rowVector[:colIndex] 45 subRowVector.extend(rowVector[colIndex+1:]) 46 #將抽取的特征行添加到特征子數據集中 47 subDataset.append(subRowVector) 48 return subDataset 49 50 ''' 51 輸入:數據集 52 功能:選擇最優的特征,以便得到最優的子數據集(可簡單的理解為特征在決策樹中的先后順序) 53 輸出:最優特征在數據集中的列索引 54 ''' 55 def BestFeatToGetSubdataset(dataset): 56 #下邊這句實現:除去最后一列類別標簽列剩余的列數即為特征個數 57 numFeature = len(dataset[0]) - 1 58 baseEntropy = calcShannonEnt(dataset) 59 bestInfoGain = 0.0; bestFeature = -1 60 for i in range(numFeature):#i表示該函數傳入的數據集中每個特征 61 # 下邊這句實現抽取特征i在數據集中的所有取值 62 feat_i_values = [example[i] for example in dataset] 63 uniqueValues = set(feat_i_values) 64 feat_i_entropy = 0.0 65 for value in uniqueValues: 66 subDataset = getSubDataset(dataset,i,value) 67 #下邊這句計算pi 68 prob_i = len(subDataset)/float(len(dataset)) 69 feat_i_entropy += prob_i * calcShannonEnt(subDataset) 70 infoGain_i = baseEntropy - feat_i_entropy 71 if (infoGain_i > bestInfoGain): 72 bestInfoGain = infoGain_i 73 bestFeature = i 74 return bestFeature 75 76 ''' 77 輸入:子數據集的類別標簽列 78 功能:找出該數據集個數最多的類別 79 輸出:子數據集中個數最多的類別標簽 80 ''' 81 def mostClass(ClassList): 82 classCount = {} 83 for class_i in ClassList: 84 if class_i not in classCount.keys(): 85 classCount[class_i] = 0 86 classCount[class_i] += 1 87 sortedClassCount = sorted(classCount.iteritems(), 88 key=operator.itemgetter(1),reverse = True) 89 return sortedClassCount[0][0] 90 91 ''' 92 輸入:數據集,特征標簽 93 功能:創建決策樹(直觀的理解就是利用上述函數創建一個樹形結構) 94 輸出:決策樹(用嵌套的字典表示) 95 ''' 96 def creatTree(dataset,labels): 97 classList = [example[-1] for example in dataset] 98 #判斷傳入的dataset中是否只有一種類別,是,返回該類別 99 if classList.count(classList[0]) == len(classList): 100 return classList[0] 101 #判斷是否遍歷完所有的特征,是,返回個數最多的類別 102 if len(dataset[0]) == 1: 103 return mostClass(classList) 104 #找出最好的特征划分數據集 105 bestFeat = BestFeatToGetSubdataset(dataset) 106 #找出最好特征對應的標簽 107 bestFeatLabel = labels[bestFeat] 108 #搭建樹結構 109 myTree = {bestFeatLabel:{}} 110 del (labels[bestFeat]) 111 #抽取最好特征的可能取值集合 112 bestFeatValues = [example[bestFeat] for example in dataset] 113 uniqueBestFeatValues = set(bestFeatValues) 114 for value in uniqueBestFeatValues: 115 #取出在該最好特征的value取值下的子數據集和子標簽列表 116 subDataset = getSubDataset(dataset,bestFeat,value) 117 subLabels = labels[:] 118 #遞歸創建子樹 119 myTree[bestFeatLabel][value] = creatTree(subDataset,subLabels) 120 return myTree 121 122 ''' 123 輸入:測試特征數據 124 功能:調用訓練決策樹對測試數據打上類別標簽 125 輸出:測試特征數據所屬類別 126 ''' 127 def classify(inputTree,featlabels,testFeatValue): 128 firstStr = inputTree.keys()[0] 129 secondDict = inputTree[firstStr] 130 featIndex = featlabels.index(firstStr) 131 for firstStr_value in secondDict.keys(): 132 if testFeatValue[featIndex] == firstStr_value: 133 if type(secondDict[firstStr_value]).__name__ == 'dict': 134 classLabel = classify(secondDict[firstStr_value],featlabels,testFeatValue) 135 else: classLabel = secondDict[firstStr_value] 136 return classLabel 137 138 139 ''' 140 輸入:訓練樹,存儲的文件名 141 功能:訓練樹的存儲 142 輸出: 143 ''' 144 def storeTree(trainTree,filename): 145 146 fw = open(filename,'w') 147 pickle.dump(trainTree,fw) 148 fw.close() 149 def grabTree(filename): 150 151 fr = open(filename) 152 return pickle.load(fr) 153 154 155 if __name__ == '__main__': 156 dataset,labels = creatDataSet() 157 storelabels = labels[:]#復制label 158 trainTree = creatTree(dataset,labels) 159 classlabel = classify(trainTree,storelabels,[0,1]) 160 print classlabel
本文來自於:
謝謝博主