决策树
决策树的概念:
决策树算法是一种基本的分类与回归方法。在下面举例的两个问题中决策树算法用于分类。
官方课本化点的说辞为:
分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点和有向边组成。结点有两种类型:内部结点和叶结点。内部结点表示一个特征或属性,叶结点表示一个类。
用决策树对需要测试的实例进行分类:从根节点开始,对实例的某一特征进行测试,根据测试结果,将实例分配到其子结点;这时,每一个子结点对应着该特征的一个取值。如此递归地对实例进行测试并分配,直至达到叶结点。最后将实例分配到叶结点的类中。
简单点理解也就是:方法本身如其名,就是不断的决策,就像树一样,会有枝干分叉,不断分叉不断递进直到无法再分叉的叶,叶子就相当于最后的决策类型。最后得出结果。
其他相关:
熵: 熵指的是体系的混乱的程度。
信息论中的熵: 是一种信息的度量方式,表示信息的混乱程度。也就是香农熵。
信息增益: 在划分数据集前后信息发生的变化称为信息增益。
感觉和决策树没有什么关系的几个概念,十分重要。
一些需要在后面找到答案的问题:
1.对于不同的特征如何进行合理的选取,使最后分类结果正确性高?
2.不同特征在不同的分叉点的位置的影响有多大?如果有影响,怎样的放置能使最后分类结果正确性高?
3.决策树算法的核心是什么?
相关实例:
一、判定鱼类和非鱼类
想要处理的问题:
根据两个有关特征,将动物分为两类:鱼类和非鱼类
关于问题的解决:
①数据的收集:
dataSet = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
labels = ['no surfacing', 'flippers']
#已知的五组数据以及两个特征
②树构造算法(这里使用的是ID3算法,因此数值型数据必须离散化。)
什么是ID3算法?为什么数值型数据必须离散化?
关于ID3算法,先不细想。
③分析数据,可以把树状图画出来
④计算给定数据集的香农熵
def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
#下面这个for循环加上if条件语句用于计数,计算数据集中是鱼类与不是鱼类的数量,构成字典。
所以结果格式为:{'yes':int ,'no',int}与数据集信息有关。
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) #香农熵的计算公式,以2为底求对数
return shannonEnt
⑤划分数据集
def splitDataSet(dataSet,index,value):
retDataSet = []
for featVec in dataSet:
if featVec[index] == value: #以一个特征为条件将数据集进行划分。就相当于对树进行分叉。
reducedFeatVec = featVec[:index]
reducedFeatVec.extend(featVec[index+1:])
retDataSet.append(reducedFeatVec)
#结果格式如下:
[[1, 'no'], [1, 'no']]
[[1, 'yes'], [1, 'yes'], [0, 'no']]
return retDataSet
⑥选择数据集划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 //特征数
baseEntropy = calcShannonEnt(dataSet) //基本香农熵
bestInfoGain,bestFeature = 0.0,-1
#以下两个for循环为对单列特征进行操作,第一个for循环为将单个特征的所有值取出,第二个for循环为对单列值对香农熵,为此定义了一个newEntropy.
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
print("infoGain=",infoGain,'bsetFeature=',i,baseEntropy,newEntropy)
if(infoGain > bestInfoGain): //选出信息增益最大的特征值
bestInfoGain = infoGain
bestFeature = i
return bestFeature
⑦构造树的数据结构
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
#利用字典套字典的格式模仿树的结构,以此便于后面决策的运行顺利。
#例:{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
⑧运用决策树进行分类
def classify(inputTree,featLabels,testVec):#createTree得到的inputTree已经将最好的决策顺序得到了,此时将测试数据代入进行分类测试
firstStr = inputTree.keys()[0]
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr)
key = testVec[featIndex]
valueOfFeat = secondDict[key]
if isinstance(valueOfFeat,dict):
classLabel = classify(valueOfFeat,featLabels,testVec)
else:
classLabel = valueOfFeat
return classLabel
⑨测试运行及存储决策树
def fishTest():
myData,labels = createDataSet()
myTree = createTree(myData,copy.deepcopy(labels))
print(myTree)
print(classify(myTree,labels,[0,1]))
#存储决策树(相关库:pickle)
def storeTree(inputTree, filename):
import pickle
fw = open(filename, 'wb')
pickle.dump(inputTree, fw) //序列化对象,并将结果数据流写入到文件对象中。
fw.close()
def grabTree(filename):
import pickle
fr = open(filename, 'rb')
return pickle.load(fr) //反序列化对象。将文件中的数据解析为一个Python对象。
关于上面提出的问题的想法:
关于问题一:对于不同的特征如何进行合理的选取,使最后分类结果正确性高?
A:特征的选取在决策树运行都是能运行的,合理的选取需要考虑实际问题,在算法中主要考虑数据量的大小以及数据是否齐全。在算法构建时要考虑数据的特殊性,把可能性的结果都构想进去,不然算法会报错无法运行。
关于问题二:不同特征在不同的分叉点的位置的影响又大多?如果有影响,怎样的放置能使最后分类的结果正确性高?
A:不同特征在不同的分叉点的位置有较大影响,在上面步骤中的第⑥歩会计算每个特征值单独的香农熵,以此选取出最好的数据集划分方式,也就是决策时的考虑特征的顺序。
关于问题三:决策树算法的核心是什么?
A:ID3算法。也就是计算香农熵以及信息增益。
ID3算法
基本原理及概念:
ID3算法是一种贪心算法,用来构造决策树。就如上面解决问题的想法一样,通过计算每个属性的信息增益(信息增益反映的给定一个条件以后,不确定性减少的程度。),认为信息增益高的是好属性,每次划分选取信息增益最高的属性为划分标准,重复这个过程,直至生成一个能完美分类训练样例的决策树。