決策樹ID3算法的實現,分四大步詳細解釋,參考機器學習實戰
一、編寫計算歷史數據的經驗熵函數
from math import log def calcShannonEnt(dataSet): numEntries = len(dataSet) labelCounts = {} for elem in dataSet: #遍歷數據集中每條樣本的類別標簽,統計每類標簽的數量 currentLabel = elem[-1] if currentLabel not in labelCounts.keys(): #如果當前標簽不在字典的key值中,則初始化該標簽對應的值為0 labelCounts[currentLabel] = 0 labelCounts[currentLabel]+= 1 #計數+1 shannonEnt = 0.0 for key in labelCounts:#開始計算歷史數據的經驗熵 prob = float(labelCounts[key])/numEntries#每類標簽在全部歷史數據中所占概率 shannonEnt -= prob * log(prob,2) #log base 2 return shannonEnt
二、按照指定特征和其特征值來劃分數據集
參數axis指定是第幾個特征,value是該特征什麽值,這個函數會在第三步的函數和第四步裏被調用,主要作用在相應步結束後介紹
def splitDataSet(dataSet, axis, value): retDataSet = [] for elem in dataSet: if elem[axis] == value: reducedFeatElem=elem[:axis] reducedFeatElem.extend(elem[axis+1:]) retDataSet.append(reducedFeatElem) return retDataSet
三、計算每個特征的熵,求得信息增益,返回使得信息增益最大的特征
比較難理解的就是第二個for叠代中的內容,會在這個函數編寫結束後解釋,其他的解釋就直接註釋在代碼中了
def chooseBestFeatureToSplit(dataSet): numFeatures = len(dataSet[0]) - 1 #統計特征數目 baseEntropy = calcShannonEnt(dataSet) #計算歷史數據的經驗熵 bestInfoGain = 0.0; bestFeature = -1 for i in range(numFeatures): #叠代所有特征 featList = [sample[i] for sample in dataSet]#創建所有樣本中每一個樣本的這個特征的取值 uniqueVals = set(featList) #獲得該特征的取值集合,即剔除重復的特征取值 newEntropy = 0.0 for value in uniqueVals: subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet)/float(len(dataSet)) newEntropy += prob * calcShannonEnt(subDataSet)#按照條件經驗熵熵的公式計算i列特征取值為value的條件下的條件經驗熵 infoGain = baseEntropy - newEntropy #計算信息增益 if (infoGain > bestInfoGain): #與目前最大的信息增益比較 bestInfoGain = infoGain #更新最大信息增益 bestFeature = i #更新使得信息增益最大的特征列i return bestFeature #returns an integer
chooseBestFeatureToSplit函數調用splitDataSet的作用就是獲得axis列特征的取值為value的樣本,splitDataSet函數返回值長度用於計算axis列特征取值為value時的樣本占全部樣本數量的概率,返回值作為參數傳遞給calcShannonEnt函數可以計算axis列特征取值為value時的經驗熵
四、遞歸構建決策樹
遞歸結束的條件是:程序已經遍歷完樣本數據的全部特征列或者所有實例樣本屬於同一類(即標簽類別相同)
另外,如果所有實例樣本標簽類別相同則得到一個葉子節點
(一)定義葉子節點中的實例類別
主要是當劃分數據集的全部屬性已經處理完,該葉子節點中的實例樣本的類別標簽不是唯一的,如何定義該葉子節點的最終類別,此時采用多數表決的方法決定
import operator def majorityCnt(classList): classCount={} for classlabel in classList: if classlabel not in classCount.keys(): classCount[classlabel] = 0 classCount[vote] += 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
(二)、構造決策樹
參數dataSet為傳入的數據集,labels為特征名
def createTree(dataSet,labels): classList = [example[-1] for example in dataSet]#獲取傳入數據集的類別標簽列表 if classList.count(classList[0]) == len(classList): #當傳入數據集的類且標簽全部相同時停止遞歸 return classList[0] if len(dataSet[0]) == 1: #當傳入的數據集只剩一個標簽列時(每次調用劃分數據集函數splitDataSet時都會刪除一個特征列)停止遞歸 return majorityCnt(classList) #返回標簽列表中類別數量最多的類別 bestFeat = chooseBestFeatureToSplit(dataSet) #調用chooseBestFeatureToSplit選擇最優特征 bestFeatLabel = labels[bestFeat] myTree = {bestFeatLabel:{}} #使用字典存儲每次叠代中的最優特征 del(labels[bestFeat]) featValues = [sample[bestFeat] for sample in dataSet] uniqueVals = set(featValues) for value in uniqueVals: subLabels = labels[:] #copy all of labels, so trees don‘t mess up existing labels myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels) return myTree
createTree函數難理解的部分在for叠代裏,作用就是對於當前最優特征的不同取值構建分支,每一個特征取值可以構建出一個分支,具體是利用for循環對於當前最優特征的每個取值value下,遞歸調用createTree函數,參數為調用splitDataSet函數(以當前最優特征列,以及最優特征取值作為參數)返回dataSet中滿足最優特征對應的列取值為value剔除了該最優特征列的樣本數據集
以上四大步驟就實現了基本的ID3算法,下面編寫一個創建數據集的函數(背後的意義是:判斷是否為魚類假設由兩個特征就可以判斷,分別是“不浮出水面是否可以生存”,“是否腳蹼”)來測試算法def createDataSet(): dataSet = [[1, 1, ‘yes‘], [1, 1, ‘yes‘], [1, 0, ‘no‘], [0, 1, ‘no‘], [0, 1, ‘no‘]] labels = [‘no surfacing‘,‘flippers‘] #change to discrete values return dataSet, labels
測試:
mytree字典的含義如圖:
五、利用以上實現的算法實現決策樹分類,需要遞歸遍歷整棵決策樹
def classify(inputTree,featLabels,testVec): firstStr = inputTree.keys()[0] secondDict = inputTree[firstStr] featIndex = featLabels.index(firstStr)#為了確定某個特征在數據集的位置 key = testVec[featIndex] valueOfFeat = secondDict[key] if isinstance(valueOfFeat, dict): classLabel = classify(valueOfFeat, featLabels, testVec) else: classLabel = valueOfFeat return classLabel
測試:
對應數據集標簽可以看到分類正確,結束啦^.^
決策樹ID3算法的實現,分四大步詳細解釋,參考機器學習實戰