本文结构:

  1. 是什么?
  2. 有什么算法?
  3. 数学原理?
  4. 编码实现算法?

1. 是什么?

简单地理解,就是根据一些 feature 进行分类,每个节点提一个问题,通过判断,将数据分为几类,再继续提问。这些问题是根据已有数据学习出来的,再投入新数据的时候,就可以根据这棵树上的问题,将数据划分到合适的叶子上。


2. 有什么算法?

常用的几种决策树算法有ID3、C4.5、CART:

ID3:选择信息熵增益最大的feature作为node,实现对数据的归纳分类。
C4.5:是ID3的一个改进,比ID3准确率高且快,可以处理连续值和有缺失值的feature。
CART:使用基尼指数的划分准则,通过在每个步骤最大限度降低不纯洁度,CART能够处理孤立点以及能够对空缺值进行处理。


3. 数学原理?

ID3: Iterative Dichotomiser 3

参考

下面这个数据集,可以同时被上面两颗树表示,结果是一样的,而我们更倾向于选择简单的树。
那么怎样做才能使得学习到的树是最简单的呢?

下面是 ID3( Iterative Dichotomiser 3 )的算法:

例如下面数据集,哪个是最好的 Attribute?

用熵Entropy来衡量:
E(S) 是数据集S的熵
i 指每个结果,即 No,Yes的概率

E越大意味着信息越混乱,我们的目标是要让E最小。
E在0-1之间,如果P+的概率在0.5, 此时E最大,这时候说明信息对我们没有明确的意义,对分类没有帮助。

但是我们不仅仅想要变量的E最小,还想要这棵树是 well organized。
所以用到 Gain:信息增益

意思是如果我后面要用这个变量的话,它的E会减少多少。

例如下面的数据集:

  1. 先计算四个feature的熵E,及其分支的熵,然后用Gain的公式计算信息增益。

  2. 再选择Gain最大的特征是 outlook。

  3. 第一层选择出来后,各个分支再继续选择下一层,计算Gain最大的,例如分支 sunny 的下一层节点是 humidity。

详细的计算步骤可以参考这篇博文。


C4.5

参考

ID3有个局限是对于有大量数据的feature过于敏感,C4.5是它的一个改进,通过选择最大的信息增益率 gain ratio 来选择节点。而且它可以处理连续的和有缺失值的数据。

P’ (j/p) is the proportion of elements present at the position p, taking the value of j-th test.

例如 outlook 作为第一层节点后,它有 3 个分支,分别有 5,4,5 条数据,则 SplitInfo(5,4,5) = -5/14log(5,14)-4/14log(4,14)-5/14(5,14) ,其中 log(5,14) 即为 log2(5/14)。

下面是一个有连续值和缺失值的例子:

连续值

第一步计算 Gain,除了连续值的 humudity,其他步骤和前文一样。

要计算 humudity 的 Gain 的话,先把所有值升序排列:
{65, 70, 70, 70, 75, 78, 80, 80, 80, 85, 90, 90, 95, 96}
然后把重复的去掉:
{65, 70, 75, 78, 80, 85, 90, 95, 96}
如下图所示,按区间计算 Gain,然后选择最大的 Gain (S, Humidity) = 0.102

因为 Gain(S, Outlook) = 0 .246,所以root还是outlook:

缺失值

处理有缺失值的数据时候,用下图的公式:

例如 D12 是不知道的。

  1. 计算全集和 outlook 的 info,

  2. 其中几个分支的熵如下,再计算出 outlook 的 Gain:

比较一下 ID3 和 C4.5 的准确率和时间:

accuracy :

execution time:


4. 编码实现算法?

代码可以看《机器学习实战》这本书和这篇博客。

完整代码可以在 github 上查看。

接下来以 C4.5 的代码为例:

1. 定义数据:

 def createDataSet():
