前言

Decision tree is one of the most popular classification tools
它用一个训练数据集学到一个映射,该映射以未知类别的新实例作为输入,输出对这个实例类别的预测。
决策树相当于将一系列问题组织成树,具体说,每个问题对应一个属性,根据属性值来生成判断分支,一直到决策树的叶节点就产生了类别。

那么,接下来的问题就是怎么选择最佳的属性作为当前的判断分支,这就引出了用信息论划分数据集的方式。
在信息论中,划分数据之前和之后信息发生的信息变化成为信息增益。

本文主要介绍的决策树算法是ID3,它的核心是将获得信息增益最高的特征作为最好的选择。
他和C4.5算法不同的是C4.5算法使用的是信息增益比最高的特征作为最好的选择。

本文参考书是《机器学习实战》


信息增益

信息论中每个符号x的信息量成为自信息,定义为:
其中是选择该分类的概率。
熵(entropy)定义为信息的期望值,意思是每个符号的平均信息量,其公式为:


给定一个数据集,最后一列默认为类别标签的时候,根据数据集计算数据集的香农熵的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#基于最后一列的分类标签,计算给定数据集的香农熵
def calcShannonEnt(dataset):
    num_of_entries = len(dataset)
    label_counts = {}
    for feat_vec in dataset:
        current_lebel = feat_vec[-1]
        if current_lebel not in label_counts.keys():
            label_counts[current_lebel] = 0
        label_counts[current_lebel] += 1
    shannonEnt = 0.0
    for value in label_counts.values():
        prob = float(value)/num_of_entries
        shannonEnt -= prob*log(prob, 2)
    return shannonEnt

