python机器学习笔记:ID3决策树算法实战

机器学习实战 决策树 决策树的构造 trees.py代码 from math import log import operator #计算熵 def calcShannonEnt(dataSet): # numEntries保存实例总数 numEntries=len(dataSet) labelCounts={} #取每个实例最后一项作为特征currentLabel for featVec in dataSet: currentLabel=featVec[-. 阅读详情


  ID3算法是一种贪心算法,用来构造决策树,ID3算法起源于概念学习系统(CLS),以信息熵的下降速度为选取测试属性的标准,即在每一个节点选取还尚未被用来划分的具有最高信息增益的属性作为划分标准,然后继续这个过程,直到生成的决策树能完美的分类训练样例。

       在此之前,推荐大家可以多在FlyAI竞赛服务平台多参加训练和竞赛,以此来提升自己的能力。FlyAI是为AI开发者提供数据竞赛并支持GPU离线训练的一站式服务平台。每周免费提供项目开源算法样例,支持算法能力变现以及快速的迭代算法模型。

ID3算法的背景知识

  ID3算法最早是由罗斯昆兰(J. Ross Quinlan)于1975年在悉尼大学提出的一种分类预测算法,算法的核心是“信息熵”。ID3算法通过计算每个属性的信息增益,认为信息增益高的是好属性,每次划分选取信息增益最高的属性为划分标准,重复这个过程,直至生成一个能完美分类训练样例的决策树。

  决策树是对数据进行分类,以此达到预测的目的。该决策树方法先根据训练集数据形成决策树,如果该树不能对所有对象给出正确的分类,那么选择一些例外加入到训练集数据中,重复该过程一直到形成正确的决策集。决策树代表着决策集的树形结构。

  决策树由决策结点、分支和叶子组成。决策树中最上面的结点为根结点,每个分支是一个新的决策结点,或者是树的叶子。每个决策结点代表一个问题或决策,通常对应于待分类对象的属性。每一个叶子结点代表一种可能的分类结果。沿决策树从上到下遍历的过程中,在每个结点都会遇到一个测试,对每个结点上问题的不同的测试输出导致不同的分支,最后会到达一个叶子结点,这个过程就是利用决策树进行分类的过程,利用若干个变量来判断所属的类别。

ID3算法数据描述

