决策树是一种通过推断分解,逐步缩小待推测事物范围的算法结构,重要任务就是理解数据中所蕴含的知识信息,可以使用不熟悉的数据集合,并从中提取出一系列规则,根据数据集创建规则的过程就是机器学习的过程。
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征的数据。
缺点:可能产生过度匹配的问题。
### 决策树的构造
使用信息论划分数据集,要知道当前数据集的哪个特征起决定性作用,为找到决定性特征,必须评估每个特征。原始数据集被划分成几个子集,分布在第一个决策点的所有分支上。如果某个分支下的所有数据属于同一个类型,则无需进一步划分,反之则重复划分数据子集。采用--ID3--算法划分数据集,每次划分数据集时只选取一个特征。
|不浮出水面可以生存| 是否有脚蹼 |属于鱼类
|1-是----------------------|-是------------|是
|2-是--------------------- |-是------------|是
|3-是--------------------- |-否------------|否
|4-否--------------------- |-是------------|否
|5-否--------------------- |-是------------|否
划分数据集最大原则:将无序数据变得更加有序,划分数据集之前之后信息的变化成为信息增益,计算每个特征值划分数据集获得的信息增益。计算增益的信息度量方式称为香农熵。
符号的信息定义为:l(xi) = -,需要计算所有类别的所有可能值包含的期望。通过下面的公式得到:
from math import log
import operator
#sys.path.append('G:/BasicPython/ML')
#print(sys.path)
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代表该标签类出现的概率,为该标签出现次数/总次数
prob = float(labelCounts[key])/numEntries
shannonEnt -= prob*log(prob,2)
return shannonEnt
创建一个数据字典,来统计数据集中最后一列即目标值的大小与次数,然后对目标值进行遍历,统计每类目标值的频率,和该目标值对应的信息熵。最后累加得到输入数据集的熵,熵越高则混合的数据越多。
划分数据集为了度量划分数据集的熵,以便判断是否正确划分。将对每个特征划分数据集的结果计算一次信息熵,然后判断哪个特征划分数据集是最好的方式。
按照给定特征划分数据集:
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
三个参数分别为:待划分数据集,划分数据集的特征,需要返回的特征的值,在if语句中将每个样本的特征项与给定特征值进行比较,相等的话,则挖去该特征项,并重新添加到新的数据集中。最后返回的数据集满足:都归属于同一个特征值划分的数据集。
接下来将遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到做好的特征划分方式。熵计算告诉我们如何划分数据集是最好的数据划分方式。
选择最好的数据集划分方式:
#选择最好的数据集划分方式
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
len(dataSet[0])-1可以得到数据集中特征的个数,然后计算得到初始的香农熵作为初始的比较值。遍历特征列表,得到特征对应的特征值列表featList,然后对特征值集合进行遍历,求得每个特征值的出现概率即权重,对每个特征值划分的数据集求香农熵,最后累加求和得到的是该特征对应的所有特征值的平均香农熵,也就是该特征对应的香农熵,将初始香农熵与之相减的含义是熵降低的显著程度,最后得到熵降低程度最大的的特征的索引。
前面都是从决策及构造决策树算法所需要的子功能模块。第一次划分后,数据集向下传递到分支的下一个节点,在该点上可以继续划分数据。因此可以用递归原则处理数据集。递归结束的条件:程序遍历完所有划分数据集的属性,每个分支下的所有实例具有相同分类。当数据集已经处理了所有的属性,但类标签仍不是唯一的,通常采用投票表决。
投票表决:
#对于所有特征已经消耗完,但类标签不是唯一时的投票表决
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
两个输入参数:数据集,标签列表。对数据集的类标签的值放入列表中,然后判断是否所有类标签的值都相同,是的话则返回该标签值;倘若特征消耗完但是仍未判断出类,则将类标签传给majorityCnt()的投票表决函数,返回“大多数”的类标签。以上都是两个终止条件。递归时,得倒当前数据集的最优特征的索引和特征名,构建决策树一个树的分支字典,键为判断分支条件的特征项,值为相应的分支。然后得到最优特征对应的所有特征值。遍历这些特征值,这些特征值作为分支,其值等于在该特征值条件下的数据集,即通过该特征值划分的数据集,需要下一层递归。