在构造决策树时候,我们需要解决的第一个问题是:当前数据集上哪个特征在划分数据时候起决定作用,为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征。
创建分支的伪代码函数createBrance()如下所示:
检测数据集中每个子项是够属于同一分类:
If so return 类标签;
else
寻找划分数据集的最好特征
划分数据集
创建分支节点
for 每个划分的子集
调用函数createBrance并增加返回结果到分支节点中
return 分支节点
一般算法采用二分法划分数据,这里我们采用ID3算法,每次划分数据集我们只选取一个特征属性,如果训练集中存在20个特征,第一次我们选择哪个特征作为划分的参考属性呢?
划分数据集的最大原则是:将无序的数据变得更加有序,我们可以使用多重方法划分数据集,这里我们引入信息增益来作为我们划分数据的主要工具。
那么什么是信息增益呢?在划分数据集前后信息发生的变化称为信息增益
如果知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得信息增益,获得信息增益最高的特征就是最好的选择。
在这之前我们需要了解一下,集合信息的度量方式:香农熵或者简称为熵
在明确这个概念之前,我们必须知道信息的定义,如果带分类的事物可能划分在多个分类中,则符号 xi 的信息定义为:
from math import log
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)
return shannonEnt
def createDataSet():
dataSet=[[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'maybe']]
labels=['no surfacing','flippes']
return dataSet,labels
if __name__=='__main__':
myDat,labels=createDataSet()
print calcShannonEnt(myDat)
我们学习了如何度量数据集的无序程序,分类算法除了需要测量信息熵,还需要分划分数据集,度量划分数据集的熵,以便判断当前是否正确地划分数据集。
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
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