上一篇博客我們看了一個決策樹分類的例子,但是我們沒有深入決策樹分類的內部原理。
這節我們討論的決策樹分類的所有特征的特征值都是離散的,明白了離散特征值如何分類的原理,連續值的也不難理解。
決策樹分類的核心在於確定那一個特征的那一個特征值分類最有效,可能不同的場景,每個人采用的衡量方法也不一樣,這里我們采用香農熵。
下面我們看一下簡單的例子
五個樣例,兩個特征(是否浮上水面,是否有鰭),判斷該動物是否是水生(類別)
def createDataSet(): dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] labels = ['no surfacing','flippers'] return dataSet, labels
計算特征划分的香農熵:傳入一個數據集,根據類別計算香農熵(每一個類別的概率p*log(p,2)的和*-1)
def calcShannonEnt(dataSet): #樣本個數 numEntries = len(dataSet) labelCounts = {} for featVec in dataSet: # currentLabel = featVec[-1] if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 labelCounts[currentLabel] += 1 shannonEnt = 0.0 for key in labelCounts: prob = float(labelCounts[key])/numEntries shannonEnt -= prob * log(prob,2) #log base 2 return shannonEnt
划分數據集:根據傳入的特征(對應數據中的那一列)和特征值對數據進行划分(把傳入特征對應的列刪掉,把特征列等於傳入特征值的行刪掉),返回根據該特征的特征值划分的結果數據集
def splitDataSet(dataSet, axis, value): retDataSet = [] for featVec in dataSet: if featVec[axis] == value: reducedFeatVec = featVec[:axis] #chop out axis used for splitting reducedFeatVec.extend(featVec[axis+1:]) retDataSet.append(reducedFeatVec) return retDataSet
尋找最佳特征的特征值:遍歷所有特征的特征值,尋找最好的結果
def chooseBestFeatureToSplit(dataSet): numFeatures = len(dataSet[0]) - 1 # baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0; bestFeature = -1 for i in range(numFeatures): #遍歷特征 featList = [example[i] for example in dataSet]#得到特征列 uniqueVals = set(featList) #從特征列獲取該特征的特征值的set集合 newEntropy = 0.0 for value in uniqueVals: subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet)/float(len(dataSet)) newEntropy += prob * calcShannonEnt(subDataSet) infoGain = baseEntropy - newEntropy #計算當前划分的香農熵 if (infoGain > bestInfoGain): #比較是否是最好的結果 bestInfoGain = infoGain #記錄最好的結果和最好的特征 bestFeature = i return bestFeature #返回最好特征的對應的列下標
找到最好的特征,然后以最好的特征作為根節點,所有的特征值作為分支,繼續划分數據集,直到每個小數據集里面的類別都一樣或者沒有可以繼續划分的特征的
存在一種情況,已經沒有可以划分的特征了,但是這個數據集的類別有多個,怎么辦?按照前一篇博客說的,多數表決的方法決定改分支的類別:
下面的函數接受一個類別的列表,返回類別數多的類別
def majorityCnt(classList): classCount={} for vote in classList: if vote not in classCount.keys(): classCount[vote] = 0 classCount[vote] += 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
知道了怎么划分數據集,也知道划分停止的條件,下面我們可以生成決策樹了
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: #如果沒有繼續可以划分的特征,就多數表決決定分支的類別 return majorityCnt(classList) bestFeat = chooseBestFeatureToSplit(dataSet) bestFeatLabel = labels[bestFeat] myTree = {bestFeatLabel:{}} del(labels[bestFeat]) featValues = [example[bestFeat] for example 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
下面是生成的決策樹:
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
畫成圖示如下:

希望到這里能理解理解決策樹分類的原理了
