决策树
概述
决策树可以根据对输入特征属性的层层筛选选定数据所属的类。决策树能直观的展示数据的结构层次,利于理解。“…决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,在这些机器根据数据创建规则时,就是机器学习的过程。”
“优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值型和标称型。”
而在构造决策树过程中重要的一点便是寻找在划分过程中起决定性作用的特征。决定性的特征可以根据通过比较计算划分前后最好的信息增益寻找出来。
香农熵的计算公式:
香农熵是对信息不确定度的描述,划分前后熵的差值便是信息增益。
代码笔记
具体计算香农熵的代码:
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) #log base 2
return shannonEnt
按照给定特征划分数据集的函数,参数dataSet为待划分的数据集、axis划分数据集的特征、value需要返回的特征的值(属性)。
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
#用了巧妙的切片取出除了axis以外的元素
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
*python中列表操作append()与extend()区别:
“这两个方法功能类似,但是在处理多个列表时,这两个方法的处理结果是完全不同的。
假定存在两个列表,a和b:
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a.append(b)
>>> a
[1, 2, 3, [4, 5, 6]]
如果执行a.append(b),则列表得到了第四个元素,而且第四个元素也是一个列表。然而如果使用extend方法:
>>> a=[1,2,3]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6]
则得到一个包含a和b所有元素的列表。”
选择最好的数据集划分方法,用于选取二叉树的分支节点。
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 #集合特征数量
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0; bestFeature = -1
#对每种特征创建唯一的属性标签,此处featList的创建方法也是6…
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
利用递归的方式创建树的函数,使用python的字典存储树,获取最佳划分的特征后对其每一个属性进行分叉。函数包含两个停止条件:1.划分后的类标签一致的时候,2.当遍历完所有特征后。
def createTree(dataSet,labels):
classList = [example[-1] for example in dataSet]
#list.count(obj)用于返回列表中obj对象的个数
if classList.count(classList[0]) == len(classList):
return classList[0]
if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet
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:
#用[:]复制标签列表,为了保证每次调用函数createTree()时不改变原始列表的内容使用新变量subLabels代替原始列表
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
return myTree
使用决策树进行分类的函数
def classify(inputTree,featLabels,testVec):
firstStr = inputTree.keys()[0] #dict.keys()返回字典的键值列表
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr) #list.index(obj)返回obj的索引
key = testVec[featIndex] #获取当前指定特征的属性值
valueOfFeat = secondDict[key]
if isinstance(valueOfFeat, dict):
classLabel = classify(valueOfFeat, featLabels, testVec)
else: classLabel = valueOfFeat
return classLabel
决策树的储存与读取,使用pickle模块的.dump()与.load()方法完成。
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)
本章所用的决策树算法称为ID3,“ID3算法无法直接处理数值型数据,尽管我们可以通过量化的方法将数值型数据转化为标称型数值,但是如果存在太多的特征划分,ID3算法仍然会面临其他问题。”其他的决策树构造算法还有诸如C4.5,CART等。