一 开发环境:win7 64 位 + Pycharm5.0 + python3.4.4
二 工具包:numpy + matplotlib (对于microsoft visual c++ 10.0 is required错误,我是通过下载microsoft visual c++ 10.0解决的)
三 参考书籍:机器学习实战
决策树:
优点:计算复杂度不高,输出易于理解,对中间值缺失不明感,可以处理不相关的特征数据
缺点:可能会出现过渡匹配问题
适用数据类型:数值型和标称型。
一般流程:
1)数据收集:任意方法;
2)准备数据:对于数值型数据需要进行离散化;
3)分析数据:任意方法,构造树完成后通过检查树形图判断是否符合预期;
4)训练算法:构造树的数据结构;
5)测试算法:使用经验树计算错误率;
6)使用方法:决策树的特点是可以更好的反映数据的内在含义;
四 程序清单:
1.划分的依据:
划分数据集就是要将无序的数据变得更加有序。数据无序的度量值为:熵,通过计算划分前后信息发生的变化决定最佳的划分方式。
# 计算给定数据集的香农熵 def calcShannonEnt(dataSet): numEntries = len(dataSet) # 数据集总数 labelCounts = {} # 最后一列数据的字典表 for featVec in dataSet: currentLabel = featVec[-1] # 获取数据最后一列值 if currentLabel not in labelCounts.keys(): # 不存在在字典表中设为0 labelCounts[currentLabel] = 0 labelCounts[currentLabel] += 1 # 存在+1 shannonEnt = 0.0 # 返回的信息熵 for key in labelCounts: # 计算数据集的信息熵 prob = float(labelCounts[key])/numEntries shannonEnt -= prob * log(prob, 2) return shannonEnt
# 按照给定特征划分数据集 # dataSet:带划分数据集;axis:期望划分的数据集特征(下标);value:返回数据集要求retDataSet[][axis] = value def splitDataSet(dataSet, axis, value): retDataSet = [] # 创建返回数据集 for featVec in dataSet: if featVec[axis] == value: reduceFeatVec = featVec[:axis] # 截取axis之前的数据 reduceFeatVec.extend(featVec[axis+1:]) # 截取axis之后的数据 retDataSet.append(reduceFeatVec) return retDataSet
# 选择最好的数据集划分方式 def chooseBestFeatureSplit(dataSet): numFeatures = len(dataSet[0]) - 1 # 获取每个实例长度减一,最后一个值为类别标签不是可划分特征 baseEntropy = calcShannonEnt(dataSet) # 获取初始信息熵 bestInfoGain = 0.0 bestFeature = -1 for i in range(numFeatures): # 从0到numFeatures依次验证划分结果 featList = [example[i] for example in dataSet] # example[i]表示从example列表中选取下标为i的值,生成下标为i时所有存在值组成的列表 uniqueVals = set(featList) newEntropy = 0.0 for value in uniqueVals: # 叠加特征属性i所有取值的信息熵 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 bestFeature2.通过递归的方式生成构造树:
问题一:若对于所有特征进行分类后仍无法分类?
采用多数表决的方法:
# 对于无法分类的数据通过多数原则获取类标签 def majorityCnt(classList): classCount = {} # 创建类标签统计字典 for vote in classList: # 遍历classList生成字典表 if vote not in classCount.keys(): classCount[vote] = 0 else: classCount[vote] += 1 # 字典表根据标签出现的次数排序排序 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) # 返回标签次数出现最多的标签 return sortedClassCount[0][0]问题二:如何存储构造树?
通过python的字典进行存储,当然不是一个普通的字典而是一个嵌套字典。
# 创建分类树 def createTree(dataSet, labels): classList = [example[-1] for example in dataSet] # 获得dataSet类别标签列表 if classList.count(classList[0]) == len(classList): # dataSet类表标签一致,返回类别 return classList[0] if len(dataSet[0]) == 1: # dataSet划分到仅剩类别标签(遍历完所有特征),返回出现最多的类别 return majorityCnt(classList) # 获取dataSet最佳划分方式 beatFeat = chooseBestFeatureSplit(dataSet) # 获取最佳划分特征 beastFeatLable = labels[beatFeat] # 生成myTree,myTree是一个嵌套字典表,key为特征,value为字典 myTree = {beastFeatLable: {}} del(labels[beatFeat]) # 获得beatFeat特征所有可能值,即该节点的分裂值 featValues = [example[beatFeat] for example in dataSet] uniqueVals = set(featValues) for value in uniqueVals: # python中参数均为传引用,若不进行复制,循环第二次开始labels值已改变 subLabels = labels[:] # 递归的创建myTree myTree[beastFeatLable][value] = createTree(splitDataSet(dataSet, beatFeat, value), subLabels) return myTree需要注意的是python函数参数使用的是传引用,所以在递归时不能直接将labels传人而是传人一个labels的副本。
递归结束条件为:
1)分类数据类别一致;
2)已遍历完所有特征;
3.使用决策树进行分类:
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
4.决策树的序列化和反序列化:
# 存储模块-序列化:将数据结构或对象转换成二进制串的过程 def storeTree(inputTree, filename): fw = open(filename, 'wb') pickle.dump(inputTree, fw) fw.close() # 读取模块-反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程 def grabTree(filename): fr = open(filename, 'rb') return pickle.load(fr)对于序列化和反序列化的介绍:http://tech.meituan.com/serialization_vs_deserialization.html
五 数据集和源码:
http://pan.baidu.com/s/1kU3l1C7