决策树之系列二—C4.5原理与代码实现

本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/9435712.html

ID3算法缺点

它一般会优先选择有较多属性值的Feature,因为属性值多的特征会有相对较大的信息增益,信息增益反映的是,在给定一个条件以后,不确定性减少的程度,

这必然是分得越细的数据集确定性更高,也就是条件熵越小,信息增益越大。为了解决这个问题,C4.5就应运而生,它采用信息增益率来作为选择分支的准则。

C4.5算法原理

信息增益率定义为:

其中,分子为信息增益(信息增益计算可参考上一节ID3的算法原理),分母为属性X的熵。

需要注意的是,增益率准则对可取值数目较少的属性有所偏好。

所以一般这样选取划分属性:选择增益率最高的特征列作为划分属性的依据。

代码实现

与ID3代码实现不同的是:只改变计算香农熵的函数calcShannonEnt,以及选择最优特征索引函数chooseBestFeatureToSplit,具体代码如下:

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Thu Aug 2 17:09:34 2018
  4. 决策树ID3,C4.5的实现
  5. @author: weixw
  6. """
  7. from math import log
  8. import operator
  9. #原始数据
  10. def createDataSet():
  11. dataSet = [[1, 1, 'yes'],
  12. [1, 1, 'yes'],
  13. [1, 0, 'no'],
  14. [0, 1, 'no'],
  15. [0, 1, 'no']]
  16. labels = ['no surfacing','flippers']
  17. return dataSet, labels
  18.  
  19. #多数表决器
  20. #列中相同值数量最多为结果
  21. def majorityCnt(classList):
  22. classCounts = {}
  23. for value in classList:
  24. if(value not in classCounts.keys()):
  25. classCounts[value] = 0
  26. classCounts[value] +=1
  27. sortedClassCount = sorted(classCounts.iteritems(),key = operator.itemgetter(1),reverse =True)
  28. return sortedClassCount[0][0]
  29.  
  30. #划分数据集
  31. #dataSet:原始数据集
  32. #axis:进行分割的指定列索引
  33. #value:指定列中的值
  34. def splitDataSet(dataSet,axis,value):
  35. retDataSet= []
  36. for featDataVal in dataSet:
  37. if featDataVal[axis] == value:
  38. #下面两行去除某一项指定列的值,很巧妙有没有
  39. reducedFeatVal = featDataVal[:axis]
  40. reducedFeatVal.extend(featDataVal[axis+1:])
  41. retDataSet.append(reducedFeatVal)
  42. return retDataSet
  43.  
  44. #计算香农熵
  45. #columnIndex = -1表示获取数据集每一项的最后一列的标签值
  46. #其他表示获取特征列
  47. def calcShannonEnt(columnIndex, dataSet):
  48. #数据集总项数
  49. numEntries = len(dataSet)
  50. #标签计数对象初始化
  51. labelCounts = {}
  52. for featDataVal in dataSet:
  53. #获取数据集每一项的最后一列的标签值
  54. currentLabel = featDataVal[columnIndex]
  55. #如果当前标签不在标签存储对象里,则初始化,然后计数
  56. if currentLabel not in labelCounts.keys():
  57. labelCounts[currentLabel] = 0
  58. labelCounts[currentLabel] += 1
  59. #熵初始化
  60. shannonEnt = 0.0
  61. #遍历标签对象,求概率,计算熵
  62. for key in labelCounts.keys():
  63. prop = labelCounts[key]/float(numEntries)
  64. shannonEnt -= prop*log(prop,2)
  65. return shannonEnt
  66.  
  67. #通过信息增益,选出最优特征列索引(ID3)
  68. def chooseBestFeatureToSplit(dataSet):
  69. #计算特征个数,dataSet最后一列是标签属性,不是特征量
  70. numFeatures = len(dataSet[0])-1
  71. #计算初始数据香农熵
  72. baseEntropy = calcShannonEnt(-1, dataSet)
  73. #初始化信息增益,最优划分特征列索引
  74. bestInfoGain = 0.0
  75. bestFeatureIndex = -1
  76. for i in range(numFeatures):
  77. #获取每一列数据
  78. featList = [example[i] for example in dataSet]
  79. #将每一列数据去重
  80. uniqueVals = set(featList)
  81. newEntropy = 0.0
  82. for value in uniqueVals:
  83. subDataSet = splitDataSet(dataSet,i,value)
  84. #计算条件概率
  85. prob = len(subDataSet)/float(len(dataSet))
  86. #计算条件熵
  87. newEntropy +=prob*calcShannonEnt(-1, subDataSet)
  88. #计算信息增益
  89. infoGain = baseEntropy - newEntropy
  90. if(infoGain > bestInfoGain):
  91. bestInfoGain = infoGain
  92. bestFeatureIndex = i
  93. return bestFeatureIndex
  94.  
  95. #通过信息增益率,选出最优特征列索引(C4.5)
  96. def chooseBestFeatureToSplitOfFurther(dataSet):
  97. #计算特征个数,dataSet最后一列是标签属性,不是特征量
  98. numFeatures = len(dataSet[0])-1
  99. #计算初始数据香农熵H(Y)
  100. baseEntropy = calcShannonEnt(-1, dataSet)
  101. #初始化信息增益,最优划分特征列索引
  102. bestInfoGainRatio = 0.0
  103. bestFeatureIndex = -1
  104. for i in range(numFeatures):
  105. #获取每一特征列香农熵H(X)
  106. featEntropy = calcShannonEnt(i, dataSet)
  107. #获取每一列数据
  108. featList = [example[i] for example in dataSet]
  109. #将每一列数据去重
  110. uniqueVals = set(featList)
  111. newEntropy = 0.0
  112. for value in uniqueVals:
  113. subDataSet = splitDataSet(dataSet,i,value)
  114. #计算条件概率
  115. prob = len(subDataSet)/float(len(dataSet))
  116. #计算条件熵
  117. newEntropy +=prob*calcShannonEnt(-1, subDataSet)
  118. #计算信息增益
  119. infoGain = baseEntropy - newEntropy
  120. #计算信息增益率
  121. infoGainRatio = infoGain/float(featEntropy)
  122. if(infoGainRatio > bestInfoGainRatio):
  123. bestInfoGainRatio = infoGainRatio
  124. bestFeatureIndex = i
  125. return bestFeatureIndex
  126.  
  127. #决策树创建
  128. def createTree(dataSet,labels):
  129. #获取标签属性,dataSet最后一列,区别于labels标签名称
  130. classList = [example[-1] for example in dataSet]
  131. #树极端终止条件判断
  132. #标签属性值全部相同,返回标签属性第一项值
  133. if classList.count(classList[0]) == len(classList):
  134. return classList[0]
  135. #没有特征,只有标签列(1列)
  136. if len(dataSet[0]) == 1:
  137. #返回实例数最大的类
  138. return majorityCnt(classList)
  139. # #获取最优特征列索引ID3
  140. # bestFeatureIndex = chooseBestFeatureToSplit(dataSet)
  141. #获取最优特征列索引C4.5
  142. bestFeatureIndex = chooseBestFeatureToSplitOfFurther(dataSet)
  143. #获取最优索引对应的标签名称
  144. bestFeatureLabel = labels[bestFeatureIndex]
  145. #创建根节点
  146. myTree = {bestFeatureLabel:{}}
  147. #去除最优索引对应的标签名,使labels标签能正确遍历
  148. del(labels[bestFeatureIndex])
  149. #获取最优列
  150. bestFeature = [example[bestFeatureIndex] for example in dataSet]
  151. uniquesVals = set(bestFeature)
  152. for value in uniquesVals:
  153. #子标签名称集合
  154. subLabels = labels[:]
  155. #递归
  156. myTree[bestFeatureLabel][value] = createTree(splitDataSet(dataSet,bestFeatureIndex,value),subLabels)
  157. return myTree
  158.  
  159. #获取分类结果
  160. #inputTree:决策树字典
  161. #featLabels:标签列表
  162. #testVec:测试向量 例如:简单实例下某一路径 [1,1] => yes(树干值组合,从根结点到叶子节点)
  163. def classify(inputTree,featLabels,testVec):
  164. #获取根结点名称,将dict转化为list
  165. firstSide = list(inputTree.keys())
  166. #根结点名称String类型
  167. firstStr = firstSide[0]
  168. #获取根结点对应的子节点
  169. secondDict = inputTree[firstStr]
  170. #获取根结点名称在标签列表中对应的索引
  171. featIndex = featLabels.index(firstStr)
  172. #由索引获取向量表中的对应值
  173. key = testVec[featIndex]
  174. #获取树干向量后的对象
  175. valueOfFeat = secondDict[key]
  176. #判断是子结点还是叶子节点:子结点就回调分类函数,叶子结点就是分类结果
  177. #if type(valueOfFeat).__name__=='dict': 等价 if isinstance(valueOfFeat, dict):
  178. if isinstance(valueOfFeat, dict):
  179. classLabel = classify(valueOfFeat,featLabels,testVec)
  180. else:
  181. classLabel = valueOfFeat
  182. return classLabel
  183.  
  184. #将决策树分类器存储在磁盘中,filename一般保存为txt格式
  185. def storeTree(inputTree,filename):
  186. import pickle
  187. fw = open(filename,'wb+')
  188. pickle.dump(inputTree,fw)
  189. fw.close()
  190. #将瓷盘中的对象加载出来,这里的filename就是上面函数中的txt文件
  191. def grabTree(filename):
  192. import pickle
  193. fr = open(filename,'rb')
  194. return pickle.load(fr)

决策树算法

  1. '''
  2. Created on Oct 14, 2010
  3.  
  4. @author: Peter Harrington
  5. '''
  6. import matplotlib.pyplot as plt
  7.  
  8. decisionNode = dict(boxstyle="sawtooth", fc="0.8")
  9. leafNode = dict(boxstyle="round4", fc="0.8")
  10. arrow_args = dict(arrowstyle="<-")
  11.  
  12. #获取树的叶子节点
  13. def getNumLeafs(myTree):
  14. numLeafs = 0
  15. #dict转化为list
  16. firstSides = list(myTree.keys())
  17. firstStr = firstSides[0]
  18. secondDict = myTree[firstStr]
  19. for key in secondDict.keys():
  20. #判断是否是叶子节点(通过类型判断,子类不存在,则类型为str;子类存在,则为dict)
  21. if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodes
  22. numLeafs += getNumLeafs(secondDict[key])
  23. else: numLeafs +=1
  24. return numLeafs
  25.  
  26. #获取树的层数
  27. def getTreeDepth(myTree):
  28. maxDepth = 0
  29. #dict转化为list
  30. firstSides = list(myTree.keys())
  31. firstStr = firstSides[0]
  32. secondDict = myTree[firstStr]
  33. for key in secondDict.keys():
  34. if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodes
  35. thisDepth = 1 + getTreeDepth(secondDict[key])
  36. else: thisDepth = 1
  37. if thisDepth > maxDepth: maxDepth = thisDepth
  38. return maxDepth
  39.  
  40. def plotNode(nodeTxt, centerPt, parentPt, nodeType):
  41. createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',
  42. xytext=centerPt, textcoords='axes fraction',
  43. va="center", ha="center", bbox=nodeType, arrowprops=arrow_args )
  44.  
  45. def plotMidText(cntrPt, parentPt, txtString):
  46. xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0]
  47. yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]
  48. createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)
  49.  
  50. def plotTree(myTree, parentPt, nodeTxt):#if the first key tells you what feat was split on
  51. numLeafs = getNumLeafs(myTree) #this determines the x width of this tree
  52. depth = getTreeDepth(myTree)
  53. firstSides = list(myTree.keys())
  54. firstStr = firstSides[0] #the text label for this node should be this
  55. cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff)
  56. plotMidText(cntrPt, parentPt, nodeTxt)
  57. plotNode(firstStr, cntrPt, parentPt, decisionNode)
  58. secondDict = myTree[firstStr]
  59. plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD
  60. for key in secondDict.keys():
  61. if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodes
  62. plotTree(secondDict[key],cntrPt,str(key)) #recursion
  63. else: #it's a leaf node print the leaf node
  64. plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalW
  65. plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
  66. plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
  67. plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD
  68. #if you do get a dictonary you know it's a tree, and the first element will be another dict
  69. #绘制决策树
  70. def createPlot(inTree):
  71. fig = plt.figure(1, facecolor='white')
  72. fig.clf()
  73. axprops = dict(xticks=[], yticks=[])
  74. createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #no ticks
  75. #createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses
  76. plotTree.totalW = float(getNumLeafs(inTree))
  77. plotTree.totalD = float(getTreeDepth(inTree))
  78. plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0;
  79. plotTree(inTree, (0.5,1.0), '')
  80. plt.show()
  81.  
  82. #绘制树的根节点和叶子节点(根节点形状:长方形,叶子节点:椭圆形)
  83. #def createPlot():
  84. # fig = plt.figure(1, facecolor='white')
  85. # fig.clf()
  86. # createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses
  87. # plotNode('a decision node', (0.5, 0.1), (0.1, 0.5), decisionNode)
  88. # plotNode('a leaf node', (0.8, 0.1), (0.3, 0.8), leafNode)
  89. # plt.show()
  90.  
  91. def retrieveTree(i):
  92. listOfTrees =[{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}},
  93. {'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}}
  94. ]
  95. return listOfTrees[i]
  96.  
  97. #thisTree = retrieveTree(0)
  98. #createPlot(thisTree)
  99. #createPlot()
  100. #myTree = retrieveTree(0)
  101. #numLeafs =getNumLeafs(myTree)
  102. #treeDepth =getTreeDepth(myTree)
  103. #print(u"叶子节点数目:%d"% numLeafs)
  104. #print(u"树深度:%d"%treeDepth)

绘制决策树

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Fri Aug 3 19:52:10 2018
  4.  
  5. @author: weixw
  6. """
  7. import myTrees as mt
  8. import treePlotter as tp
  9. #测试
  10. dataSet, labels = mt.createDataSet()
  11. #copy函数:新开辟一块内存,然后将list的所有值复制到新开辟的内存中
  12. labels1 = labels.copy()
  13. #createTree函数中将labels1的值改变了,所以在分类测试时不能用labels1
  14. myTree = mt.createTree(dataSet,labels1)
  15. #保存树到本地
  16. mt.storeTree(myTree,'myTree.txt')
  17. #在本地磁盘获取树
  18. myTree = mt.grabTree('myTree.txt')
  19. print(u"采用C4.5算法的决策树结果")
  20. print (u"决策树结构:%s"%myTree)
  21. #绘制决策树
  22. print(u"绘制决策树:")
  23. tp.createPlot(myTree)
  24. numLeafs =tp.getNumLeafs(myTree)
  25. treeDepth =tp.getTreeDepth(myTree)
  26. print(u"叶子节点数目:%d"% numLeafs)
  27. print(u"树深度:%d"%treeDepth)
  28. #测试分类 简单样本数据3列
  29. labelResult =mt.classify(myTree,labels,[1,1])
  30. print(u"[1,1] 测试结果为:%s"%labelResult)
  31. labelResult =mt.classify(myTree,labels,[1,0])
  32. print(u"[1,0] 测试结果为:%s"%labelResult)