所使用的样本数据有一定的要求,ID3是:

  描述-属性-值相同的属性必须描述每个例子和有固定数量的价值观。

  预定义类-实例的属性必须已经定义的,也就是说,他们不是学习的ID3。

  离散类-类必须是尖锐的鲜明。连续类分解成模糊范畴(如金属被“努力,很困难的,灵活的,温柔的,很软”都是不可信的。

  足够的例子——因为归纳概括用于(即不可查明)必须选择足够多的测试用例来区分有效模式并消除特殊巧合因素的影响。

  ID3决定哪些属性如何是最好的。一个统计特性,被称为信息增益,使用熵得到给定属性衡量培训例子带入目标类分开。信息增益最高的信息(信息是最有益的分类)被选择。为了明确增益,我们首先从信息论借用一个定义,叫做熵。每个属性都有一个熵。

ID3决策树概述

  ID3决策树是一种非常重要的用来处理分类问题的结构,它形似一个嵌套N层的IF…ELSE结构,但是它的判断标准不再是一个关系表达式,而是对应的模块的信息增益。它通过信息增益的大小,从根节点开始,选择一个分支,如同进入一个IF结构的statement,通过属性值的取值不同进入新的IF结构的statement,直到到达叶子节点,找到它所属的“分类”标签。

  它的流程图是一颗无法保证平衡的多叉树,每一个父节点都是一个判断模块,通过判断,当前的向量会进入它的某一个子节点中,这个子节点是判断模块或者终止模块(叶子节点),当且仅当这个向量到达叶子节点,它也就找到了它的“分类”标签。

  ID3决策树通过一个固定的训练集是可以形成一颗永久的“树”的,这课树可以进行保存并且运用到不同的测试集中,唯一的要求就是测试集和训练集需要是结构等价的。这个训练过程就是根据训练集创建规则的过程,这也是机器学习的过程。

  ID3决策树的一个巨大缺陷是:它将产生过度匹配问题。这里在不讨论信息增益的前提下,有这样一个例子:人的属性中有性别和年龄两个属性,由于人的性别只有男和女两种,年龄有很多种分支,当它有超过两个分支的时候,在用信息增益选择新的属性的时候,会选择年龄而不是性别,因为ID3决策树在使用信息增益来划分数据集的时候会倾向于选择属性分支更多的一个;另外一个缺陷是,人的年龄假定为1~100,如果不进行离散化,即区间的划分,那么在选择年龄这个属性的时候,这棵决策树会产生最多100个分支,这是非常可怕而且浪费空间和效率的,考虑这 样一种情况:两个人的其他所有属性完全相同,他们的分类都是"A",然而在年龄这一个树节点中分支了,而这个年龄下有一个跟这两个人很像,却不属于“A”类别的人,由于ID3决策树无法处理连续性数据,那么这两个人很有可能被划分到两个分类中,这是不合理的,这也是C4.5决策树考虑的问题。

 ID3决策树算法推导

  如果以前没有接触过决策树,完全不用担心,它的概念非常简单,即使不知道它也可以通过简单的图像了解其工作原理,下图就是一个决策树,正方形代表判断模型(decision block),椭圆形代表终止模块(terminating block),表示已经得出结论,可以终止运行。从判断模块引出的左右箭头称作分支(branch),它首先检测发送邮件域名地址。如果地址为myEmployer.com,则将其分类“无聊时需要阅读的邮件”中。如果邮件不是来自这个域名,则检查邮件内容里是否包含单词曲棍球,如果包含则将邮件归类到“需要及时处理的盆友邮件”,如果不包含则将邮件归类到“无需阅读的垃圾邮件”。

  决策树很多任务都是为了数据中蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列的规则,机器学习算法最终将使用这些机器从数据及中创造的规则。专家系统中经常使用决策树,而且决策树给出结果往往可以匹敌在当前领域具有几十年工作经验的人类专家。

  现在我们已经大致了解了决策树可以完成哪些任务,接下来我们将学习如何从一堆原始数据中构造决策树,首先我们讨论构造决策树的方法,以及如何编写决策树的Python代码;接着剔除一些度量算法成功率的方法;最后使用递归建立分类器,并且使用Matplotlib绘制决策树图,构造完成决策树分类器之后,我们将输入一些隐形眼镜的处方数据,并由决策树分类器预测需要的镜片类型。

1. 决策树的构造

  在一步步地构造决策树算法的时候,首先我们讨论数学上如何使用信息论划分数据集,然后编写代码将理论应用到具体的数据集上,最后编写代码构建决策树。

  在构造决策树时,我们需要解决的第一个问题就是,当前数据集上哪个特征在划分数据分类时起决定性作用。为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征。完成测试之后,原始数据集就被划分为几个数据子集,这些数据子集会分布在第一个决策点的所有分支上,如果某个分支下的数据属于同一类型,则当前无需阅读的垃圾邮件已经正确的划分数据分类,无需进一步对数据集进行分割,如果数据子集内的数据不属于同一类型,则需要重复划分数据子集的过程,如何划分数据子集的算法和划分原始数据集的方法相同,知道所有具有相同类型的数据均在一个数据子集内。

  创建分支的伪代码函数createBranch() 如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

检测数据集中的每个子项是否属于同一分类:

    If  so  retrun   类标签;

    Else

            寻找划分数据集的最好特征

 

            划分数据集

 

            创建分支节点

 

                for  每个划分的子集

 

                    调用函数createBranch并增加返回结果到分支节点中

                 

                return  分支节点

  上面的伪代码createBranch是一个递归函数,在倒数第二行直接调用了它自己,后面我们将把上面的伪代码转换为Python代码,这里我们需要了解一下算法是如何划分数据集的。

1.1 决策树的一般流程

  • (1) 收集数据:可以使用任何方法
  • (2) 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化
  • (3) 分析数据:可以使用任何方法,构造树完成之后,我们应该检查图像是否符合预期
  • (4) 训练算法:构造树的数据结构
  • (5) 测试算法:使用经验树计算错误率
  • (6)使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好的理解数据的内在含义

1.2 划分特征

  一些决策树算法采用二分法划分数据,此处不采用这种方法,本次将使用ID3算法划分数据集,该算法处理如何划分数据集,何时停止划分数据集。如果依据某个属性划分数据将会产生4个可能的值,我们将数据划分为四块,并创建四个不同的分支,每次划分数据集时我们只选取一个特征属性,如果训练集中存在20个特征,第一次我们选择哪个特征作为划分的参考属性呢?

  下表的数据包含5个海洋动物,特征包括:不浮出水面是否可以生存,以及是否有脚趾。我们可以将这些动物划分为两类:鱼类和非鱼类,现在我们想要决定依据第一个特征还是第二个特征划分数据,那么那个特征可以作为第一个划分点呢?让我们继续学习。

 1.3  信息增益

  划分数据集的大原则是:将无序的数据变得更加有序。我们可以使用多种方法划分数据集,但是每种方法都有各自的优缺点。组织杂乱无章数据的一种方法就是使用信息论度量信息,信息论是量化处理信息的分支科学。我们可以在划分数据之前使用信息论量化度量信息的内容。

  在划分数据集之前之后信息发生的变化成为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

  在可以评测哪种数据划分方式是最好的数据划分之前,我们必须学习如何计算信息增益。集合信息的度量方式称为香农熵或者简称为熵(这名字来源于信息论之父克劳德*香农)

  熵定义为信息的期望值,在明白这个概念之前,我们必须知道信息的定义,如果待分类的事务可能划分在多个分类之中,则符号Xi的信息定义为:

  其中P(Xi)是选择该分类的概率。

  为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值,通过下面的公式得到:

  其中n是分类的数目。

代码如下:

  此代码是使用Python计算给定数据集的信息熵。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

from math import log

 

# 计算数据的熵(entropy)

def calcShannonRnt(dataSet):

    # 数据条数,计算数据集中实例的总数

    numEntries = len(dataSet)

    labelCounts = {}

    for featVec in dataSet:

        # 每行数据的最后一个类别(也就是标签)

        currentLable = featVec[-1]

        if currentLable not in labelCounts.keys():

            labelCounts[currentLable] = 0

        # 统计有多少个类以及每个类的数量

        labelCounts[currentLable]  += 1

    shannonEnt = 0.0

    for key in labelCounts:

        # 计算单个类的熵值

        prob = float(labelCounts[key]) / numEntries

        # 累加每个类的熵值

        shannonEnt -= prob * log(prob , 2)

    return shannonEnt

  下面我们创建一个简单的数据集(此处我们使用机器学习实战中的简单鱼鉴定数据集):

1

2

3

4

5

6

7

8

9

# 创建数据集

def createDataSet():

    dataSet = [[1,1,'yes'],

               [1,1,'yes'],

               [1,0,'no'],

               [0,1,'no'],

               [0,1,'no']]

    labels = ['no surfacing','flippers']

    return dataSet,labels

  代码执行结果:

1

2

3

4

5

6

7

8

9

10

11

12

myData,labels = createDataSet()

print(myData)

print(labels)

 

shannonEnt = calcShannonRnt(myData)

print(shannonEnt)

 

'''

[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

['no surfacing', 'flippers']

0.9709505944546686

'''

  从结果来看,熵为0.97,熵越高,则混合的数据也越多,随机变量的不确定性越大,我们可以在数据集中添加更多的分类,观察熵是如何变化的。

 2. 划分数据集

  上面我们学习了如何度量数据集的无序程度,分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便判断当前是否正确地划分了数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。想象一个分布在二维空间的数据散点图,需要在数据之间画条线,将他们分成两部分,我们应该按照x轴还是y轴划分呢?

  按照给定特征划分数据集代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

# 按照给定特征划分数据集

def splitDataSet(dataSet,axis,value):

    '''

 

    :param dataSet: 待划分的数据集

    :param axis: 划分数据集的特征

    :param value: 特征的返回值

    :return:

    '''

    retDataSet = []

    for featVec in dataSet:

        # 如果发现符合要求的特征,将其添加到新创建的列表中

        if featVec[axis] == value:

            reduceFeatVec = featVec[:axis]

            reduceFeatVec.extend(featVec[axis+1:])

            retDataSet.append(reduceFeatVec)

 

    return retDataSet

  我们使用划分数据集测试一下,可以看到划分数据集结果如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

myData,labels = createDataSet()

print(myData)

print(labels)

 

splitDataSetVal = splitDataSet(myData,0,1)

print(splitDataSetVal)

splitDataSetVal = splitDataSet(myData,0,0)

print(splitDataSetVal)

 

'''

[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

['no surfacing', 'flippers']

[[1, 'yes'], [1, 'yes'], [0, 'no']]

[[1, 'no'], [1, 'no']]

'''

  接下来,我们将遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的特征划分方式,熵计算将会告诉我们如何划分数据集是最好的数据组织方式。

  选择最好的数据集划分方式代码:

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

# 选择最好的数据集划分方式

def chooseBestFeatureTpSplit(dataSet):

    '''

        此函数中调用的数据满足以下要求

        1,数据必须是一种由列表元素组成的列表,而且所有列表元素都要具有相同的数据长度

        2,数据的最后一列或者实例的最后一个元素是当前实例的类别标签

    :param dataSet:

    :return:

    '''

    numFeatures = len(dataSet[0]) - 1

    # 原始的熵

    baseEntropy = calcShannonRnt(dataSet)

    bestInfoGain = 0.0

    bestFeature = -1

    for i 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 * calcShannonRnt(subDataSet)

        infoGain = baseEntropy - newEntropy

        if (infoGain > bestInfoGain):

            # 计算最好的信息增益,信息增益越大,区分样本的能力越强,根据代表性

            bestInfoGain = infoGain

            bestFeature = i

    return bestFeature

  测试代码,得到结果:

1

2

3

4

5

6

7

8

9

10

11

myData,labels = createDataSet()

print(myData)

print(labels)

 

bestFeatureRes = chooseBestFeatureTpSplit(myData)

print(bestFeatureRes)

'''

[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

['no surfacing', 'flippers']

0

'''

  结果告诉我们第0个特征是最好的用于划分数据集的特征。如果按此特征分类,第一个海洋动物分组将有两个属于鱼类,两个属于非鱼类,另一个分组则只有一个非鱼类。

  假设我们按照第一个特征属性划分数据,也就是说第一个特征是1的放在一个组,第一个特征是0的放在另一个组,数据一致性如何?按照上述的方法划分数据集,第一个特征为1的海洋生物分组将有两个属于鱼类,一个属于非鱼类;另一个分组则全部属于非鱼类。

3 . 递归构建决策树

     总结一下构建决策树的工作原理:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。

  递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。如果所有实例具有相同的分类,则得到一个叶子节点护着终止块。任何到达叶子节点的数据必然属于叶子节点的分类,参见下图:

  第一个结束条件使得算法可以终止,我们甚至可以设置算法可以划分的最大分组数目。当然目前我们只需要在算法开始运行前计算列的数目,查看算法是否使用了所有属性即可,如果数据集已经处理了所有属性,但是类标签依然不是唯一的,此时我们需要决定如何定义该叶子节点,在这种情况下,我们通常会采用多数表决的方法决定该叶子节点的分类。

  按照分类后类别数量排序代码:

1

2

3

4

5

6

7

8

9

10

# 按照分类后类别数量排序

def majorityCnt(classList):

    classCount = {}

    for vote in classList:

        if vote not in classCount.keys():

            classCount[vote] = 0

        classCount[vote] += 1

    sortedClassCount = sorted(classCount.items(),

                              key=operator.itemgetter(1),reverse=True)

    return sortedClassCount[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

# 创建树的函数代码

def createTree(dataSet,labels):

    '''

 

        :param dataSet:  输入的数据集

        :param labels:  标签列表(包含了数据集中所有特征的标签)

        :return:

        '''

    # classList 包含了数据集中所有类的标签

    classList = [example[-1] for example in dataSet]

    # 类别完全相同则停止继续划分

    if classList.count(classList[0]) == len(classList):

        return classList[0]

    # 遍历完所有特征时返回出现次数最多的

    if len(dataSet[0])  == 1:

        return majorityCnt(classList)

    bestFeat = chooseBestFeatureTpSplit(dataSet)

    bestFeatLabel = labels[bestFeat]

    # 字典myTree存储了树的所有信息,这对于后面绘制树形图很重要

    myTree = {bestFeatLabel:{}}

    del(labels[bestFeat])

    # 得到列表包含的所有属性值

    featValues = [example[bestFeat] for example in dataSet]

    uniqueVals = set(featValues)

    for value in uniqueVals:

        subLabels =labels[:]

        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),

                                                  subLabels)

  运行代码结果如下:

