Rongxuanhong's Blog

脚踏实地地做好自己


  • Home

  • About

  • Tags

  • Categories

  • Archives

决策树的简要理论及其部分python实现

Posted on 2019-03-01 | Comments:

前言

决策树是机器学习中非常重要的一类监督学习方法。它可以对数据进行分类和回归学习。我们来看一下一个简单的决策树模型,如下图所示。

注意:本文 python 实现都是基于标签值位于样本的最后一列进行计算。


在进行垃圾邮件分类的时候,首先从根节点开始判断,如果发送的邮件域名为:myEmployer.com,那么将其分类为”无聊时需要阅读的邮件”,否则进一步判断是否为包含单词”曲棍”的邮件,如果是则被列入”需要及时处理的朋友邮件”,否则被归入”无需阅读的垃圾邮件”。总之,像这样一步步缩小判断范围,至到找到目标分类的过程就是决策树不断作出决策的机制。

决策树的构造

决策树的优缺点

  1. 优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
  2. 缺点:可能会产生过拟合的问题。
  3. 适用数据类型:数值型(回归分析)和标称型(分类任务)

决策树的一般流程

  1. 收集数据: 可以使用任何方法;
  2. 准备数据: 树的构造算法只适用于标称型数据,因此数值型的数据需要离散化;
  3. 分析数据:可以使用任何方法,构造树完成之后,需要检查决策树是符合预期;
  4. 训练算法:构造树的数据结构;
  5. 测试算法:使用经验树计算错误率;
  6. 使用算法:使用决策树可以更好地理解数据的内在含义。

熵的概念及其公式

熵,也称信息熵,它是度量样本集合纯度的一种指标。也就说,样本集合中越不纯,那么表示信息越杂乱无序(如集合中类别越多,表示这个集合信息越杂乱),则其含有的信息量就越大。而比较官方的定义是,熵是对不确定性的度量,即越不确定,信息量越大。
在衡量样本集合的熵时,单个类别的信息量我们可以定义为

其中p(x)是类别占比(该类别的数目占总类别数目的概率)
而熵是信息(或单个类别所含信息)的期望值,因此最终(香农)熵的公式为:

计算给定数据的香农熵


其实就是把每个类别在样本集合中的占比计算出来,即p(xi),再利用上述公式计算即可。

划分数据集

决策的时候其实也是划分数据集的过程,你需要一个依据来将数据集划分开,一步一步地缩小可选的范围。所以我们此时需要一个依据,也就是能让我们作出最好决策的一个依据。然后根据这个依据将数据集划分开。
在决策树算法中,常用的依据主要有三种算法:ID3、C4.5和CART。

基于信息增益(ID3)

信息增益主要是利用集合的香农熵来计算的。信息增益衡量的是集合划分前的香农熵与划分后各子集香农熵的期望之差。
简单决策树
我们以上图为例。假设样本集合为 D,年龄为每个样本具备的其中一个特征,记作 a,它共有三个属性值(青年、中年、老年),设 v 为属性值的个数,D^v 为属性值为v的样本集合(被划分为青年的人数)。记 Ent(D) 表示样本集合 D 对应的熵,那么信息增益的计算公式为:

我们用 Gain(D,a) 表示样本集合 D 依据属性 a 进行划分时的信息增益。那么,在实际中,属性 a 可以有多个,即划分方式有多种,但是我们只取信息增益最大的那个划分方式来划分数据集。

基于信息增益率(C4.5)

信息增益率为信息增益的改进版本。由于信息增益对可取值数目较多的属性有所偏好,为减少这种偏好所带来的不利影响,信息增益率应运而生。增益率定义为:

这其实比之前的版本多除了一个分母,IV(a)其实表示就是属性 a 的各个属性值对应的香农熵总和。

需要注意的是,增益率对可取值数目较少的属性也有所偏好,所以 C4.5 算法并不是取增益率最大的那个最为最佳划分方式,而是先从候选属性中找出信息增益高于平均水平的属性(ID3 算法),再从中计算出信息增益率最高的那个属性。

基于基尼指数(CART)

在前面介绍的 ID3 和 C4.5 算法其实都是采用熵模型进行计算,并且涉及到大量的对数运算。计算相对复杂。
那么,基尼指数呢。它可以也用来衡量数据集的纯度,但是它与前面二者相反,基尼指数越小,数据集的纯度反而是越高的。对于数据集 D 的纯度可用基尼值来衡量(在分类问题中,假设有 k 个类别,第 k 个类别的占比为pk):

对于属性a,我们给出它的基尼系数定义为:

最后我们选择基尼指数最小的属性作为最优划分属性。特别注意的是,利用基尼指数来划分数据集只会对数据集二分,不多分。怎样理解?,对于多分类问题,可以利用整体思想,把数据集分成两大类,常见的有一对多,多对多,即一边是正例,一边是反例,因此使用基尼指数进行最优属性划分后得到的树是一颗优雅的二叉树。

基于ID3的python代码实现

根据指定特征的属性值返回数据集的子集

注意 extend 与 append 的用法区别,前者为抽取元素加入列表,后者可能会将整个列表作为元素加入列表中。

选择最好的数据集划分