测试

运行结果

不要让懒惰占据你的大脑,不要让妥协拖垮你的人生。青春就是一张票,能不能赶上时代的快车,你的步伐掌握在你的脚下。

机器学习之决策树二-C4.5原理与代码实现的更多相关文章

  1. 机器学习之决策树三-CART原理与代码实现

    决策树系列三—CART原理与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/9482885.html ID ...

  2. 机器学习之决策树一-ID3原理与代码实现

    决策树之系列一ID3原理与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/9429257.html 应用实 ...

  3. 深入了解机器学习决策树模型——C4.5算法

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第22篇文章,我们继续决策树的话题. 上一篇文章当中介绍了一种最简单构造决策树的方法--ID3算法,也就是每次选择一个特 ...

  4. 图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)

    项目链接:https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1 欢迎fork欢迎三连!文章篇幅有限, ...

  5. 《机器学习_09_01_决策树_ID3与C4.5》

    简介 先看一个例子,某银行是否给用户放贷的判断规则集如下: if 年龄==青年: if 有工作==是: if 信贷情况==非常好: 放 else: 不放 else: if 有自己的房子==是: if ...

  6. 机器学习之决策树(ID3)算法与Python实现

    机器学习之决策树(ID3)算法与Python实现 机器学习中,决策树是一个预测模型:他代表的是对象属性与对象值之间的一种映射关系.树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每 ...

  7. python机器学习实战(二)

    python机器学习实战(二) 版权声明:本文为博主原创文章,转载请指明转载地址 http://www.cnblogs.com/fydeblog/p/7159775.html 前言 这篇noteboo ...

  8. 决策树之C4.5算法

    决策树之C4.5算法 一.C4.5算法概述 C4.5算法是最常用的决策树算法,因为它继承了ID3算法的所有优点并对ID3算法进行了改进和补充. 改进有如下几个要点: 用信息增益率来选择属性,克服了ID ...

  9. 机器学习之AdaBoost原理与代码实现

    AdaBoost原理与代码实现 本文系作者原创,转载请注明出处: https://www.cnblogs.com/further-further-further/p/9642899.html 基本思路 ...