1

2

3

4

5

6

7

8

9

10

11

myData,labels = createDataSet()

print(myData)

print(labels)

 

myTree = createTree(myData,labels)

print(myTree)

'''

[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

['no surfacing', 'flippers']

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

'''

  从结果来看,变量myTree包含了很多代表树结构信息的嵌套字典,从左边开始,第一次关键字nosurfacing是第一个划分数据集的特征名称,该关键字的值也是另一个数据字典,第二个关键字是no   surfacing特征划分的数据集,这些关键字的值时no  surfacing节点的子节点,这些值可能是类标签,也可能是另一个数据字典,如果值时类标签,则该子节点是叶子节点;如果值时另一个数据字典,则子节点是一个判断节点,这种格式结构不断重复就构成了整棵树。

4 . 使用Matplotlib注解绘制树形图

  上面我们学习了如何从数据集中创建树,然而字典的表示形式非常不易于理解,而且直接绘制图形也比较困难,这里我们学习使用Matplotlib库创建树形图。决策树的主要优点就是直观易于理解,如果不能将其直观的显示出来,就无法发挥其优势。

  Matplotlib可以对文字着色并提供多种形状以供选择,而且我们还可以反转箭头,将它指向文本框而不是数据点。

  使用文本注解绘制树节点代码:

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

import matplotlib.pyplot as plt

 

# 定义文本框和箭头格式(树节点格式的常量)

decisionNode = dict(boxstyle='sawtooth',fc='0.8')

leafNode = dict(boxstyle='round4',fc='0.8')

arrows_args = dict(arrowstyle='<-')

 

# 绘制带箭头的注解

def plotNode(nodeTxt,centerPt,parentPt,nodeType):

    createPlot.ax1.annotate(nodeTxt,xy=parentPt,

                            xycoords='axes fraction',

                            xytext=centerPt,textcoords='axes fraction',

                            va='center',ha='center',bbox=nodeType,

                            arrowprops=arrows_args)

 

def createPlot():

    # 创建一个新图形并清空绘图区

    fig = plt.figure(1,facecolor='white')

    fig.clf()

    createPlot.ax1 = plt.subplot(111,frameon=False)

    plotNode('a decision node',(0.5,0.1),(0.1,0.5),decisionNode)

    plotNode('a leaf node',(0.8,0.1),(0.3,0.8),leafNode)

    plt.show()

 

if __name__ == '__main__':

    createPlot()

  结果:

 

 5 . 构造决策树

  绘制一颗完整的树需要一些技巧。我们虽然有x,y坐标,但是如何放置所有的树节点却是个问题,我们必须知道有多少个叶子节点,以便可以正确确定x轴的长度我们还需要知道树有多少层,以便可以正确确定y轴的高度,这里我们定义两个新函数getNumLeafs() 和getTreeDepth(),来获取叶节点数目和树的层数,如下:

  获取叶子节点的数目和树的层数代码:

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

34

35

36

37

38

39

40

41

42

43

44

45

import matplotlib.pyplot as plt

 