其实大致步骤可以概括为:  
1. 遍历每个属性;  
2. 得到每个属性的属性值集合,并遍历;  
3. 对每个属性值进行划分,计算各个子集的信息熵期望;  
4. 计算信息增益;  
5. 比较不同属性划分下的信息增益,取最大的信息增益对应的特征索引返回。

创建决策树

根据标签列表”投票”选出最多标签的

主要使用字典记录类别的个数,然后再按递减排序即可。

决策树具体算法

使用决策树分类

使用决策树分类,就是从根节点开始递归寻找待分类样本所能到达的分类节点位置,然后确定最终的分类。

总结

决策树分类器就像带有终止块的流程图,终止块表示分类结果。开始处理数据集时,我们需要先衡量数据集中数据的不一致性,即熵,然后寻找最佳特征确定划分方式、分割数据集,直到数据集中的所有数据属于同一分类,或者已无可划分的特征了就停止。其实上述详细介绍的 ID3 算法局限性也比较大,它只能处理标称型的数据,对于连续值、缺失值无法处理,因而后面改进的 C4.5 和 CART 算法都改进了此点,我们下次再详细介绍,就这样了。

参考

<<机器学习实战>>
<<机器学习>>周志华
决策树算法原理

剑指offer2之求1+2+..+n

Posted on 2018-10-01 | Comments:

题目(求1+2+3+…+n)

求 1+2+3…+n ,要求不能使用乘除法。for、while、if、else、switch、case 等关键字即条件判断语句 (A?B:C)

算法思路

此处仅提供一种解法。即利用构造函数求解。
首先,从循环的实现思想入手。如果我们可以使用for、while,那么只要设置自增变量,然后累积自增变量的和,重复 n 遍即可。那现在,如果采用构造函数求解,那么我们就可以定义一个类,然后将重复代码放入构造函数中。最后外部调用时,只需要生成 n 个该类的实例即可达到一样的效果。

算法实现

参考

[1] <<剑指offer2>>

剑指offer2之股票的最大利润

Posted on 2018-09-29 | Comments:

题目(股票的最大利润)

假设把某股票的价格按时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?例如,一只股票在某些时间节点的价格为{9,11,8,5,7,12,16,14}。如果我们在价格为 5 的时候买入并在价格为 16 时卖出,则能获得最大的利润为 11。

算法思路

本题抽象出来就是,在不改变数组元素顺序的前提下,找到一对数对,他们的差值最大。
首先,由常识可知,在股票最低价的时候买入,最高价的时候卖出,最后所得的利润是最大的。
那么当我们买入第 i 个时间节点的股票时(此时相当于固定了卖出价),那么必须在之前的最低价格时买入,所以我们需要设置一个变量 min 记录 i 之前的最低价格(即 i 之前的最小数组元素)。当当前的价格小于 min,那么就更新 min 的值,否则就算出此时能够获得的利润,再将其与目前的最大利润比较,如果更大,则更新当前的最大利润。

算法实现

复杂度分析

算法仅遍历一次数组,时间复杂度为 O(n);空间复杂度为 O(1)。

参考

[1] <<剑指offer2>>

剑指offer2之圆圈中最后剩下的数字

Posted on 2018-09-28 | Comments:

题目(圆圈中最后剩下的数字)

0,1,…,n-1 这 n 个数字排成一个圆圈,从数字 0 开始,每次从这个圆圈里删除第 m 个数字。求出这个圆圈里剩下的最后一个数字。

算法思路

题目描述了一个圆圈来编排数字,那么就该想到可以用环形链表这种数据结构来表示它。另外,可以借助 STL 中用链表实现的 list 来模拟环形链表,只要我们在遍历到尾巴的时候,手动再跳到链表表头即可实现类似环形链表的效果。
那么算法每确定从一个数开始,就走到第 m 个数,然后删掉第 m 个数,同时确定下一个开始的数字。在这过程中,都要判断是否走到了链表尾部。

算法实现

复杂度分析

算法每删除一个数字需要走 m 步,共有 n 个数字,因此时间复杂度为 O(mn);另外,使用了辅助链表来模拟圆圈,空间复杂度是O(n)。

参考

[1] <<剑指offer2>>
[2] 牛客网

剑指offer2之扑克牌中的顺子

Posted on 2018-09-27 | Comments:

题目(扑克牌中的顺子)

从扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这 5 张牌是不是连续的。2-10 为数字本身,A 为 1,J 为 11,Q 为 12 ,K 为13,而大、小王可以看出任意数字。

算法思路

首先,考虑不是顺子的情况。当5张牌中至少存在相同的两张牌即可断定为非顺子。
然后,由于大小王可以当场任意数字,这里其将它们当成 0 处理,恰好也能和 1-13 之间的数字区分开。
那么,首先算法统计一遍 0 的个数,然后对数组升序处理;然后,再次统计除了 0 之外的相邻两牌之间所需的间隔数量以达到连续的目的,最后比较可用于填充的 0 的数量和间隔数量,那么这时候只要间隔数量足够用 0 填充那么就可以当场是顺子,否则为非顺子。

算法实现

复杂度分析

由于随机取出扑克牌数量固定,只有5,时间复杂度较低。具体算的话是O(nlogn)

参考

[1] <<剑指offer2>>

12…15
洪荣宣

洪荣宣

Rongxuanhong's Blog

72 posts
11 tags
GitHub E-Mail 简书 CSDN
© 2017 — 2019 洪荣宣
Powered by Hexo