决策树学习是应用最广泛的归纳推理算法之一,是一种逼近离散值目标函数的方法,在这种方法中学习到的函数被表示为一棵决策树。决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,机器学习算法最终将使用这些从数据集中创造的规则。决策树的优点为:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。缺点为:可能产生过度匹配的问题。决策树适于处理离散型和连续型的数据。
决策树通过把实例从根节点排列到某个叶子节点来分类实例,叶子节点即为实例所属的分类。树上的每个节点指定了对实例的某个属性(特征)的测试,并且该节点的每个后继分支对应该属性的一个可能值。分类实例的方法是从这棵树的根节点开始,测试这个节点指定的属性,然后按照给定实例的该属性值对应的数值向下移动。然后这个过程在一以新节点为跟的子树上重复。
本次学习将基于ID3算法构造决策树。ID3算法的核心问题是选取在树的每个节点要测试的特征或者属性,希望选择的是最有助于分类实例的属性。如何定量地衡量一个属性的价值呢?这里需要引入熵和信息增益的概念。熵是信息论中广泛使用的一个度量标准,刻画了任意样本集的纯度。熵定义为信息的期望值,那什么是信息呢?如果待分类的事物可能划分在多个分类中,则符号的信息定义为
为了计算熵,需要计算所有类别所有可能值包含的信息期望值,公式如下:
其中n是分类的数目。
如何在实践中应用上面的公式呢?假设有10个训练样本,其中6个的分类标签为yes,4个的分类标签为no,那熵是多少呢?在该例子中,分类的数目为2(yes,no),yes的概率为0.6,no的概率为0.4,则熵为 :
接下来定义属性分类训练数据的能力的度量标准,即信息增益。简单地说,一个属性的信息增益就是由于使用这个属性分割样本而导致的期望熵降低。更精确地将,一个属性或者特征A相对训练样本集S的信息增益Gain(S,A)被定义为:
其中value(A)是属性A所有可能值的集合,是S中属性A的值为v的子集,即。上述公式的第一项为原集合S的熵,第二项是用A分类S后熵的期望值,该项描述的期望熵就是每个子集的熵的加权和,权值为属于的样本占原始样本S的比例。所以Gain(S, A)是由于知道属性A的值而导致的期望熵减少。下面根据一个例子来演示如何计算信息增益Gain(S, A)。
假设S是一套有关天气的训练样例,特征为wind,其值为weak和strong,设S包含14个样例,其中9个为正样例,5个为反样例。又6个正样例和2个反样例有wind=weak,其它的为wind=strong,则按照上述公式的有:
当有多个特征或者属性时,分别计算各个属性的信息增益,选择信息增益最大的属性为最佳属性。
下面是计算熵的源代码:
from math import log
def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabe not in labelCounts.keys():
labelCounts[current] = 0
labelCounts[current] += 1
shannonEnt = 0.0
for key in labelCounts:
pro = float(labelCounts[key])/numEntries
shannonEnt -= pro * log(pro,2)
return shannoEnt
在计算信息增益时,添加了辅助函数splitDataSet:
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reduceFeatVec = featVec[:axis]
reduceFeatVec.extend(featVec[axis+1:])
retDataSet.append(reduceFeatVec)
return retDataSet
def chooseBestFeatToSplit(dataSet):
numFeats = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeat = -1
dataSetLength = len(dataSet)
for i in range(numFeats):
featList = [exampel[i] for example in dataSet]
uniqueValue = set(featList)
newEntropy = 0.0
for value in uniqueValue:
subDataSet = splitDataSet(dataSet, i, value)
pro = len(sunDataSet)/float(dataSetLength)
newEntropy += pro * calcShannonEnt(subDataSet)
infoGain = baseEntropy - newEntropy
if(infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeat = i
return bestFeat
根据前面对熵和信息增益的描述,相信在阅读源代码时就会比较容易了,不再进行详细赘述,直接看看实际运行效果。先是熵的计算:
>>> myData,labels = createDataSet()
>>> myData
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> labels
['no surfacing', 'flippers']
>>> trees.calcShannonEnt(myData)
0.9709505944546686
然后为myData增加更多的分类,看看熵是如何变化的:
>>>myData[0][-1]='maybe'
>>>myData
[[1, 1,'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>>trees.calcShannonEnt(myData)
1.3709505944546687
这说明混合的数据越多,即数据集中的分类数量越多,熵越高。接下来看看如何选择特征使信息增益最大:
>>> myData,labels = createDataSet()
>>> myData
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
>>> trees.calcShannonEnt(myData)
0.9709505944546686
>>> trees.chooseBestFeatToSplit(myData)
0
结果输出表明第0个特征,即nosurfacing,是最好的用于划分数据集的特征。结果是否正确呢?使用特征no surfacing划分数据时,该特征为1的为一组,为0的则为另一组,为1的一组由两类组成(两个yes,一个no),为0的仅有一类(no)。如果按照第一个特征,即flippers,划分的话,为1的一组将由4个样本,其中两个为yes,两个为no,为0的一组仅有一类(no)。这些表明按照第0个特征划分数据集效果是最好的。
《机器学习实战》这本书接下来的内容是如何构造决策树,基本上考验的是一个人对python及其工具的熟悉程度和编码能力,将不再逐一进行学习笔记的记录,直接跳到使用决策树预测隐形眼镜类型的实验。实验的输出结果为:
>>> fr=open('lenses.txt')
>>> lenses=[inst.strip().split('\t') for inst in fr.readlines()]
>>> lensesLabels=['age','prescript','astigmatic','tearRate']
>>> lensesTree=trees.createTree(lenses,lensesLabels)
>>> lensesTree
{'tearRate': {'reduced': 'no lenses', 'normal': {'astigmatic': {'yes': {'prescript': {'hyper': {'age': {'pre': 'no lenses', 'presbyopic': 'no lenses', 'young': 'hard'}}, 'myope': 'hard'}}, 'no': {'age': {'pre': 'soft', 'presbyopic': {'prescript': {'hyper': 'soft', 'myope': 'no lenses'}}, 'young': 'soft'}}}}}}
>>> treePlotter.createPlot(lensesTree)
执行最后一条语句后输出如下的图片:
决策树分类器就像带有终止块的流程图,终止块表示分类结果。开始处理数据集时,首先需要测量集合中数据的不一致性,也就是熵,然后寻找最优方案划分数据集,直到数据集中的所有数据属于统一分类。本篇主要学习了ID3算法构造决策树,该算法用于划分离散数据集,也会产生过拟合的问题,为了减少过拟合的问题,可以裁剪决策树,后面会学习如何实现。