def myTree():

    treeData = {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

    return treeData

 

# 获取叶子节点的数目

def getNumLeafs(myTree):

    # 初始化结点数

    numLeafs = 0

    firstSides = list(myTree.keys())

    # 找到输入的第一个元素,第一个关键词为划分数据集类别的标签

    firstStr = firstSides[0]

    secondDect = myTree[firstStr]

    for key in secondDect.keys():

        # 测试节点的数据类型是否为字典

        if type(secondDect[key]).__name__ == 'dict':

            numLeafs += getNumLeafs(secondDect[key])

        else:

            numLeafs +=1

    return numLeafs

 

# 获取树的层数

def getTreeDepth(myTree):

    maxDepth = 0

    firstSides = list(myTree.keys())

    firstStr = firstSides[0]

    secondDict = myTree[firstStr]

    for key in secondDict.keys():

        # 测试节点的数据类型是否为字典

        if type(secondDict[key]).__name__ == 'dict':

            thisDepth = 1 + getTreeDepth(secondDict[key])

        else:

            thisDepth = 1

        if thisDepth > maxDepth:

            maxDepth = thisDepth

 

    return maxDepth

 

if __name__ == '__main__':

    myData  = myTree()

    LeafNum = getNumLeafs(myData)

    TreeDepth = getTreeDepth(myData)

    print(LeafNum)

    print(TreeDepth)

  测试结果如下:

1

2

3

2

  下面我们将前面的方法组合在一起,得到决策树,此时我们需要更新画图的代码:

完整代码如下:

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

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

import matplotlib.pyplot as plt

 

# 定义文本框和箭头格式(树节点格式的常量)

decisionNode = dict(boxstyle='sawtooth',fc='0.8')

leafNode = dict(boxstyle='round4',fc='0.8')

arrows_args = dict(arrowstyle='<-')

 

# 绘制带箭头的注解

def plotNode(nodeTxt,centerPt,parentPt,nodeType):

    createPlot.ax1.annotate(nodeTxt,xy=parentPt,

                            xycoords='axes fraction',

                            xytext=centerPt,textcoords='axes fraction',

                            va='center',ha='center',bbox=nodeType,

                            arrowprops=arrows_args)

 

 

# 在父子节点间填充文本信息

def plotMidText(cntrPt,parentPt,txtString):

    xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0]

    yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1]

    createPlot.ax1.text(xMid,yMid,txtString, va="center", ha="center", rotation=30)

 

def plotTree(myTree,parentPt,nodeTxt):

    # 求出宽和高

    numLeafs = getNumLeafs(myData)

    depth = getTreeDepth(myData)

    firstStides = list(myTree.keys())

    firstStr = firstStides[0]

    # 按照叶子结点个数划分x轴

    cntrPt = (plotTree.xOff + (0.1 + float(numLeafs)) /2.0/plotTree.totalW,plotTree.yOff)

    plotMidText(cntrPt,parentPt,nodeTxt)

    plotNode(firstStr,cntrPt,parentPt,decisionNode)

    secondDict = myTree[firstStr]

    # y方向上的摆放位置 自上而下绘制,因此递减y值

    plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD

    for key in secondDict.keys():

        if type(secondDict[key]).__name__  == 'dict':

            plotTree(secondDict[key],cntrPt,str(key))

        else:

            plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW  # x方向计算结点坐标

            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)  # 绘制

            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))  # 添加文本信息

    plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD  # 下次重新调用时恢复y

 

def myTree():

    treeData = {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

    return treeData

 

# 获取叶子节点的数目

def getNumLeafs(myTree):

    # 初始化结点数

    numLeafs = 0

    firstSides = list(myTree.keys())

    # 找到输入的第一个元素,第一个关键词为划分数据集类别的标签

    firstStr = firstSides[0]

    secondDect = myTree[firstStr]

    for key in secondDect.keys():

        # 测试节点的数据类型是否为字典

        if type(secondDect[key]).__name__ == 'dict':

            numLeafs += getNumLeafs(secondDect[key])

        else:

            numLeafs +=1

    return numLeafs

 

# 获取树的层数

def getTreeDepth(myTree):

    maxDepth = 0

    firstSides = list(myTree.keys())

    firstStr = firstSides[0]

    secondDict = myTree[firstStr]

    for key in secondDict.keys():

        # 测试节点的数据类型是否为字典

        if type(secondDict[key]).__name__ == 'dict':

            thisDepth = 1 + getTreeDepth(secondDict[key])

        else:

            thisDepth = 1

        if thisDepth > maxDepth:

            maxDepth = thisDepth

 

    return maxDepth

 

# 主函数

def createPlot(inTree):

    # 创建一个新图形并清空绘图区

    fig = plt.figure(1,facecolor='white')

    fig.clf()

    axprops = dict(xticks=[], yticks=[])

    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)  # no ticks

    # createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses

    plotTree.totalW = float(getNumLeafs(inTree))

    plotTree.totalD = float(getTreeDepth(inTree))

    plotTree.xOff = -0.5 / plotTree.totalW

    plotTree.yOff = 1.0

    plotTree(inTree, (0.5, 1.0), '')

    plt.show()

 

 

if __name__ == '__main__':

    myData  = myTree()

    myData['no surfacing'][3] = 'maybe'

    print(myData)

    # LeafNum = getNumLeafs(myData)

    # TreeDepth = getTreeDepth(myData)

    # print(LeafNum)

    # print(TreeDepth)

    createPlot(myData)

  

6 . 测试算法:使用决策树执行分类

  依靠训练数据构造了决策树之后,我们可以将它用于实际数据的分类,在执行数据分类时,需要决策树以及用于构造树的标签向量。然后,程序比较测试数据与决策树上的数值,递归执行该过程直到进入叶子节点,最后将测试数据定义为叶子节点所属的类型。

 使用决策树的分类函数代码:

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

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

from math import log

import operator

 

# 计算数据的熵(entropy)

def calcShannonRnt(dataSet):

    # 数据条数,计算数据集中实例的总数

    numEntries = len(dataSet)

    labelCounts = {}

    for featVec in dataSet:

        # 每行数据的最后一个类别(也就是标签)

        currentLable = featVec[-1]

        if currentLable not in labelCounts.keys():

            labelCounts[currentLable] = 0

        # 统计有多少个类以及每个类的数量

        labelCounts[currentLable]  += 1

    shannonEnt = 0.0

    for key in labelCounts:

        # 计算单个类的熵值

        prob = float(labelCounts[key]) / numEntries

        # 累加每个类的熵值

        shannonEnt -= prob * log(prob , 2)

    return shannonEnt

 

# 创建数据集

def createDataSet():

    dataSet = [[1,1,'yes'],

               [1,1,'yes'],

               [1,0,'no'],

               [0,1,'no'],

               [0,1,'no']]

    labels = ['no surfacing','flippers']

    # dataSet = [['Long', 'Think', 'male'],

    #            ['Short', 'Think', 'male'],

    #            ['Short', 'Think', 'male'],

    #            ['Long', 'Thin', 'female'],

    #            ['Short', 'Thin', 'female'],

    #            ['Short', 'Think', 'female'],

    #            ['Long', 'Think', 'female'],

    #            ['Long', 'Think', 'female']]

    # labels = ['hair', 'voice']

    return dataSet,labels

 

# 按照给定特征划分数据集

def splitDataSet(dataSet,axis,value):

    '''

 

    :param dataSet: 待划分的数据集

    :param axis: 划分数据集的特征

    :param value: 特征的返回值

    :return:

    '''

    retDataSet = []

    for featVec in dataSet:

        # 如果发现符合要求的特征,将其添加到新创建的列表中

        if featVec[axis] == value:

            reduceFeatVec = featVec[:axis]

            reduceFeatVec.extend(featVec[axis+1:])

            retDataSet.append(reduceFeatVec)

 

    return retDataSet

 

# 选择最好的数据集划分方式