dataSet = [[0, 0, 0, 0, 'N'],
[0, 0, 0, 1, 'N'],
[1, 0, 0, 0, 'Y'],
[2, 1, 0, 0, 'Y'],
[2, 2, 1, 0, 'Y'],
[2, 2, 1, 1, 'N'],
[1, 2, 1, 1, 'Y']]
labels = ['outlook', 'temperature', 'humidity', 'windy']
return dataSet, labels

2. 计算熵:

 def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1 # 数每一类各多少个, {'Y': 4, 'N': 3}
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
shannonEnt -= prob * log(prob, 2)
return shannonEnt

3. 选择最大的gain ratio对应的feature:

 def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 #feature个数
baseEntropy = calcShannonEnt(dataSet) #整个dataset的熵
bestInfoGainRatio = 0.0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataSet] #每个feature的list
uniqueVals = set(featList) #每个list的唯一值集合
newEntropy = 0.0
splitInfo = 0.0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i, value) #每个唯一值对应的剩余feature的组成子集
prob = len(subDataSet)/float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
splitInfo += -prob * log(prob, 2)
infoGain = baseEntropy - newEntropy #这个feature的infoGain
if (splitInfo == 0): # fix the overflow bug
continue
infoGainRatio = infoGain / splitInfo #这个feature的infoGainRatio
if (infoGainRatio > bestInfoGainRatio): #选择最大的gain ratio
bestInfoGainRatio = infoGainRatio
bestFeature = i #选择最大的gain ratio对应的feature
return bestFeature

4. 划分数据,为下一层计算准备:

 def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value: #只看当第i列的值=value时的item
reduceFeatVec = featVec[:axis] #featVec的第i列给除去
reduceFeatVec.extend(featVec[axis+1:])
retDataSet.append(reduceFeatVec)
return retDataSet

5. 多重字典构建树:

 def createTree(dataSet, labels):
classList = [example[-1] for example in dataSet] # ['N', 'N', 'Y', 'Y', 'Y', 'N', 'Y']
if classList.count(classList[0]) == len(classList):
# classList所有元素都相等,即类别完全相同,停止划分
return classList[0] #splitDataSet(dataSet, 0, 0)此时全是N,返回N
if len(dataSet[0]) == 1: #[0, 0, 0, 0, 'N']
# 遍历完所有特征时返回出现次数最多的
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet) #0-> 2
# 选择最大的gain ratio对应的feature
bestFeatLabel = labels[bestFeat] #outlook -> windy
myTree = {bestFeatLabel:{}}
#多重字典构建树{'outlook': {0: 'N'
del(labels[bestFeat]) #['temperature', 'humidity', 'windy'] -> ['temperature', 'humidity']
featValues = [example[bestFeat] for example in dataSet] #[0, 0, 1, 2, 2, 2, 1]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:] #['temperature', 'humidity', 'windy']
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
# 划分数据,为下一层计算准备
return myTree

6. 可视化决策树的结果:

dataSet, labels = createDataSet()
labels_tmp = labels[:]
desicionTree = createTree(dataSet, labels_tmp)
treePlotter.createPlot(desicionTree)
 