要得到信息增益,我们还需要对每个特征划分数据集的结果计算一次信息熵。
所以首先如要划分数据集。按照给定特征的某个值把属于这个值的数据集部分划分出来,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# =================================
# 按照给定特征划分数据集
# 输入:dataset数据集;
#       axis指定特征,用下标表示;
#       value需要返回的特征的值
# 返回:数据集中特征值等于value的子集
# =================================
def splitDataset(dataset, axis, value):
    retDataset = []
    for featVec in dataset:
        if featVec[axis] == value:
            reducedFeatVec = featVec[0:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            retDataset.append(reducedFeatVec)
    return  retDataset

具体到这个算法来说,应该选择能使信息增益最大的特征作为数据集的划分方式。
信息增益等于原始数据集的熵减去某个特征所带来的信息熵。
计算某个特征的信息熵以及基于此选择最好的数据集划分方式的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# ===============================================
# 输入:
#        dataSet: 数据集
# 输出:
#        bestFeature: 和原数据集熵差最大划分对应的特征的列号
# ===============================================
def chooseBestFeatureToSplit(dataSet):
    # 最后一列用于标签,剩下的才是特征
    numFeatures = len(dataSet[0]) - 1
    # 根据标签计算的熵
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain = 0.0; bestFeature = -1
    # iterate over all the features
    for in range(numFeatures):
        # 取出某个特征列的所有值
        featList = [example[i] for example in dataSet]
        # 去重
        uniqueVals = set(featList)
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataset(dataSet, i, value)
            prob = len(subDataSet)/float(len(dataSet))
            newEntropy += prob * calcShannonEnt(subDataSet)
        # calculate the info gain,计算信息增益
        infoGain = baseEntropy - newEntropy
        # 和目前最佳信息增益比较,如果更大则替换掉
        if (infoGain > bestInfoGain):
            bestInfoGain = infoGain
            bestFeature = i
    # 返回代表某个特征的下标
    return bestFeature
用一下代码去测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#用于生成数据集,测试计算熵的函数
def testDataset():
    dataset1 = [[11'yes'],
               [11'yes'],
               [10'no'],
               [01'no'],
               [01'no']]
    labels = ['no surfacing''flippers']
    return dataset1, labels
 
# 用于测试的函数
def test():
    mydata, labels = testDataset()
    print chooseBestFeatureToSplit(mydata)
可得到如下结果:
结果表明,第0个特征是用来划分数据集最好的。


递归构建决策树

选择了一个特征进行划分之后,数据将被传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。
所以这是一个递归的过程。
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有势力都属于同一个分类。

在Python中可以使用字典来表示一棵树,例如这样的一棵树
可以用字典{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}表示

但是这里还可能出现另一种可能,就是所有用来划分数据集的属性全部用完之后,类别标签依然不是唯一的
这种情况我们采用少数服从多数来解决所以我们需要一个找出多数类别的函数,如下:
1
2
3
4
5
6
7
8
9
10
11
12
# 传入分类名称组成的列表,返回出现次数最多的分类名称
import operator
def majorityCnt(class_list):
    classCount = {}
    for vote in class_list:
        if vote not in classCount:
            classCount[vote] = 0
        classCount[vote] += 1
    sorted_class_list = sorted(classCount.iteritems(),
                               key = operator.itemgetter(1),
                               reverse=True)
    return sorted_class_list[0][0]

接下来是创建树的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# ===============================================
# 本函数用于创建决策树
# 输入:
#        dataSet: 数据集
#        labels: 划分特征标签集
# 输出:
#        myTree: 生成的决策树
# ===============================================
def createTree(dataSet, labels):
    # 获得类别标签列表
    classList = [example[-1for example in dataSet]
    # 递归终止条件一:如果数据集内所有分类一致
    if classList.count(classList[0]) == len(classList):
        return classList[0]
    # 递归终止条件二:如果所有特征都划分完毕,任然不能将数据集划分成仅仅包含唯一类别的分组
    if len(dataSet[0]) == 1:  # 只剩下一列为类别列
        return majorityCnt(classList) # 返回出现次数最多的类别
    # 选择最佳划分特征,返回的时候特征的下标
    best_feature = chooseBestFeatureToSplit(dataSet)
    best_feat_label = labels[best_feature]
    # 创建空树
    myTree = {best_feat_label:{}}
    # 删除划分后的特征标签
    del(labels[best_feature])
    # 获取最佳划分特征中全部的特征值
    featValues = [example[best_feature] for example in dataSet]
    # 去重
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]  # 保存用于下一次递归
        myTree[best_feat_label][value] = createTree(splitDataset(dataSet, best_feature, value), subLabels)
 
    return myTree
至此,决策树就算构造完成了。
测试一下效果:

序列化存储

由于决策树构造使用递归算法,如果数据集过大的话将会产生很大的开销。
所以构造好一个决策树我们可以把它保存起来,这样就不用每次使用都构造。
保存的方式使“序列化”,在Python中又叫“pickling”,它的反操作叫反序列化——“unpickling”。
任何对象都可以执行序列化操作。
本文中用于把树序列化的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
# 把传入的树序列化之后存入文件
def storeTree(inputTree, filename):
    import pickle # 用于序列化的模块
    fw = open(filename, 'w')
    pickle.dump(inputTree, fw)
    fw.close()
 
# 从文件中把存好的树反序列化出来
def grabTree(filename):
    import pickle
    fr = open(filename)
    return pickle.load(filename)

【3】Decision tree(决策树)的更多相关文章

  1. Decision tree(决策树)算法初探

    0. 算法概述 决策树(decision tree)是一种基本的分类与回归方法.决策树模型呈树形结构(二分类思想的算法模型往往都是树形结构) 0x1:决策树模型的不同角度理解 在分类问题中,表示基于特 ...

  2. decision tree 决策树(一)

    一 决策树 原理:分类决策树模型是一种描述对实例进行分类的树形结构.决策树由结点(node)和有向边(directed edge)组成.结点有两种类型:内部结点(internal node)和叶结点( ...

  3. Decision tree——决策树

    基本流程 决策树是通过分次判断样本属性来进行划分样本类别的机器学习模型.每个树的结点选择一个最优属性来进行样本的分流,最终将样本类别划分出来. 决策树的关键就是分流时最优属性$a$的选择.使用所谓信息 ...

  4. OpenCV码源笔记——Decision Tree决策树

    来自OpenCV2.3.1 sample/c/mushroom.cpp 1.首先读入agaricus-lepiota.data的训练样本. 样本中第一项是e或p代表有毒或无毒的标志位:其他是特征,可以 ...

  5. 决策树Decision Tree 及实现

    Decision Tree 及实现 标签: 决策树熵信息增益分类有监督 2014-03-17 12:12 15010人阅读 评论(41) 收藏 举报  分类: Data Mining(25)  Pyt ...

  6. 用于分类的决策树(Decision Tree)-ID3 C4.5

    决策树(Decision Tree)是一种基本的分类与回归方法(ID3.C4.5和基于 Gini 的 CART 可用于分类,CART还可用于回归).决策树在分类过程中,表示的是基于特征对实例进行划分, ...

  7. 决策树(decision tree)

    决策树是一种常见的机器学习模型.形象地说,决策树对应着我们直观上做决策的过程:经由一系列判断,得到最终决策.由此,我们引出决策树模型. 一.决策树的基本流程 决策树的跟节点包含全部样例,叶节点则对应决 ...

  8. (ZT)算法杂货铺——分类算法之决策树(Decision tree)

    https://www.cnblogs.com/leoo2sk/archive/2010/09/19/decision-tree.html 3.1.摘要 在前面两篇文章中,分别介绍和讨论了朴素贝叶斯分 ...

  9. 决策树decision tree原理介绍_python sklearn建模_乳腺癌细胞分类器(推荐AAA)

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

  10. [ML学习笔记] 决策树与随机森林(Decision Tree&Random Forest)

    [ML学习笔记] 决策树与随机森林(Decision Tree&Random Forest) 决策树 决策树算法以树状结构表示数据分类的结果.每个决策点实现一个具有离散输出的测试函数,记为分支 ...

随机推荐

  1. 不用 Spring Security 可否?试试这个小而美的安全框架

    写在前面 在一款应用的整个生命周期,我们都会谈及该应用的数据安全问题.用户的合法性与数据的可见性是数据安全中非常重要的一部分.但是,一方面,不同的应用对于数据的合法性和可见性要求的维度与粒度都有所区别 ...

  2. Docker笔记(七):常用服务安装——Nginx、MySql、Redis

    开发中经常需要安装一些常用的服务软件,如Nginx.MySql.Redis等,如果按照普通的安装方法,一般都相对比较繁琐 —— 要经过下载软件或源码包,编译安装,配置,启动等步骤,使用 Docker ...

  3. 数据结构之堆栈C++版

    /* 堆栈本身就是一种线性数据结构,说白了他与容器线性表是一种数据类型,不要认为他多高大上. 实时上他还没有线性表复杂,下面简单的实现一下堆栈. 事实上整个核心操作都是在操作指向堆栈的顶部元素的指针 ...

  4. hdu1241 油田计数

    具体思路:求联通块,在"@“的周围进行dfs,使用8个方向向量来代表搜索的方向 贴一下我的主要代码段: int dir[8][2]={{1,1},{-1,-1},{1,-1},{-1,1}, ...

  5. kpm字符串匹配算法

    首先是简单的朴素匹配算法 /* * 返回子串t在主串s的位置,若不存在则返回0 */ public static int index(String s, String t) { int i = 0;/ ...

  6. vue生成element左侧菜单

    首先来总结element ui 官方文档的左侧菜单结构,带有el-submenu为子级节点,el-menu-item表示没有下级.当然,菜单不能写死,因为菜单也许不止两级,所以我们需要递归来实现.根据 ...

  7. 初学html总结

    2019-08-17 17:58:49 html:超文本标记语言,用于网页结构的搭建 html语言构成:由标签.属性.属性值构成 标签:" < "后面第一个单词 属性:标签后 ...

  8. 为什么要用Kubernetes?

    1.前言 第一次接触Kubernetes是在2016年,再一次浏览博文的时候,那是我第一次听到Kubernetes这个名词,也是第一次认识了k8s这么一个东西.后来在慢慢了解它的时候,被它天生高可用. ...

  9. 最小生成树详细讲解(一看就懂!) & kruskal算法

    0.前言 因为本人太蒟了 我现在连NOIP的初赛都在胆战心惊 并且我甚至连最小生成树都没有学过 所以这一篇博客一定是最详细的QAQ 哈哈 请您认真看完如果有疏漏之处敬请留言指正 感谢! Thanks♪ ...

  10. DFS树求割点问题

    时间复杂度:O(n玄学)总之不大 代码实现(好麻烦,蓝题变紫题) #include<iostream> #include<string.h> #include<algor ...