def chooseBestFeatureTpSplit(dataSet):

    '''

        此函数中调用的数据满足以下要求

        1,数据必须是一种由列表元素组成的列表,而且所有列表元素都要具有相同的数据长度

        2,数据的最后一列或者实例的最后一个元素是当前实例的类别标签

    :param dataSet:

    :return:

    '''

    numFeatures = len(dataSet[0]) - 1

    # 原始的熵

    baseEntropy = calcShannonRnt(dataSet)

    bestInfoGain = 0.0

    bestFeature = -1

    for i 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 * calcShannonRnt(subDataSet)

        infoGain = baseEntropy - newEntropy

        if (infoGain > bestInfoGain):

            # 计算最好的信息增益,信息增益越大,区分样本的能力越强,根据代表性

            bestInfoGain = infoGain

            bestFeature = i

    return bestFeature

 

# 按照分类后类别数量排序

def majorityCnt(classList):

    classCount = {}

    for vote in classList:

        if vote not in classCount.keys():

            classCount[vote] = 0

        classCount[vote] += 1

    sortedClassCount = sorted(classCount.items(),

                              key=operator.itemgetter(1),reverse=True)

    return sortedClassCount[0][0]

 

# 创建树的函数代码

def createTree(dataSet,labels):

    '''

 

        :param dataSet:  输入的数据集

        :param labels:  标签列表(包含了数据集中所有特征的标签)

        :return:

        '''

    # classList 包含了数据集中所有类的标签

    classList = [example[-1] for example in dataSet]

    # 类别完全相同则停止继续划分

    if classList.count(classList[0]) == len(classList):

        return classList[0]

    # 遍历完所有特征时返回出现次数最多的

    if len(dataSet[0])  == 1:

        return majorityCnt(classList)

    bestFeat = chooseBestFeatureTpSplit(dataSet)

    bestFeatLabel = labels[bestFeat]

    # 字典myTree存储了树的所有信息,这对于后面绘制树形图很重要

    myTree = {bestFeatLabel:{}}

    del(labels[bestFeat])

    # 得到列表包含的所有属性值

    featValues = [example[bestFeat] for example in dataSet]

    uniqueVals = set(featValues)

    for value in uniqueVals:

        subLabels =labels[:]

        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),

                                                  subLabels)

 

    return myTree

 

# 使用决策树的分类函数

def classify(inputTree ,featLabels,testVec):

    firstSides = list(inputTree.keys())

    firstStr = firstSides[0]

    secondDict = inputTree[firstStr]

    # print('featLables:',featLabels)

    featIndex = featLabels.index(firstStr)

    for key in secondDict.keys():

        # 若该特征值等于当前key,yes往下走

        if testVec[featIndex]  == key:

            if type(secondDict[key]).__name__ == 'dict':

                classLabel = classify(secondDict[key],featLabels,testVec)

            else:

                classLabel = secondDict[key]

    return classLabel

#使用决策树来分类

def classify11(inputTree,featLabels,testVec):

    #python3.X

    firstSides = list(inputTree.keys())

    firstStr = firstSides[0]  # 找到输入的第一个元素

    # python3.X

    secondDict=inputTree[firstStr]  #baocun在secondDict中

    featIndex=featLabels.index(firstStr)  #建立索引

    for key in secondDict.keys():

        if testVec[featIndex]==key: #若该特征值等于当前key,yes往下走

            if type(secondDict[key]).__name__=='dict':# 若为树结构

                classLabel=classify(secondDict[key],featLabels,testVec) #递归调用

            else:  classLabel=secondDict[key]#为叶子结点,赋予label值

    return classLabel #分类结果

 

def retrieveTree(i):

    listOfTrees = [

        {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}},

        {'no surfacing': {0: 'no', 1: {'flippers': {0: {'head':{0:'no', 1: 'yes'}},1:'no'}}}}

    ]

    return listOfTrees[i]

  测试结果如下:

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

if __name__ == '__main__':

    myData,labels = createDataSet()

    # print(myData)

    # print(labels)

 

    myTree = createTree(myData,labels)

 

    # print(myTree0)

    myTree1 = {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

    # print(type(myTree1))

    # myTree = retrieveTree(0)

    myData, labels = createDataSet()

    print(myData)

    print(labels)

    res1 = classify(myTree,labels,[1,1])

    print(res1)

    res2 = classify(myTree,labels,[1,0])

    print(res2)

 

 

'''

[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]

['no surfacing', 'flippers']

yes

no

'''

  

 7. 使用算法:决策树的存储

  构造决策树是很耗时的任务,即使处理很小的数据集,如前面的样本数据,也要花费几秒的时间,如果数据集很大,将会耗费很多计算时间,然而使用创建好的决策树解决问题,则可以很快完成。所以为了节省时间,最好能够在每次执行分类时调用已经构造好的决策树,为了解决这个问题,需要使用pickle序列化对象。序列化对象可以在磁盘上保存对象,并在需要的时候读取出来,任何对象都可以执行序列化操作,字典对象也不例外。

  使用pickle模块存储决策树代码:

1

2

3

4

5

6

7

8

9

10

11

# 使用pickle模块存储决策树

def storeTree(inputTree,filename):

    import pickle

    fw =open(filename,'wb')

    pickle.dump(inputTree,fw)

    fw.close()

 

def grabTree(filename):

    import pickle

    fr = open(filename,'rb')

    return pickle.load(fr)

  

 通过上面的代码,我们可以将分类器存储在硬盘上,而不是每次对数据分类时重新学习一边,这就是决策树的优点之一。

  验证上面代码,测试结果如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

if __name__ == '__main__':

    myData,labels = createDataSet()

    myTree = createTree(myData,labels)

    print(myTree)

    print(type(myTree))

    myTree1 = {'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

    storeTree(myTree1,'classifierStorage.txt')

    res = grabTree('classifierStorage.txt')

    print(res)

 

'''

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

<class 'dict'>

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

'''

  

8. 示例:使用决策树预测隐形眼镜类型

  此处通过一个例子学习决策树如何预测患者需要佩戴的隐形眼镜类型,使用小数据集,我们就可以利用决策树学到很多知识:眼科医生是如何判断患者需要佩戴的镜片类型;

 (隐形眼镜数据集时非常著名的数据集,它包含很多患者眼部状况的观察条件以及医生推荐的隐形眼镜类型,隐形眼镜类型包括硬材质,软材质以及不适合佩戴隐形眼镜。数据来源于UCI数据库,为了更容易显示数据,机器学习实战将数据做了简单的更改,数据存储在文本文件中)

  lenses.txt内容可以去我的GitHub上拿(GitHub地址:https://github.com/LeBron-Jian/MachineLearningNote)

  程序如下:

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

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

# _*_coding:utf-8_*_

import operator

from math import log

import  matplotlib.pyplot as  plt

 

# 计算香农熵  度量数据集无序程度

def calcShannonEnt(dataSet):

    numEntries = len(dataSet)

    labelCounts = {}

    for fearVec in dataSet:

        currentLabel = fearVec[-1]

        if currentLabel not in labelCounts.keys():

            labelCounts[currentLabel] = 0

        labelCounts[currentLabel] +=1

    shannonEnt = 0.0

    for key in labelCounts:

        prob = float(labelCounts[key])/numEntries

        shannonEnt -= prob*log(prob,2)

    return shannonEnt

 

# 划分数据集

def  splitDataSet(dataSet,axis,value):

    retDataSet = []

    for featVec in dataSet:

        if featVec[axis] == value:

            reducesFeatVec = featVec[:axis]

            reducesFeatVec.extend(featVec[axis+1:])

            retDataSet.append(reducesFeatVec)

    return retDataSet

 

# 选择最好的数据集划分方式

def chooseBestFeatureTpSplit(dataSet):

    '''

        此函数中调用的数据满足以下要求

        1,数据必须是一种由列表元素组成的列表,而且所有列表元素都要具有相同的数据长度

        2,数据的最后一列或者实例的最后一个元素是当前实例的类别标签

    :param dataSet:

    :return:

    '''

    numFeatures = len(dataSet[0]) - 1

    # 原始的熵

    baseEntropy = calcShannonEnt(dataSet)

    bestInfoGain = 0.0

    bestFeature = -1

    for i 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)

        infoGain = baseEntropy - newEntropy

        if (infoGain > bestInfoGain):

            # 计算最好的信息增益,信息增益越大,区分样本的能力越强,根据代表性

            bestInfoGain = infoGain

            bestFeature = i

    return bestFeature

 

# 按照分类后类别数量排序

def majorityCnt(classList):

    classCount = {}

    for vote in classList:

        if vote not in classCount.keys():

            classCount[vote] = 0

        classCount[vote] += 1

    sortedClassCount = sorted(classCount.items(),

                              key=operator.itemgetter(1),reverse=True)

    return sortedClassCount[0][0]

 

# 创建树的函数代码

def createTree(dataSet,labels):

    '''

 

        :param dataSet:  输入的数据集

        :param labels:  标签列表(包含了数据集中所有特征的标签)

        :return:

        '''

    # classList 包含了数据集中所有类的标签

    classList = [example[-1] for example in dataSet]

    # 类别完全相同则停止继续划分

    if classList.count(classList[0]) == len(classList):

        return classList[0]

    # 遍历完所有特征时返回出现次数最多的

    if len(dataSet[0])  == 1:

        return majorityCnt(classList)

    bestFeat = chooseBestFeatureTpSplit(dataSet)

    bestFeatLabel = labels[bestFeat]

    # 字典myTree存储了树的所有信息,这对于后面绘制树形图很重要

    myTree = {bestFeatLabel:{}}

    del(labels[bestFeat])

    # 得到列表包含的所有属性值

    featValues = [example[bestFeat] for example in dataSet]

    uniqueVals = set(featValues)

    for value in uniqueVals:

        subLabels =labels[:]

        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),

                                                  subLabels)

 

    return myTree

 