随机推荐

  1. 书籍推荐Python编程:从入门到实践(高清完整pdf)

    这本书我看了电子版的,感觉还不错,全书共有20章,书中的简介如下: 本书旨在让你尽快学会 Python ,以便能够编写能正确运行的程序 -- 游戏.数据可视化和 Web 应用程序,同时掌握让你终身受益 ...

  2. css 滚动视差 之 水波纹效果

    核心属性: background-attachment 这个属性就牛逼了, 它可以定义背景图片是相对视口固定, 还是随着视口滚动, 加上这个属性网页瞬间就从屌丝变成 高大上. 我们来看个例子: htm ...

  3. 重磅!!!微软发布ASP.NET Core 2.2,先睹为快。

    我很高兴地宣布ASP.NET Core 2.2现在作为.NET Core 2.2的一部分提供! 如何获取? 您可以从.NET Core 2.2下载页面下载适用于您的开发机器和构建服务器的新.NET C ...

  4. KnockoutJS知识规整目录

    对于Web开发来讲,前端接触是避免不了的,特别是对于中小公司,没有严格的职位区分,前后端人员互相身兼是常有的事情,使用一些好的框架,能够帮助我们快速开发并完成需要的功能,对于前端的JS框架来讲MVVM ...

  5. 微服务(入门二):netcore通过consul注册服务

    基础准备 1.创建asp.net core Web 应用程序选择Api 2.appsettings.json 配置consul服务器地址,以及本机ip和端口号信息 { "Logging&qu ...

  6. Springboot文件上传代码笔记

    1.在src下创建filter包,包内Class名UploadFilter package com.gd.filter; import org.apache.catalina.servlet4prev ...

  7. Docker入门(三)使用Docker Compose

    Compose介绍   Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排.Compose 是一个用户定义和运行多个容器的 Docker 应用程序.在 ...

  8. 第65章 博客帖子 - Identity Server 4 中文文档(v1.0.0)

    第65章 博客帖子 65.1 团队帖子 65.1.1 2019 IdentityServer中的范围和声明设计 尝试使用IdentityServer4的设备流程 OAuth2中隐含流的状态 另一种保护 ...

  9. SpringBoot整合SpringCloud搭建分布式应用

    什么是SpringCloud? SpringCloud是一个分布式的整体解决方案.SpringCloud为开发者提供了在分布式系统中快速构建的工具,使用SpringCloud可以快速的启动服务或构建应 ...

  10. 【代码总结● Swing中的一些操作与设置】

    Swing中设置关闭窗口验证与添加背景图片 package com.swing.test; import java.awt.EventQueue; import java.awt.Image; imp ...