ID3是决策树的一种。
以下的内容出自
1. 机器学习实战 Peter Harrington
2. 统计学习方法 李航
下面首先是一段python代码:
# coding=utf-8
from math import log
import operator
def clacShannonEnt(dataSet):#计算给定数据集的香农熵
numEntries=len(dataSet)
labelCount={}
for featVec in dataSet:
currentLabel=featVec[-1]
if currentLabel not in labelCount.keys():
labelCount[currentLabel]=0
labelCount[currentLabel]+=1
shannonEnt=0.0
for key in labelCount:
prob=float(labelCount[key])/numEntries
shannonEnt-=prob*log(prob,2)
return shannonEnt
def createDataSet():
dataSet=[[1,3,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']]
labels=['no surfacing','flippers']
return dataSet,labels
#分类的类别越多,越无序
#香农熵越小,集合越纯,当集合仅仅只有一种 则H=p(x)logp(x)=1*log1=0
#当x可以取值很多的时候,H越大
#将数据集合中的实例按照给定的特征来划分,每次去掉一个特征
def splitDataSet(dataSet,axis,value):
retDataSet=[]
for featVec in dataSet:
if featVec[axis]==value:
reducedFeatVec=featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
#找到信息增益最大的feature分裂 Gain(S)=Entroy(S原始)-sum(SV/S*Entroy(SV))
def chooseBestFeatureToSplit(dataSet):
numFeatures=len(dataSet[0])-1
baseEntroy=clacShannonEnt(dataSet)#计算原始香农熵
bestInfoGain=0.0
bestFeature=-1
for i in range(numFeatures):
featList=[example[i] for example in dataSet]#获得第i个feature的所有可能取值
uniqueVals=set(featList)#unique it
newEntroy=0.0
for value in uniqueVals:#for循环计算SV/S*entroy(SV)的sum
subDataSet=splitDataSet(dataSet,i,value)
prob=len(subDataSet)/float(len(dataSet))
newEntroy+=prob*clacShannonEnt(subDataSet)
infoGain=baseEntroy-newEntroy
if infoGain>bestInfoGain:
bestInfoGain=infoGain
bestFeature=i
return bestFeature #返回最佳的分裂feature
#当数据集已经处理了所有的feature,那就只剩下了label,使用多数表决法则来决定该叶子节点的分类类型
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]
#创建ID3 Tree
def createTree(dataSet,labels):
classList=[example[-1] for example in dataSet]
if classList.count(classList[0])==len(classList):#若所有的类别都相同,那么直接返回。
# print classList[0]
return classList[0]
if len(dataSet[0])==1:#当消耗掉了所有的feature 则要采用多数表决的方式
return majorityCnt(classList)
bestFeat=chooseBestFeatureToSplit(dataSet)#找到dataSet中最好的Split Feature
bestFeatLabel=labels[bestFeat]
# print bestFeatLabel
myTree={bestFeatLabel:{}}
del(labels[bestFeat])#消耗掉feature
featValues=[example[bestFeat] for example in dataSet]
uniqueVals=set(featValues)
for value in uniqueVals:
subLable=labels[:]
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLable)
return myTree
#执行分类操作 ID3决策树
def classify(inputTree,featLabels,testVec):
firstStr=inputTree.keys()[0]
#print firstStr
secondDict=inputTree[firstStr]
featIndex=featLabels.index(firstStr)#找到最佳Split Tree对应的Feature Index
for key in secondDict.keys():
if testVec[featIndex]==key:
if type(secondDict[key]).__name__=='dict':
classLabel=classify(secondDict[key],featLabels,testVec)
else:
classLabel=secondDict[key]
return classLabel
#存储树
def storeTree(inputTree,filename):
import pickle
fw=open(filename,'w')
pickle.dump(inputTree,fw)
fw.close()
#从硬盘获得树
def grabTree(filename):
import pickle
fr=open(filename)
return pickle.load(fr)
#myDat,myLabel=createDataSet()
#myTree=createTree(myDat,myLabel)
myTree=grabTree("id3.txt")
print myTree
myLabel=['no surfacing','flippers']
print classify(myTree,myLabel,[1,3])
#storeTree(myTree,'id3.txt')
1. 度量数据集合中数据的一致性:香农熵
2. 信息增益
ID3决策树分类器就像带有终止块的流程图,终止块表示分类的结果,而中间的块表示数据集的feature。开始处理数据集的时候,首先需要测量数据集合的不一致性(香农熵可以表示);然后寻找最优的方案来切割数据集(通常使用信息增益最大的概念);直到数据集属于同一类或者是feature已经消耗完毕(每次split都会消耗掉一个feature)。ID3算法无法直接处理数值型数据,尽管可以将数值型数据转化为标称型数据,但是如果存在了过多的特征划分,那么会存在overfiting的问题。
ID3算法与KNN相比,ID3给出的数据形式非常容易理解,KNN无法给出数据的内在含义。
1.数学上如何使用信息论划分数据集?当前数据集上哪个特征在划分数据分类时候起到了决定性的作用?
答:1. 信息增益:划分数据集合之前之后信息发生的变化
划分数据集的原则:是的无序的数据变得更加有序。
可以使用信息论中的香农熵来度量数据集合的混乱程度。
香农熵是信息的期望值,信息x的信息量为-log(p(x),2)
所以香农熵为H=sum(p(label=x)log(p(x=label),2)) p(label==x)标签为x的概率。
香农熵表示了一个dataSet的混乱成都,当类别标签越多,也就是所dataSet越杂乱无章,那么H越大。若p(label==x)==1那么这个dataSet是非常纯净的,则H=1*log(1,2)=0
也就是说:H越大,越杂乱;H越小,越纯净。这也是为什么要找信息增益最大的原因。
计算香农熵的伪代码如下:
Del calcShannonEnt(dataSet):
numEntries=len(dataSet)
labelCounts={}
For featVec in dataSet:
currentLabel=featVec[-1]
If currentLabel not in labelCounts.keys():
labelCounts[currentLabel]=0
labelCount[currentLabel]+=1
shannonEnt=0.0
For key in labelCounts:
Prob=float(labelCounts[key])/numEntries
shannonEnt-=Prob*log(Prob,2)
Return shanonEnt
使用信息增益来划分数据集:
数据集的无序程序可以使用香农熵来度量。对每个特征都进行一次划分,来找到使用哪个feature会是的信息增益最大。
Gain(s)=Entroy(s原始)-sum((Sv/S)*(Entroy(Sv))
2. 创建ID3决策树的伪代码:
检测数据集中的每个子项是否属于同一个分类or 检测数据集中的feature是否已经被消耗殆尽
If So return 类标签 or 多数表决
Else:
寻找划分数据集最好的特征(指的是采用该特征来划分数据集该数据集合的信息增益最大)
划分数据集
创建分支节点
For 每个划分的子集
调用createTree 并返回到分支节点中
Return 分支节点
ATTENTION:
ID3树构造只适用于标称型数据,因此应该将数值型数据离散化。
一些决策树算法使用二分法划分数据,ID3不是这样的。如果依据某个feature划分数据,若该feature可能去4个值,那么该feature下将会有4个分支。每次划分数据集的时候仅仅采用一个feature,也即是信息增益最大的feature。