# 获取叶子节点的数目

def getNumLeafs(myTree):

    # 初始化结点数

    numLeafs = 0

    firstSides = list(myTree.keys())

    # 找到输入的第一个元素,第一个关键词为划分数据集类别的标签

    firstStr = firstSides[0]

    secondDect = myTree[firstStr]

    for key in secondDect.keys():

        # 测试节点的数据类型是否为字典

        if type(secondDect[key]).__name__ == 'dict':

            numLeafs += getNumLeafs(secondDect[key])

        else:

            numLeafs +=1

    return numLeafs

 

# 获取树的层数

def getTreeDepth(myTree):

    maxDepth = 0

    firstSides = list(myTree.keys())

    firstStr = firstSides[0]

    secondDict = myTree[firstStr]

    for key in secondDict.keys():

        # 测试节点的数据类型是否为字典

        if type(secondDict[key]).__name__ == 'dict':

            thisDepth = 1 + getTreeDepth(secondDict[key])

        else:

            thisDepth = 1

        if thisDepth > maxDepth:

            maxDepth = thisDepth

 

    return maxDepth

 

# 定义文本框和箭头格式(树节点格式的常量)

decisionNode = dict(boxstyle='sawtooth',fc='0.8')

leafNode = dict(boxstyle='round4',fc='0.8')

arrows_args = dict(arrowstyle='<-')

 

# 绘制带箭头的注解

def plotNode(nodeTxt,centerPt,parentPt,nodeType):

    createPlot.ax1.annotate(nodeTxt,xy=parentPt,

                            xycoords='axes fraction',

                            xytext=centerPt,textcoords='axes fraction',

                            va='center',ha='center',bbox=nodeType,

                            arrowprops=arrows_args)

 

 

# 在父子节点间填充文本信息

def plotMidText(cntrPt,parentPt,txtString):

    xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0]

    yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1]

    createPlot.ax1.text(xMid,yMid,txtString, va="center", ha="center", rotation=30)

 

 

def plotTree(myTree,parentPt,nodeTxt):

    # 求出宽和高

    numLeafs = getNumLeafs(myData)

    depth = getTreeDepth(myData)

    firstStides = list(myTree.keys())

    firstStr = firstStides[0]

    # 按照叶子结点个数划分x轴

    cntrPt = (plotTree.xOff + (0.1 + float(numLeafs)) /2.0/plotTree.totalW,plotTree.yOff)

    plotMidText(cntrPt,parentPt,nodeTxt)

    plotNode(firstStr,cntrPt,parentPt,decisionNode)

    secondDict = myTree[firstStr]

    # y方向上的摆放位置 自上而下绘制,因此递减y值

    plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD

    for key in secondDict.keys():

        if type(secondDict[key]).__name__  == 'dict':

            plotTree(secondDict[key],cntrPt,str(key))

        else:

            plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW  # x方向计算结点坐标

            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)  # 绘制

            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))  # 添加文本信息

    plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD  # 下次重新调用时恢复y

 

# 主函数

def createPlot(inTree):

    # 创建一个新图形并清空绘图区

    fig = plt.figure(1,facecolor='white')

    fig.clf()

    axprops = dict(xticks=[], yticks=[])

    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)  # no ticks

    # createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses

    plotTree.totalW = float(getNumLeafs(inTree))

    plotTree.totalD = float(getTreeDepth(inTree))

    plotTree.xOff = -0.5 / plotTree.totalW

    plotTree.yOff = 1.0

    plotTree(inTree, (0.5, 1.0), '')

    plt.show()

 

if __name__ == '__main__':

    fr = open('lenses.txt')

    lenses = [inst.strip().split('\t')  for inst in fr.readlines()]

    lensesLabels = ['age','prescript','astigmatic','tearRate']

    print(lenses)

    myData = createTree(lenses, lensesLabels)

    print(myData)

    createPlot(myData)

  结果如下:

 

  上图所示的决策树非常好的匹配了实验数据,然而这些选项可能太多了。我们将这种问题称为过度匹配,为了减少过度匹配问题,我们可以裁剪决策树,去掉一些不必要的叶子节点,如果叶子节点只能增加少许信息,则可以删除该节点,将它并入到其他叶子节点中。

  ID3算法无法直接处理数值型数据,尽管我们可以通过量化的方法将数值型数据转化为标称型数值,但是如果存在太多的特征划分,ID3算法仍然会面临其他问题。

