一:ID3算法
简单回顾上一节我们使用ID3算法:
ID3算法的核心是在决策树各个结点上对应信息增益准则选择特征,递归地构建决策树。
具体方法是:
- 从根结点开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征,由该特征的不同取值建立子节点;
- 再对子结点递归地调用以上方法,构建决策树;
- 直到所有特征的信息增益均很小或没有特征可以选择为止,最后得到一个决策树。
ID3相当于用极大似然法进行概率模型的选择。
二:直观感受一下我们的树
上一篇中我们构建了决策树,但是能否用代码清楚的表示出整个树结构呢?
例如使用字典格式保存整个树结构:
{'纹理': {0: {'色泽': {0: 'no', 1: {'敲声': {1: 'yes', 2: 'no'}}, 2: 'no'}}, 1: {'脐部': {0: 'yes', 1: {'根蒂': {0: 'yes', 1: 'yes'}}, 2: 'no'}}}}
接下来我们为了直观的看到我们创建的树是什么样子,
- 创建函数majorityCnt统计classList中出现此处最多的元素(类标签),
- 创建函数createTree用来递归构建决策树。
- 编写代码如下:
from math import log
import operator
#训练数据集
def createDataSet():
dataSet = [[0,0,1,1,0,0,'yes'],
[1,0,2,1,0,0,'yes'],
[1,0,1,1,0,0,'yes'],
[0,0,2,1,0,0,'yes'],
[2,0,1,1,0,0,'yes'],
[0,1,1,1,1,1,'yes'],
[1,1,1,0,1,1,'yes'],
[1,1,1,1,1,0,'yes'],
[1,1,2,0,1,0,'no'],
[0,2,0,1,2,1,'no'],
[2,2,0,0,2,0,'no'],
[2,0,1,0,2,1,'no'],
[0,1,1,0,0,0,'no'],
[2,1,2,0,0,0,'no'],
[1,1,1,1,1,1,'no'],
[2,0,1,0,2,0,'no'],
[0,0,2,0,1,0,'no']]
labels = ['色泽','根蒂','敲声','纹理','脐部','触感','好坏']#分类属性
return dataSet, labels #返回数据集和分类属性
#计算信息熵
def calMessageEnt(dataSet):
numEntires = len(dataSet) #返回数据集的行数
labelCounts = {} #保存每个标签出现次数的次数
#对每组特征向量就进行统计
for featVec in dataSet:
currentLabel = featVec[-1] #提取标签的信息
#如果标签不在统计里面
if (currentLabel not in labelCounts.keys()):
labelCounts[currentLabel] = 0
#如果在,计数
labelCounts[currentLabel]+=1
#信息熵
MessageEnt = 0.0
#计算信息熵
for key in labelCounts:
#选择该标签(Label)的概率
prob = float(labelCounts[key])/numEntires
#利用公式计算
MessageEnt -= prob*log(prob,2)
#返回信息熵
return MessageEnt
#按照给定特征划分 数据集
# 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
#选择最优特征
# dataSet - 数据集
# bestFeature - 信息增益最大的(最优)特征的索引值
# bestInFoGain 信息增益
def chooseBestFeatureToSplit(dataSet):
#特征的数量
numFeatures = len(dataSet[0])-1
#计算数据集的信息熵
baseEntropy = calMessageEnt(dataSet)
#信息增益
bestInfoGain =0.0
#最优特 征索引值
bestFeature = -1
#遍历所有特征
for i in range(numFeatures):
#获取dataSet的第i个所有特征
featList = [example[i] for example in dataSet]
#创建SET集合,元素不可重复
uniqueVals =set(featList)
newEntropy = 0.0 #经验条件熵
#计算信息增益
for value in uniqueVals:
#划分子集
subDataSet = splitDataSet(dataSet,i,value)
#计算子集的概率
prob = len(subDataSet)/float(len(dataSet))
#根据公式计算经验条件熵
newEntropy += prob*calMessageEnt(subDataSet)
#信息增益
infoGain = baseEntropy - newEntropy
#计算最优特征的信息增益
if (infoGain > bestInfoGain):
#更新信息增益,找到最大的信息增益
bestInfoGain = infoGain
#记录信息增益最大的特征的索引值
bestFeature = i
#返回信息增益最大的特征的索引值
return bestFeature
#统计classList中出现此处最多的元素(类标签)
def majorityCnt(classList):
classCount = {}
for vote in classList:
#统计classList中每个元素出现的次数
if vote not in classCount.keys():classCount[vote] = 0
classCount[vote] += 1
#根据字典的值降序排序
sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)
#返回classList中出现次数最多的元素
return sortedClassCount[0][0]
#创建树
def createTree(dataSet, labels, featLabels):
#取分类标签(好瓜与否:yes or no)
classList = [example[-1] for example in dataSet]
#如果类别完全相同则停止继续划分
if classList.count(classList[0]) == len(classList):
return classList[0]
#遍历完所有特征时返回出现次数最多的类标签
if len(dataSet[0]) == 1:
return