在前面ML經典算法:決策樹(1)中描述了什麼是決策樹算法,以及介紹了三種不同的屬性劃分方法。不僅要知道理論,還要實踐,下面對這信息增益劃分方法進行python實現。
先來複習一下信息增益:
信息增益越大,則意味着使用屬性 α 來進行劃分所獲得的"純度提升"越大。
假定離散屬性 α 有 V 個可能的取值 {α1 , α2. …, αv},若使用 α 來對樣本集D 進行劃分,則會產生 V 個分支結點,其中第 u 個分支結點包含了 D 中所有在屬性 α 上取值爲 α" 的樣本記爲 Du. 先計算出 Du的信息熵(下面會介紹),再考慮到不同的分支結點所包含的樣本數不同,給分支結點賦予權重 IDuI / IDI ,即樣本數越多的分支結點的影響越大,於是可計算出用屬性 α 對樣本集 D 進行劃分所獲得的"信息增益",公式如下:
其中Ent(D)爲"信息熵" (information entropy),信息熵是度量樣本集合純度最常用的一種指標。假定當前樣本集合 D 中第 k 類樣本所佔的比例爲 Pk (k = 1,2,. . . , IYI) , D的信息熵定義爲:Ent(D)
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 = shannonEnt - prob * log(prob, 2)
return shannonEnt
2. 求信息增益
首先爲求信息增益中第axis個屬性值爲value的樣本子集(即在屬性 α 上取值爲 α" 的樣本 Du):
# 劃分數據集,axis:按第幾個屬性劃分,value:要返回的子集對應的屬性值
# 獲得數據集中,第axis個屬性值爲value的其他屬性和標籤的樣本子集
def splitDataSet(dataSet, axis, value):
retDataSet = []
featVec = []
for featVec in dataSet:
if featVec[axis] == value:
# 獲得第axis個屬性之前的幾個屬性
reducedFeatVec = featVec[:axis]
# 獲得第axis個屬性之後的幾個屬性
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
然後求Du的信息熵和權重 IDuI / IDI ,最後求得信息增益,獲得增益最大的那個屬性:
# 選擇最好的數據集劃分方式
def chooseBestFeatureToSplit(dataSet):
# 獲得屬性的個數,-1是去掉標籤
numFeatures = len(dataSet[0]) - 1
# 獲得信息熵Ent(D)
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
# 對每個屬性技術信息增益
for k in range(numFeatures):
# 獲得所有樣本的第i個屬性值
featList = [example[k] for example in dataSet]
# 該屬性的取值集合
uniqueVals = set(featList)
newEntropy = 0.0
# 對每一種取值計算信息增益
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, k, value)
# 樣本集合中第 k 類樣本所佔的比例:權重 IDuI / IDI
prob = len(subDataSet) / float(len(dataSet))
# 獲得獲得第k類屬性值爲value的信息熵:calcShannonEnt(subDataSet)
newEntropy += prob * calcShannonEnt(subDataSet)
# 獲得信息增益
infoGain = baseEntropy - newEntropy
# 選擇信息增益最大的屬性
if (infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = k
return bestFeature