9 总结

  决策树分类器就像带有终止块的流程图,终止块表示分类结果。开始处理数据集时,我们首先需要测量集合中数据的不一致性,也就是熵,然后寻找最优方案划分数据集,直到数据集中的所有数据属于同一分类。ID3算法可以用于划分标称型数据集。构建决策树时,我们通常采取递归的方法将数据集转化为决策树。一般我们并不构造新的数据结构,而是使用字典存储树节点信息。

参考文献:机器学习实战

 

更多精彩内容请访问FlyAI-AI竞赛服务平台;为AI开发者提供数据竞赛并支持GPU离线训练的一站式服务平台;每周免费提供项目开源算法样例,支持算法能力变现以及快速的迭代算法模型。

挑战者,都在FlyAI!!!

机器学习笔记(4)——ID3决策树算法及其Python实现 决策树是一种基于树结构来进行决策的分类算法,我们希望从给定的训练数据集学得一个模型(即决策树),用该模型对新样本分类。决策树可以非常直观展现分类的过程和结果,一旦模型构建成功,对新样本的分类效率也相当高。 最经典的决策树算法有ID3、C4.5、CART,其中ID3算法是最早被提出的,它可以处理离散属性样本的分类,C4.5和CART算法则可以处理更加复杂的分类问题,本文重点介绍ID3算法。 举个... 阅读详情

相关推荐

机器学习笔记15——决策树(DT)、ID3算法、C4.5算法原理以及python实现案例

决策树1、概述2、决策树模型3、决策树学习4、决策树构建-三步骤4.1 特征选择4.1.1 熵(entropy)4.1.2 条件熵(entropy)4.1.3 信息增益总结 1、概述 决策树(decision tree): 是一种基本的分类与回归方法,此处主要讨论分类的决策树。 在分类问题中,表示基于特征对实例进行分类的过程,可以认为是if-then的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。 2、决策树模型 定义: 分类决策树模型是一种描述对实例进行分类的树形结构。 决策树由结点和有向边组

weixin_45666566的博客 1万+

机器学习之决策树——ID3算法简介

决策树简介     在相亲过程中,女方会根据男方的一些特征(年龄、长相、收入、是否公务员)来决策自己的行为(见还是不见)。如果利用kNN算法来预测女方是否去见相亲对象的话,需要保存样本数据,且需要将年龄等这些特征先转换为对应的数值,同时需要对所有数据计算距离值,非常耗时。因此,我们可以利用概率测量方法(决策树)来处理分类问题。 决策树定义     决策树的基本组成部分:决策节

westenboy的专栏 2001

机器学习算法的Python实现 (2):ID3决策树

本文数据参照 机器学习-周志华 一书中的决策树一章。可作为此章课后习题3的答案 代码则参照《机器学习实战》一书的内容,并做了一些修改。 本文使用的Python库包括 numpypandasmathoperatormatplotlib 本文所用的数据如下:                   Idx 色泽 根蒂 敲声 纹理 脐部 触感

Will Lin的博客 2万+

ID3决策树及Python实现(详细)

函数作用:指定特征列的索引index,对样本集中特征值==value的具体样本sample划分出来,组成一个dataSet的样本子集retDataSet(并将这些样本中的这些value去掉,去掉sample[index]的目的是因为下轮比较各特征信息增益Gain从而获得最大信息增益bestGain(决定最优划分特征bestFeature)时,不能将已选出的最优特征放在比较队列中)ID3决策树算法就是。信息熵,是度量样本集合纯度的一种指标,Ent(D)的值越小,则样本集D的纯度越高;

weixin_66845445的博客 1万+

决策树之ID3算法以及决策树挑西瓜代码实现

决策树之ID3算法及西瓜案例实现一、决策树1. 决策树的基本认识2. 决策树的构造过程(1)特征选择(2)决策树的生成(3)决策树的裁剪决策树的优缺点二、ID3算法原理1. 信息增益二级目录三级目录 一、决策树 1. 决策树的基本认识 决策树是一种依托决策而建立起来的一种树。在机器学习中,决策树是一种预测模型,代表的是一种对象属性与对象值之间的一种映射关系,每一个节点代表某个对象,树中的每一个分叉路径代表某个可能的属性值,而每一个叶子节点则对应从根节点到该叶子节点所经历的路径所表示的对象的值。决策树仅有单一

One2332x的博客 7460

【机器学习】九大决策树模型总结

这篇博客深入探讨了九种不同的决策树模型,每种模型都拥有其独特的特点和应用场景。我将带您了解一些经典的模型,让你理解它们的工作原理,并在实践中灵活运用它们。

chunyang的博客 3611

机器学习算法——决策树算法(ID3算法划分数据集,基于香农熵的python底层实现)

决策树算法是一种非参数的决策算法,它根据数据的不同特征进行多层次的分类和判断,最终决策出所需要预测的结果。它既可以解决分类算法,也可以解决回归问题,具有很好的解释能力。 决策树就如上图所示,决策树算法能够读取数据集合,构建类似于上图的决策树。 决策树的一个重要任务是为了厘清数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,在这些机器根据数据集创建规则时,就是机器学习的学习过程。 传统的专家系统中经常使用决策树,而且决策树给出的结果往往可以匹敌在当前领域具有几十年工作经验的

weixin_45709671的博客 2175

西瓜书中ID3决策树的实现。

在上一篇文章基于信息增益的ID3决策树介绍。中介绍了基本的决策树概念和基于信息增益的ID3决策树的计算。这篇文章中介绍一下如何使用Python实现一个ID3决策树,其中主要的代码来自于机器学习实战一书中,本人对其做了一些改动,增加了一些内容。决策树的伪代码。决策树的生成可以使用一个递归来实现,在西瓜书中给出了决策树的伪代码: 输入:训练集D=(x1,y1),(x2,y2),...,(xm,ym)

繁城落叶 1万+

分类——ID3算法

分类——ID3算法

qq_22749197的博客 1900

数据中心机房建设标准及依据

深刻理解、尊重并恪守这些依据,是我们以科学、经济、可靠的方式,为蓬勃发展的数字经济筑牢基石的唯一路径。让我们共同努力,以标准为依据,以匠心为精神,建造更多世界一流的高效能、高可用、高智能的绿色数据中心,共同赋能数字中国的宏伟事业,支撑中华民族在数字时代的伟大复兴征程中稳步前行!今天我将力图以宏大的视角和深入的剖析,系统性地阐述数据中心机房建设所应遵循的多层次、多维度的标准依据,揭示这些依据背后的深层逻辑与时代使命,并探讨其如何共同作用于一座世界级数据中心的诞生。,以支持万兆乃至更高速率的传输。

qq_39980997的博客 1139

AI Agent Harness Engineering 创业PMF验证工具:用户满意度+留存率+业务指标监测表

在AI Agent创业的浪潮中,我们看到了无数产品如雨后春笋般涌现,但最终能够真正找到PMF并存活下来的却寥寥无几。根据CB Insights的数据,创业公司失败的首要原因(占42%)是"没有市场需求"——换句话说,就是没有找到PMF。AI Agent产品由于其特殊性(交互范式的变革、技术复杂度高、用户期望管理困难等),PMF验证变得更加具有挑战性。传统的SaaS产品PMF验证框架虽然有参考价值,但无法完全适用于AI Agent这种新型产品形态。

