本博客记录《机器学习实战》(MachineLearningInAction)的学习过程,包括算法介绍和python实现。
决策树
由一个数据集构造决策树的关键是对数据集的拆分,ID3算法的数据划分是依据信息增益的,简单地说就是选择一种能够使划分过后的信息增益最大的方式划分数据集。例如,一组数据的特征有两个,一是不浮出水面能否生存(no surfacing),二是是否有脚蹼(flippers),根据这两个特征判断该数据代表的物种是否属于鱼类,构造决策树如下。
构造出决策树之后,对于新数据,只需要输入这两个特征的值并从根节点开始向下查找,到达叶结点时就完成了分类。
信息增益
划分数据的意义在于,把无序的数据变得更加有序,即增加了数据的信息量。数据集中信息的度量方式称为香农熵(entropy),熵代表了数据的混乱程度,用划分前的熵减去划分后的熵,就得到了熵的减少量,也就是信息的增益量。熵的计算公式如下:
其中n是分类的数目,p(x)是选择该分类的概率。
# 计算给定数据集的香农熵
def calShannonEnt(dataSet):
numEntries = len(dataSet)
labelCount = {}
# 计算每个分类出现的次数
for featVec in dataSet:
curLabel = featVec[-1]
if curLabel not in labelCount.keys():
labelCount[curLabel] = 0
labelCount[curLabel] += 1
shannonEnt = 0.0
for key in labelCount:
# 选择该分类的概率
prob = float (labelCount[key] / numEntries)
shannonEnt -= prob * log(prob, 2)
return shannonEnt
选择数据集划分方式
对于给定的数据集,遍历其中每一个特征,计算出以该特征划分数据集的信息增益,选择信息增益最大的那个特征作为划分特征。
# 选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1
baseEnt = calShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
# 遍历每个特征
for i in range(numFeatures):
featList = [feat[i] for feat in dataSet]
uniqueVals = set(featList)
newEnt = 0.0
# 遍历特征中的每一个分类,计算总熵
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet) / float(len(dataSet))
newEnt += prob * calShannonEnt(subDataSet)
# 计算信息增益
infoGain = baseEnt - newEnt
if infoGain > bestInfoGain:
bestInfoGain = infoGain
bestFeature = i
return bestFeature
构建决策树(ID3算法)
使用递归的方式构建决策树,在原始数据集中选择最佳特征划分数据集(产生决策树分支),并对划分出的数据集使用同样的方法划分。当某个分支下的数据都属于同一分类,或遍历完所有特征时,递归结束(产生叶子结点)。
# 构建决策树
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 majCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet)
bestLabel = labels[bestFeat]
myTree = {bestLabel: {}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueValues = set(featValues)
# 遍历每个分支,生成子树
for value in uniqueValues:
subLabels = labels[:]
myTree[bestLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
return myTree
使用决策树进行分类
根据输入的决策树,特征标签列表,以及跟这些标签对应的特征值列表(输入数据),来对输入数据进行分类。例如,特征标签列表为[‘no surfacing’, ‘flippers’],此时输入数据[0, 1]就代表不浮出水面无法生存,以及有脚蹼。
# 使用决策树进行分类
def classify(inputTree, featLabels, testVec):
# 获取当前特征
firstStr = list(inputTree.keys())[0]
# 获取每个分支的子树
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr)
# 遍历每个分支,选择与输入数据特征相同的分支往下走
for key in secondDict.keys():
if testVec[featIndex] == key:
# 若是子树则递归继续查找,若是叶子结点则返回分类结果
if type(secondDict[key]).__name__ == 'dict':
classLabel = classify(secondDict[key], featLabels, testVec)
else:
classLabel = secondDict[key]
return classLabel
使用决策树进行分类的优点是计算复杂度不高,并且能够很好地给出数据的内在含义,缺点是容易产生过度匹配的问题。在python中,构造的决策树可以使用pickle来保存到文件中,并且可以使用matplotlib中的pyplot.figure进行绘制,上文中的决策树图就是这样画出来的。