您的位置:首页 > 其它

机器学习实战-决策树

2018-01-22 13:27 274 查看


参考:http://blog.csdn.net/rujin_shi/article/details/78776996


前言

决策树(Decesion Tree)是一种基本的分类算法。它可以认为是if-then规则的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。其主要优点就是模型具有可读性,分类速度块。学习时,利用训练数据,根据损失函数最小化的原则建立决策树模型。旨在构建一个与训练数据拟合很好,并且复杂度小的决策树。因为从可能的决策树中直接选取最优决策树是NP问题,现实中采用启发方法学习次优的决策树。
总的来说,决策树就是找出每一列特征的最佳划分以及划分的先后顺序排列。

决策树学习算法包括3部分:特征选择、树的生成和树的剪枝。常用算法有ID3(本文采用)、C4.5CART算法。 
决策树的生成:通常使用信息增益最大,信息增益比最大,或基尼指数最小作为特征选择的准则。决策树的生成往往通过计算信息增益或其他指标,从根节点开始,递归地产生决策树。这相当于用信息增益或其他准则不断地选取局部最优 的特征,或将训练集分割为能够基本正确分类的子集。 
决策树的剪枝:由于生成的决策树存在过拟合的问题,需要对它进行剪枝(考虑全局最优)。决策树的剪枝,往往从已生成的树上剪掉一些叶结点或叶结点以上的树,并将其父结点或根结点作为新的叶结点。


3.1 决策树的构造

创建分支的伪代码如下: 




3.1.1 香农熵

    在可以评测哪个数据划分方式是最好的数据划分之前,我们必须学习如何计算信息增益。集合信息的度量方式成为香农熵或者简称为熵(entropy),这个名字来源于信息论之父克劳德·香农。

    如果看不明白什么是信息增益和熵,请不要着急,因为他们自诞生的那一天起,就注定会令世人十分费解。克劳德·香农写完信息论之后,约翰·冯·诺依曼建议使用”熵”
4000
这个术语,因为大家都不知道它是什么意思。

    熵定义为信息的期望值。在信息论与概率统计中,熵是表示随机变量不确定性的度量。如果待分类的事务可能划分在多个分类之中,则符号xi的信息定义为



    其中p(xi)是选择该分类的概率。有人可能会问,信息为啥这样定义啊?答曰:前辈得出的结论。这就跟1+1等于2一样,记住并且会用即可。上述式中的对数以2为底,也可以e为底(自然对数)。

    通过上式,我们可以得到所有类别的信息。为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值(数学期望),通过下面的公式得到:



    期中n是分类的数目。熵越大,随机变量的不确定性就越大。

    当熵中的概率由数据估计(特别是最大似然估计)得到时,所对应的熵称为经验熵(empirical entropy)。什么叫由数据估计?比如有10个数据,一共有两个类别,A类和B类。其中有7个数据属于A类,则该A类的概率即为十分之七。其中有3个数据属于B类,则该B类的概率即为十分之三。浅显的解释就是,这概率是我们根据数据数出来的。我们定义贷款申请样本数据表中的数据为训练数据集D,则训练数据集D的经验熵为H(D),|D|表示其样本容量,及样本个数。设有K个类Ck,k = 1,2,3,···,K,|Ck|为属于类Ck的样本个数,这经验熵公式可以写为:


 
         
程序清单3-1:计算给定数据集的香农熵


#计算给定数据集的香农熵


from math import log

def calcShannonEnt(dataSet):
numEntries=len(dataSet)#实例总数
labelCounts={}#判断每个类标签对应数据集的出现次数,用字典存储
for featVec in dataSet:
currentLabel=featVec[-1]
#labelCounts[currentLabel]=labelCounts.get(currentLabel,0)+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,'no']]
labels=['no surfacing','flippers']
return dataSet,labels

myData,labels=createDataSet()
print(len(myData[0]))
x=calcShannonEnt(myData)
print(x)

输出结果:
3

0.9709505944546686



3.1.2 划分数据集

程序清单3-2 按照给定特征划分数据集
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

data1=splitDataSet(myData,0,1)
data0=splitDataSet(myData,0,0)
print("取第一个特征值为1的数据集:",data1)
print("取第一个特征值为0的数据集:",data0)