AI智能探索者的博客 331

决策树算法ID3算法(Python3实现)

目录 1、数据集准备 2、使用ID3算法递归构建决策树并使用决策树执行分类 2.1 ID3算法概述 2.2 递归终止的条件: 2.3 代码实现如下: 3、Matplotlib实现决策树可视化 4、决策树的存储与读取 5、决策树优点和缺点 1、数据集准备 ...

分享实践与思考 4万+

衡山派Luban-Lite系统SDIO无线网络配置实战:以D133CBV-QFN88开发板与RTL8189模组为例

本文详细介绍了在衡山派D133CBV-QFN88开发板上,为Luban-Lite系统配置SDIO无线网络的完整实战流程。通过配置SDMC接口、RTL8189 Wi-Fi驱动、lwIP协议栈及内核参数,并解决编译分区问题,最终实现Wi-Fi网络连接与测试,为嵌入式设备联网提供清晰指导。

weixin_35677363的博客 267

【机器学习实战】3、决策树

(声明:本文内容来自机器学习实战和统计学习方法,是两者的整合,并非来自单个书籍)决策树(decision tree):是一种基本的分类与回归方法,此处主要讨论分类的决策树。在分类问题中,表示基于特征对实例进行分类的过程,可以认为是if-then的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。决策树通常有三个步骤:特征选择、决策树的生成、决策树的修剪。用决策树分类:从根节点开始,对实例的某一特征进行测试,根据测试结果将实例分配到其子节点,此时每个子节点对应着该特征的一个取值,如此递归的对实例进行测

呆呆的猫的博客 37万+

从零开始掌握机械臂运动控制:原理、建模与实践

本文系统介绍了机械臂运动控制的核心原理与实践方法。从运动学与动力学建模的基础理论出发,详细解析了经典PID控制与现代MPC(模型预测控制)算法的应用,并通过ROS与MATLAB仿真案例,指导读者从零开始实现机械臂的轨迹规划与精准控制,为工业自动化应用提供完整的技术路径。

weixin_29099217的博客 565

DeepSpeed Zero3 Offload实战:从GCC版本检查到环境配置的完整避坑指南

本文是一份针对DeepSpeed Zero3 Offload部署的完整避坑指南。针对训练中常见的`AttributeError: ‘DeepSpeedCPUAdam‘ object has no attribute ‘ds_opt_adam`报错,文章深入剖析了其根源在于GCC编译器版本过低。指南提供了从系统GCC版本深度探查、安全启用高版本GCC环境,到DeepSpeed一致性安装验证的全链路实战方案,旨在帮助用户构建稳定的大模型训练环境。

weixin_29070721的博客 474

在西瓜数据集上用Python实现ID3决策树算法完整代码

在西瓜数据集上用Python实现ID3决策树算法完整代码。

myec_lipse的博客 9683

Spark Catalog详解

旁边的实习生说:我想要用spark代码中对hive库中的内部表和外部表进行删除(包括数据),咋感觉网上搜了一圈都找不到解决方案啊,spark这么鸡肋吗?我:你应该静下心来好好把spark基础知识进行全面学习。实习生:难道spark有这功能,而我没有学习过?咋弄啊?我:学习一下Spark Catalog。实习生:啥是Catalog啊?Spark Catalog是Spark SQL中的一个元数据管理组件,它提供了一个集中化的存储和查询表、视图和函数的位置。

SunnyRivers 1814

决策树模型 - (ID3算法、C4.5算法) - Python代码实现

目录 算法简介 信息熵(Entropy) 信息增益(Information gain) - ID3算法 信息增益率(gain ratio) - C4.5算法 源数据 代码实现 - ID3算法 代码实现 - C4.5算法 画决策树代码-treePlotter 算法简介 决策数(Decision Tree)在机器学习中也是比较常见的一种算法,属于监督学习中的一种。其中ID3算法是以...

黄饱饱 1万+

农业无人机:无人机图像处理_(8).遥感图像处理与分析

遥感图像处理与分析在农业无人机技术中具有重要作用,通过图像预处理、特征提取、分类与分割、目标检测等技术,可以实现对农田的精细管理、作物监测、病虫害预测等应用。本节详细介绍了这些技术的基本概念、常用方法和具体实现,希望对您在农业无人机图像处理方面的学习和应用有所帮助。

zhubeibei168的博客 1336

WPS Office JS宏实现文件和数据的上传及下载

WPS Office JS宏下载、上传文件;通过加载项实现数据的获取和上传。

学点笔记 4300

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

机器学习中,决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映射关系。树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个叶结点则对应从根节点到该叶节点所经历的路径所表示的对象的值。决策树仅有单一输出,若欲有复数输出,可以建立独立的决策树以处理不同输出。 数据挖掘中决策树是一种经常要用到的技术,可以用于分析数据,同样也可以用来作预测。

moxigandashu的博客 2万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

经典决策树算法(ID3、C4.5、CART)原理以及Python实现

 决策树(Decision Tree),是每个分支都通过条件判断进行划分的树,是解决分类和回归问题的一种机器学习算法,其核心是一个贪心算法,它采用自顶向下的递归方法构建决策树。 决策树模型是一种对实例进行分类的树,由节点(node,由圆框表示)和有向边(directed edge,由方框表示)组成,其中节点分为内部节点(internal node)和叶子节点(leaf node),内部节点表示一个属性或特征,叶子节点表示一个类。 决策树可以被看作是一个if-then规则的集合:由决策树的根节点到叶子节点的每

AndersonHuang的博客 2376

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

机器学习决策树ID3

在机器学习中,决策树是一种预测模型,代表的是一种对象属性与对象值之间的一种映射关系...

caechen的博客 3248

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

ID3决策树 Python实现 + sklearn库决策树模型的应用

本文介绍机器学习中决策树算法的python实现过程 (1)亲手实现Python ID3决策树经典算法 (2)利用sklearn库实现决策树算法

YJY131248的博客 3516

机器学习——决策树算法

文章目录一、决策树介绍二、利用信息增益选择最优划分属性三、ID3代码实现 一、决策树介绍 决策树是一种基于树结构来进行决策的分类算法,我们希望从给定的训练数据集学得一个模型(即决策树),用该模型对新样本分类。决策树可以非常直观展现分类的过程和结果,一旦模型构建成功,对新样本的分类效率也相当高。 最经典的决策树算法有ID3、C4.5、CART,其中ID3算法是最早被提出的,它可以处理离散属性样本的分类,C4.5和CART算法则可以处理更加复杂的分类问题。使用算法ID3, C4.5和C5.0生成树算法使

羚漆的博客 5396
上一篇: 最全面的贝叶斯机器学习入门教程
下一篇: FlyAI小课堂:python机器学习笔记:深入学习决策树算法原理
iFlyAI
博客等级 码龄9年 238粉丝 134原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值