郑捷《机器学习算法原理与编程实践》学习笔记(第三章 决策树的发展)(一 )_ID3
3.1 决策树的基本思想
3.1.1 从一个实例开始(略)
3.1.2 决策树的算法框架(略)
3.1.3 信息熵测度(略)
3.2 ID3决策树
3.2.1 ID3算法(略)
3.2.2 ID3的实现(Python实现)
定义一个ID3DTree的类来封装算法:
#coding:utf-8 from numpy import * import math import copy import cPickle as pickle # 定义一个ID3DTree的类来封装算法: class ID3DTree(object): def __init__(self): #构造方法 self.tree = {} #生成的树 self.dataSet = [] #数据集 self.label = [] #标签集
(1) 数据导入函数
#数据导入函数 def loadDataSet(self,path,labels): recordlist = [] fp = open(path,"rb") content = fp.read() fp.close() rowlist = content.splitlines() #按行转换为一维表 recordlist = [row.split("\t") for row in rowlist if row.strip()] self.dataSet = recordlist self.labels = labels
(2)执行决策树函数
#执行决策树函数 def train(self): labels = copy.deepcopy(self.labels) self.tree = self.buildTree(self.dataSet,labels)
3.2.3 决策树主方法
(1)构建决策树:创建决策树主程序
# 3.2.3 决策树主方法 # (1)构建决策树:创建决策树主程序 def buildTree(self,dataSet,labels): cateList = [data[-1] for data in dataSet] #抽取源数据集的决策标签列 #程序的终止条件1:如果classList只有一种决策标签,停止划分,返回这个决策标签 if cateList.count(cateList[0]) == len(cateList): return cateList[0] #程序的终止条件2:如果数据集的第一个决策标签只有一个,则返回这个决策标签 if len(dataSet[0]) == 1: return self.maxCate(cateList) #算法核心: bestFeat = self.getBestFeat(dataSet) #返回数据集的最优特征轴 bestFeatLabel = labels[bestFeat] tree = {bestFeatLabel:{}} del(labels[bestFeat]) #抽取最优特征轴的列向量 uniqueVals = set([data[bestFeat] for data in dataSet]) #去重 for value in uniqueVals: #决策树递归生长 subLabels = labels[:] #将删除后的特征类别接建立子类别集 #按最优特征列和值分割数据集 splitDataset = self.splitDataSet(dataSet,bestFeat,value) subTree = self.buildTree(splitDataset,subLabels) tree[bestFeatLabel][value] = subTree return tree
(2)计算出现次数最多的类别标签
#计算出现次数最多的类别标签 def maxCate(self,catelist): items = dict([(catelist.count(i),i) for i in catelist]) return items([max(items.keys())])
(3)计算最优特征
#计算最优特征 def getBestFeat(self,dataSet): #计算特征向量维,其中最后一列用于类别标签,因此要减去 numFeatures = len(dataSet[0])-1 #特征向量维数=行向量维数-1 baseEntropy = self.computeEntropy(dataSet) #基础熵:源数据香农熵 bestInfoFain = 0.0 #初始化最优的信息增益 bestFeature = -1 #初始化最优特征轴 #外循环:遍历数据集各列,计算最优特征轴 # i 为数据集列索引:取值范围0~(numFeatures-1) for i in xrange(numFeatures): #抽取第i列的列向量 uniqueVals = set([data[i] for data in dataSet]) #去重:该列唯一值集 newEntropy = 0.0 #初始化该列的香农熵 for value in uniqueVals: #内循环:按列和唯一值计算香农熵 #按选定列i和唯一值分割数据集 subDataSet = self.splitDataSet(dataSet,i,value) prob = len(subDataSet)/float(len(dataSet)) newEntropy += prob*self.computeEntropy(subDataSet) infoGain = baseEntropy -newEntropy #计算最大增益 if(infoGain > bestInfoFain): #如果信息增益>0 bestInfoFain = infoGain #用当前的信息增益替代之前的最优增益值 bestFeature = i #重置最优特征为当前列 return bestFeature
(4)计算信息熵
#计算信息熵 def computeEntropy(self,dataSet): #计算香农熵 datalen = float(len(dataSet)) cateList = [data[-1] for data in dataSet] #从数据集中得到类别标签 #得到类别为key,出现次数value的字典 items = dict([(i,cateList.count(i)) for i in cateList]) infoEntropy = 0.0 for key in items: #香农熵:=-p*log2(p) --infoEntropy = -prob*log(prob,2) prob = float(items[key])/datalen infoEntropy -= prob*math.log(prob,2) return infoEntropy
(5)划分数据集:分割数据集;删除特征轴所在的数据列,返回剩余的数据集
#(5)划分数据集:分割数据集;删除特征轴所在的数据列,返回剩余的数据集 def splitDataSet(self,dataSet,axis,value): rtnList = [] for featVec in dataSet: if featVec[axis] == value: rFeatVec = featVec[:axis] #list操作:提取0~(axis-1)的元素 rFeatVec.extend(featVec[axis+1:])#lsit操作:将特征轴(列)之后的元素加回
rtnList.append(rFeatVec)
return rtnList
3.2.4 训练决策树
代码如下:
#coding:utf-8 from numpy import * from ID3DTree import * dtree = ID3DTree() ''' Sepal.Length Sepal.Width Petal.Length Petal.Width ''' dtree.loadDataSet('iris.txt',['Sepal.Length','Sepal.Width','Petal.Length','Petal.Width']) dtree.train() print dtree.tree
3.2.5 持久化决策树
ID3类也提供了专门的方法用于保存决策树到文件,并可从文件读取决策树到内存
def storeTree(self,inputTree,filename): #存储树到文件 fw = open(filename,'w') pickle.dump(inputTree,fw) fw.close() def grabTree(self,filename): #从文件抓取树 fr = open(filename) return pickle.load(fr)
执行代码:
dtree.storeTree(dtree.tree,"data.tree") mytree = dtree.grabTree("data.tree") print mytree
3.2.6 决策树的分类
def predict(self,inputTree,featLabels,testVec): #分类器 root = inputTree.keys()[0] #树根节点 seconDict = inputTree[root] #value-子树结构或分类标签 featIndex = featLabels.index(root) #根节点在分类标签中的位置 key = testVec[featIndex] valueOfFeat = seconDict[key] if isinstance(valueOfFeat,dict): #判断是否仍然是字典的类型 classLabel = self.predict(valueOfFeat,featLabels,testVec) #递归分类 else: classLabel = valueOfFeat return classLabel
测试代码:
dtree = ID3DTree() labels = ['Sepal.Length','Sepal.Width','Petal.Length','Petal.Width'] vector = ['5.1000000e+00', '3.5000000e+00' ,'1.4000000e+00','2.0000000e-01'] mytree = dtree.grabTree('data.tree') print u"真实输出",u"no",u"->",u"决策树输出",dtree.predict(mytree,labels,vector)
输出结果:
真实输出 no -> 决策树输出 0.0000000e+00