在《机器学习笔记:ID3算法建立决策树(一)》中记录了ID3算法的计算公式和步骤,现在用例子记录一下ID3构建决策树的过程。
对以下数据进行分类:
- | 是否能飞? | 是否有羽毛? | 是小鸟? |
---|---|---|---|
1 | 是 | 是 | 是 |
2 | 是 | 是 | 是 |
3 | 是 | 否 | 否 |
4 | 否 | 是 | 否 |
5 | 是 | 否 | 是 |
是否能飞用0,1表示,0不能飞,1能飞;
是否有羽毛用0,1表示,0没有羽毛,1有羽毛;
也就是用是否能飞,是否有羽毛去判断一个东西是不是小鸟。
用矩阵表达如下:
def createDataSet():
dataSet = [[1,1,'是'],
[1,1,'是'],
[1,0,'否'],
[0,1,'否'],
[0,1,'否']]
labels = ['能飞的','有羽毛的']
return dataSet, labels
根据熵公式计算熵:
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 * math.log(prob,2)
return shannonEnt
对整个样本数据按类别划分子集:
def splitDataSet(dataSet,axis,value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
#featVec 是一维数组,下标为axis元素之前的值加入到reducedFeatVec
reducedFeatVec = featVec[:axis]
#下一行的内容axis+1之后的元素加入到reducedFeatVec
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) #值去重
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[:]
#对每个特征集递归调用建树方法
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
return myTree
调用
myDat,labels = createDataSet()
print(createTree(myDat, labels))
结果
{'能飞的': {0: '否', 1: {'有羽毛的': {0: '否', 1: '是'}}}}
接下来继续学习图形化显示决策树,更直观。
总结
计算各属性的信息增益,找出最大者为根节点
先验熵:样板数据中,先求出各种值出现的概率P(ui),再利用公式求熵
后验熵:对其他列,先求出各种值出现的概率P(vi),再求出先验列中各种值对本列取值为vi时的概率P(u|vi),再根据公式求熵H(U|vi),把u为各种值的情况下的H(U|vi) 算出来
条件熵:对后验熵在输出符号集V中求期望,接收到全部符号后对信源的不确定性,根据后验熵得到的P(vi)乘以H(U|vi)之和
信息增益:先验熵与条件熵的差,即先验熵中求得的P(ui)减去条件熵
拆分子集:对作为根节点的属性的数据根据不同的值分成子集。
对剩余属性重复上述步骤。
这是个人理解,如果有不到位的地方,或者有理解偏差,希望有人能指出扶正,交流学习。