【机器学习笔记之二】决策树的python实现的更多相关文章

  1. [机器学习笔记]主成分分析PCA简介及其python实现

    主成分分析(principal component analysis)是一种常见的数据降维方法,其目的是在“信息”损失较小的前提下,将高维的数据转换到低维,从而减小计算量. PCA的本质就是找一些投影 ...

  2. 机器学习笔记之二-win10+cuda9.1+CUDNN7+Anaconda3+VS2017+tensorflow1.5+opencv3.4

     [Tensorflow]环境搭建vs2017+win10+py3.6+cuda9.1+cudnn7+tf1.5 一.安装cuda 9.1+VS2017   一路下一步即可,环境变量cuda会自动配好 ...

  3. 机器学习算法总结(二)——决策树(ID3, C4.5, CART)

    决策树是既可以作为分类算法,又可以作为回归算法,而且在经常被用作为集成算法中的基学习器.决策树是一种很古老的算法,也是很好理解的一种算法,构建决策树的过程本质上是一个递归的过程,采用if-then的规 ...

  4. python3学习笔记(二):Python初识

    一.算法 在开始认真地编程之前,首先来解释下什么是计算机程序设计.简单地说,它就是告诉计算机要做什么.计算机可以做很多事情,但是它不会自己思考,需要我们告诉它具体细节,并且使用计算机能够理解的语言把算 ...

  5. Coursera 机器学习笔记(二)

    主要为第三周课程内容:逻辑回归与正则化 逻辑回归(Logistic Regression) 一.逻辑回归模型引入 分类问题是指尝试预测的是结果是否属于某一个类. 维基百科的定义为:根据已知训练区提供的 ...

  6. python学习笔记(二)之python简单实践

    1 安装python开发环境 Linux环境下自动安装好了python,可以通过以下命令更新到python最新版本. #echo "alias python=/usr/bin/python3 ...

  7. 吴恩达机器学习笔记(二) —— Logistic回归

    主要内容: 一.回归与分类 二.Logistic模型即sigmoid function 三.decision boundary 决策边界 四.cost function 代价函数 五.梯度下降 六.自 ...

  8. Python学习笔记(二)——列表

    Python学习笔记(二)--列表 Python中的列表可以存放任何数据类型 >>> list1 = ['Hello','this','is','GUN',123,['I','Lov ...

  9. cs229 斯坦福机器学习笔记(一)-- 入门与LR模型

    版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/Dinosoft/article/details/34960693 前言 说到机器学习,非常多人推荐的学习资 ...

随机推荐

  1. Kafka 源代码分析之MessageSet

    这里分析MessageSet类 MessageSet是一个抽象类,定义了一条log的一些接口和常量,FileMessageSet就是MessageSet类的实现类.一条日志中存储的log完整格式如下 ...

  2. 关于mysql查询数据库时间和系统时间差

    1. MySQL数据库表中有两个时间的字段,需要计算他们的时间差: (1)datediff函数来表示时间差. 基本语法: DATEDIFF(datepart,startdate,enddate) 说明 ...

  3. 打包可执行的jar

    #配置项目路径 *除程序文件以外,其他相关素材也可以打包进jar,但在内部访问时需以包名作为跟路径,如hello/xxx/yyy.zzz mkdir hello vi hello/HelloWorld ...

  4. Mybatis(七) mybatis的逆向工程的配置详解

    还是觉得看书学习有意思~嘿嘿.今天把mybatis给结束掉. --WH 一.什么是逆向工程? 简单点说,就是通过数据库中的单表,自动生成java代码. Mybatis官方提供了逆向工程,可以针对单表自 ...

  5. hibernate总结-持续更新

    简介 hibernate官网:Hibernate Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思 ...

  6. JavaScript图片翻转

    <script type="text/javascript"> /** * 注册函数f,当文档加载问成时执行这个函数f * 如果文件已经载入完成,尽快以异步方式执行它 ...

  7. HTML5中a标签的锚点使用

    前几天有个用户问我关于在线手册功能里的锚点问题.因为他通过代码发现,在编辑手册内容时,锚点的设置是通过id选择器来制定的,而不是带有name属性的a标签.其实这是HTML5和HTML4(XHTML)等 ...

  8. 禁用Ubuntu 15.04登录界面显示客人会话

    在控制台打开如下配置文件,如果没有就自己创建一个: sudo vi /etc/lightdm/lightdm.conf 向文件中添加如下内容: [SeatDefaults] greeter-sessi ...

  9. promise异步编程的原理

    一.起源 JavaScript中的异步由来已久,不论是定时函数,事件处理函数还是ajax异步加载都是异步编程的一种形式,我们现在以nodejs中异步读取文件为例来编写一个传统意义的异步函数: var ...

  10. koa2 use里面的next到底是什么

    koa2短小精悍,女人不爱男人爱. 之前一只有用koa写一点小程序,自认为还吼吼哈,知道有一天某人问我,你说一下 koa或者express中间件的实现原理.然后我就支支吾吾,好久吃饭都不香. 那么了解 ...