- 转载请注明作者和出处:http://blog.csdn.net/u011475210
- 代码地址:https://github.com/WordZzzz/ML/tree/master/Ch03
- 操作系统:WINDOWS 10
- 软件版本:python-3.6.2-amd64
- 编 者:WordZzzz
前言:
本渣渣(WordZzzz直接被舍友叫成了“我的渣”,所以以后我在博客中就以此自居了!),最近在学习Peter Harrington的Machine Learning in Action,一边看书一边用Python3.6实现课本中的算法(原书中使用的是Python2.x)。好记性不如烂笔头,奈何本渣渣连烂笔头都买不起,所以就来这不费笔墨的地方费尽心思写博客。本渣渣记性不是一般的差,在此记下每个算法的学习要点及Python代码实现,一方面方便自己以后复习,另一方面贴出来和大家一起学习,共同进步~~~
注意:python3.x与python2.x的部分函数库有较大差异,针对这个问题,本渣渣会将代码版本升级中遇到的问题在每篇博文的最后列出来,并加以解释说明,帮助大家区分理解。
原著代码(python2.x)地址:https://www.manning.com/books/machine-learning-in-action
本渣渣代码(python3.x)地址:https://github.com/WordZzzz/ML/tree/master/Ch03
博客中的代码都会在本渣渣的GitHub上贴出,欢迎Watch、Star、Fork。
一、算法介绍:
上一个算法介绍的是k-近邻算法,它可以完成很多分类任务,但是它最大的缺点就是无法给出数据的内在含义,决策树的主要优势就在于数据形式非常容易理解。决策树的一个重要任务就是为了理解数据中所蕴含的知识信息,所以决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,这些机器根据数据集创建规则的过程就是机器学习的过程。专家系统中经常食用决策树,而且决策树给出结果往往可以匹敌在当前领域具有几十年工作经验的人类专家。流程图形式的决策树如下:
决策树:
- 优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
- 缺点:可能会产生过度匹配问题。
- 适用数据类型:数值型和标称型。
在构造决策树之前,我们需要解决一个问题:当前数据集上哪个特征在划分数据分类时起决定性作用。为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征。完成测试之后,原始数据集就被划分为几个数据子集。这些数据子集会分布在第一个决策点的所有分支上,如果某个分支下的数据属于同一类型,则无需进一步对数据进行分割,如果数据子集内的数据不属于同一类型,则需要重复划分数据子集的过程。
决策树的一般流程
- 收集数据:可以使用任何方法。
- 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化。
- 分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否否和预测。
- 训练算法:构造树的数据结构。
- 测试算法:使用经验树计算错误率。
- 使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好的理解数据的内在含义。
二、代码实现与详解:
一些决策树算法采用二分法划分数据,书里面采用的是ID3算法划分数据集,该算法处理如何划分数据集,何时停止划分数据集。
前面提了个问题,这么多特征,我们每次之选一个特征值进行划分,那这个特征要如何选择呢?下面将进行讲解。
信息增益:
划分数据集的最大原则是:将无序的数据变得更加有序。组织杂乱无章数据的一种方法是使用信息论度量信息,信息论是量化处理信息的分支科学。我们可以在划分数据之前或者之后使用信息论量化信息的内容。
在划分数据集之前之后信息发生的变化称之为信息增益,获得信息增益最高的特征就是最好的选择。几何信息的度量方式称为香农熵或者简称为熵(其他学科也有熵这个定义,意思都差不多),这个名字来源于信息论之父克劳德·香农。
熵定义为信息的期望值,如果待分类的事物可能划分在多类分类中,则符号$x_i$的信息论定义为:
$$l(x_i) = -log_2p(x_i)$$
其中$p(x_i)$是选择该分类的概率。
为了计算熵,我们需要计算所有类别可能值包含的信息期望值,通过下面的公式得到:
$$H = - \sum{^n_{i-1}p(x_i)log_2p(x_i)}$$
其中n是分类的数目。创建trees.py文件,我们来使用python计算信息熵。
为了后续测试方便,我们先编写createDataSet()函数,创建数据集。
代码实现:
1 | # -*- coding: UTF-8 -*- |
结果输出:
1 | import trees |
接下来编写calcShannonEnt(dataSet)函数,计算给定数据集的香农熵。
代码实现:
1 | def calcShannonEnt(dataSet): |
结果输出:
1 | trees.calcShannonEnt(myDat) |
熵越高,则混合的数据也越多,我们可以在数据集中添加更多的分类,观察熵是如何变化的:
1 | 0][-1]='maybe' myDat[ |
划分数据集:
分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以判断当前是否正确划分了数据集。我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。
编写splitDataSet(dataSet, axis, value)函数,按照给定特征划分数据集。
代码实现:
1 | def splitDataSet(dataSet, axis, value): |
输出结果:
1 | reload(trees) |
注意事项:
- Python语言不用考虑内存分配问题,在函数中传递的是列表的引用,在函数内部对列表对象的更改,将会影响该列表对象的整个生存周期。为了消除这个不良影响,我们需要在函数的开始创建一个新列表。
- 代码中使用了extend()和append()来抽取符合要求的元素,这两个方法功能类似,但是在处理多个列表时,这两个方法的处理结果是完全不同的。
append
1 | 1,2,3] a=[ |
extend
1 | 1,2,3] a=[ |
编写chooseBestFeatureToSplit(dataSet)函数, 选择最好的数据集划分方式。该函数调用的数据需要满足一定的要求:第一个要求是,数据必须是一种由列表元素组成的列表,而且所有的列表元素都要具有相同的数据长度;第二个要求是,数据的最后一列或者每个实例的最后一个元素是当前实例的类别标签。
代码实现:
1 | def chooseBestFeatureToSplit(dataSet): |
note:从列表中创建集合是Python语言得到列表中唯一元素值的最快方法。
输出结果:
1 | reload(trees) |
递归构建决策树:
从数据集构造决策树算法的工作原理如下:得到原始数据,然后基于最好的属性值划分数据集。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,可以再次划分数据(递归)。
递归结束的条件:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。如果所欲实例都具有相同的分类,则得到一个叶子节点或者终止块。任何到达叶子节点的数据必须属于叶子节点的分类,如下图:
如果数据集已经处理了所有的属性,但是类标签依然不是唯一的,此时我们需要决定如何定义该叶子节点,在这种情况下,书中采用的是多数表决的方法决定该叶子节点的分类。
编写majorityCnt(classList)函数,进行多数表决,这里和k-近邻算法里面的classify0部分的投票表决代码非常类似。
代码实现:
1 | def majorityCnt(classList): |
编写createTree(dataSet, labels)函数,创建树
代码实现:
1 | def createTree(dataSet, labels): |
输出结果:
1 | reload(trees) |
变量myTree包含了很多代表树结构信息的嵌套字典,从左边开始,第一个关键字no surfacing是第一个划分数据集的特征名称,该关键字的值也是另一个数据字典。第二个关键字是no surfacing特征划分的数据集,这些关键字的值是no surfacing节点的子节点。这些值可能是类标签,也可能是另一个数据字典,如果值是标签,则该子节点是叶子结点;如果是另一个数据字典,则子节点是一个判断节点,这种格式结构不断重复就构成了整棵树。
系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~
完的汪(∪。∪)。。。zzz