输出结果:

取第一个特征值为1的数据集: [[1, 'yes'], [1, 'yes'], [0, 'no']]

取第一个特征值为0的数据集: [[1, 'no'], [1, 'no']]

这里注意append()和extend()的区别a=[4,5,6],b=[1,2,3]
a.append(b)结果是[4,5,6[1,2,3]],a.expend(b)结果是[4,5,6,1,2,3],


有了划分数据集和计算香农熵的算法,接下来就是设计如何划分数据集合了。
方法是对每个特征进行计算,找到信息增益最大的,就是最优的
程序清单3-3 选择最好的数据集划分
def choose_bestFeature_tosplit(dataset):
numfeature=len(dataset[0])-1#特征总数,减去最后一个标签
baseEntropy=calcShannonEnt(dataset)#初始熵
bestInfoGain=0.0                   #最大信息增益
bestFeature=-1                     #最优特征
for i in range(numfeature):
featlist=[example[i] for example in dataset]   #获取第i个特征的所有特征值,列表推到
uniqueVals=set(featlist)                       #将重复特征去除
newEntropy=0.0
for value in uniqueVals:
subDataSet=splitDataSet(dataset,i,value)
prob=(len(subDataSet)/float(len(dataset)))
# print("当前分类出现的概率:",prob)
newEntropy+=prob*calcShannonEnt(subDataSet)
inforGain=baseEntropy-newEntropy
# print("当前最好的信息增益是:",inforGain)
if (inforGain>bestInfoGain):
bestInfoGain=inforGain
bestFeature=i
return bestFeature


   #v3.x 以后,“/”运算符被命名为“真除”,

            #不再依据操作数类型选择返回值类型,保证计算结果数值上的精度是第一位的。

            #所以,无须再把操作数转变成浮点型,以保证运算结果不被截断小数部分。
程序清单3-4 构建决策树

在基于最好的属性值划分数据集后(第一次),数据将被向下传递到树分支的下一个节点,在这个节点上,我们再次划分数据。因此我们可以采用递归的原则处理数据集。 
递归结束的条件是: 
1.每个分支下的所有实例都具有相同的分类。 

  怎么判断具有相同的分类呢?
  首先获得所有的类标签,计算列表长度,然后计算第一类标签的数量,若两者相同,证明标签类只有一类
2.数据集已经处理了所有属性,但是类标签依然不唯一,此时我们需要使用投票的方式决定该叶子节点的分类。 


def majorityCnt(classList):#处理完所有特征属性,标签类依然不唯一,采用多数表决,类似KNN中classy0
classCount={}
for vote in classList:
if vote not in classCount.keys():           #计算每个特征值的出现次数,选择次数最多的
classCount[vote]=0
classCount[vote]+=1
sortedclassCount=sorted(classCount.items(),Key=operator.itemgetter(1),reverse=True)#根据键值排序
return sortedclassCount[0][0]

def createTree(dataSet,labels):
classlist=[example[-1] for example in dataSet]#返回的是yes和no
if classlist.count(classlist[0])==len(classlist):#当classlist中所有元素都相同
return classlist[0]
if len(dataSet[0])==1:#所有特征都用完了,采用多数表决法
return majorityCnt(classlist)
bestFeat=choose_bestFeature_tosplit(dataSet)
bestFeatLabel=labels[bestFeat]
myTree={bestFeatLabel:{}}
del(labels[bestFeat])
featValues=[example[bestFeat] for example in dataSet]#得到该划分特征属性的所有特征值
uniqueValues=set(featValues)

for value in uniqueValues:
subLabels=labels[:]    #防止对原来的labels改sublabels=labels只是对同一列表起别名
#print(bestFeatLabel)
myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)#递归
return myTree

#帮助理解           #dics={'a':{0:'q',1:'w',2:'e'}}
#dics['a']
#{0: 'q', 1: 'w', 2: 'e'}
#dics['a'][2]   'e'

bestfeature=choose_bestFeature_tosplit(myData)
print(bestfeature)
mytree=createTree(myData,labels)
print(mytree)

输出:
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

到这里,树就算是构建完毕了。但是这样的树并不直观,因此要将它图形化显示出来,这里就用到了matplotlib。下一节将继续。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: