在之前的文章中,对于介绍的分类算法有逻辑回归算法和朴素贝叶斯算法,这类算法都是二分类的分类器,但是往往只实际问题中不仅仅只有,当出现一个新的类别时,之前的分类器就不太适用,这里就要介绍一个叫做决策树的新算法,该算法对于多个目标的离散特征往往有比较好的分类效果,用以解决是离散型的数据,这是判别模型,也是一个生成学习算法。
假设当前网上有很多机器学习的教程,我想学机器学习,但是不知道哪一个教程比较好;又或者想买一个榴莲,不知道哪一只肉比较厚实,不知道该挑那一只,对于这种不确定性就叫做熵。
当一个事情有多种可能情况时,这件事情对于观察人而言具体是哪种情况的不确定性叫做熵。
当你不知道选择哪一篇教程来学习机器学习时,有人和你说TTyb的机器学习教程很好,这个人对你提供的TTyb的机器学习教程很好就是信息;又或者在买榴莲的时候,卖榴莲的人和你说这个榴莲的肉肯定很厚实,卖榴莲的人和你说的这个榴莲的肉肯定很厚实就是信息。当某件事情存在不确定性,能够消除观察人对于某件事情的不确定性,也就是前面说的熵,能够消除不确定性的事物叫做信息。
信息有很多种表现形式,能够调整不确定的概率的事物可以叫做信息
例如卖榴莲的老板说有很大概率这个榴莲的肉可能很厚实;能够排除干扰的事物可以叫做信息,例如卖榴莲的老板说这个榴莲壳厚,肉肯定小不要选择这个;能够确定情况的事物可以叫做信息,卖榴莲的老板说这个榴莲肉肯定很厚实,开了不厚实算我的。
熵和信息数量相等,意义相反,获取信息意味着消除了不确定性熵。
假设有4个榴莲只有一个肉是厚实的,老板和你说榴莲A的肉不厚实,给你提供了的信息,榴莲B的肉不厚实,给你提供了的信息,榴莲C的肉不厚实,给你提供了的信息,最后让你确定了榴莲D肉是厚实的。老板每次都给你提供了一次信息,为什么提供的信息量却是不一样的?信息是如何量化的?回想一下什么东西有单位,质量、温度等物理量,信息也是一个物理量,要测量这个物理量,不妨回想一下我们是怎样测量质量的,“千克”最初又是怎么被定义出来的。
其实我们最初并不知道千克的质量,而是选定了一个参照物,把这个物体的质量就称为千克。当想要测量其他物体的质量时,就看这个物体的质量相当于多少个参照物体的质量,这里的“多少个”便是千克,如果参照物体换成“斤”,那么单位就会变化。
测量信息也是一样,既然信息消除的是不确定性,那么就选择另外一个事件的不确定性作为参照事件,当想要测量其他事件的信息时,就看待测事件的不确定性相当于“多少个”参照事件的不确定性,这里的多少个便是信息量。即待测物体质量为,参照物体个数(信息量)为,参照物体质量(千克)为,那么:
当选择的参照物事件是像抛硬币这样,只有两种等概率情况的事件时,测得的信息量的单位就被称为比特(bit),然而测量参照物体个数(信息量)时,我们是用待测物体质量除以参照物体质量(千克),即:
可是测量信息时却不能用除法,因为抛掷3个硬币能够产生的等可能结果并非,而是种,也就是说信息量不是线性关系,而是指数关系:
|正正正|反正正|
|正反正|正正反|
|反反正|反正反|
|正反反|反反反|
所以当知道可能情况的个数,想求这些情况相当于多少个参照事件所产生的,用指数运算的反函数,即对数运算计算:
而这里硬币只有正面和反面,那么这里的参照事件,公式变换为:
上面的式子代表,在抛硬币的实验中,假设8个不确定情况就相当于3个硬币抛出来结果,即信息量为;4个不确定情况就相当于2个硬币抛出来结果,即信息量为。
而上面讲的时抛掷硬币,被测事件的所有可能情况都是等概率事件,但是如果存在如下假设,这里有4个榴莲,只有一个榴莲的肉是厚实的,其他3个榴莲的肉是不厚实的:
那么买到肉是厚实榴莲的不确定性(信息熵)为,因为这里可能情况的个数,所以不确定性是。但是如果老板和你说最大的那个(最右边圆圈)榴莲有50%概率是肉厚的,那么从概率上来说买最大的那个(最右边圆圈)是肉厚的概率为,取出不是肉厚的概率是,这里代表剩下3个榴莲要瓜分剩下的概率,所以剩下3个榴莲每个的概率是,这时候各个情况的概率不一样了:
这时该如何计算总信息量了呢?这时候就要分别测量每种可能情况的信息量后,乘以他们各自发生的概率再相加即可,即这时候买到肉厚的榴莲的不确定性(信息熵)为:
不过怎么测量每种情况的信息量呢,怎么知道概率为的情况的不确定性相当于抛掷多少个硬币所产生的不确定性呢?我们确实没有办法用这个公式了,但是我们知道会发生的情况,相当于从100个等概率情况中确定实际情况,即,概率的倒数等于等概率情况的个数,即,用概率的倒数替换等概率情况的个数后,我们就可以计算每种情况的信息量了:
再用每个情况的信息量乘以对应事件发生的概率,再相加后就能算出总信息量了:
由此可得到,任何随机变量信息熵为:
既然任何随机变量信息熵为:
那么假设是随机变量发生的概率,如果存在多个变量和,则他们的联合熵为:
其中代表事件和一起出现的联合概率。有了联合熵,又可以得到条件熵的表达式,条件熵类似于条件概率,当事件发生过后,事件还剩下的不确定性(信息熵):
其中,信息熵和信息增益的关系如下图所示:
左边的椭圆代表随机变量的信息熵,右边的椭圆代表随机变量的信息熵,中间重合的部分就是信息增益, 左边的椭圆去掉重合部分就是,右边的椭圆去掉重合部分就是,两个椭圆的并就是,由此可以得到信息增益的公式为:
回到最初的问题,当4个榴莲中不知道那个是肉厚的榴莲的时候,随机变量,不确定性(信息熵)的套用公式为:
假设每个榴莲从左到右记为随机变量、、、,老板告诉说最大的那个(最右边圆圈)榴莲有50%概率是肉厚的,即提供了信息,求肉厚的信息熵。这时候先计算的信息熵为条件熵:
最后由总的不确定性(信息熵)减去后面的不确定性(信息熵),得到老板说的话“最大的那个(最右边圆圈)榴莲有50%概率是肉厚的”提供的信息为:
提供的信息也叫做信息增益
输入的是个样本,样本输出集合为,每个样本有个离散特征,特征集合即为,输出为决策树。例如存在样本如下所示:
天气 | 气温 | 湿度 | 风力 | 外出 |
---|---|---|---|---|
晴朗 | 高温 | 高 | 无风 | 否 |
晴朗 | 高温 | 高 | 有风 | 否 |
多云 | 高温 | 高 | 无风 | 是 |
下雨 | 温暖 | 高 | 无风 | 是 |
下雨 | 寒冷 | 正常 | 无风 | 是 |
则样本数量;输出集合为是
或者否
,即;每个样本的离散特征为,即天气
、气温
、湿度
、风力
;特征集合为,当离散特征是天气
时,特征集合为{晴朗,多云,下雨}
。算法的过程为:
- 计算中的各个特征(一共个)对输出的信息增益,选择信息增益最大的特征,其中
- 按特征的不同取值将对应的样本输出分成不同的类别,其中。每个类别产生一个子节点。对应特征值为。返回增加了节点的数。
- 对于所有的子节点,令递归调用1-2步,得到子树并返回。
ID3算法虽然提出了新思路,但是还是有如下4点需要改进的地方:
- ID3没有考虑连续特征,比如长度、密度都是连续值,无法在ID3运用,这大大限制了ID3的用途。--数据分箱即可解决
- ID3采用信息增益大的特征优先建立决策树的节点,但信息增益准则对可取值数目较多的属性有所偏好。
- 算法对于缺失值的情况没有做考虑。--特征工程即可解决
- 没有考虑过拟合的问题。--预剪枝和后剪枝
对于上面的第2点需要做一下解释。从上面可以知道,ID3信息增益的计算公式为:
信息增益的大小取决于随机变量信息熵的大小,越小则信息增益越大,而什么情况下会有极小的信息熵呢?举一个极端的例子,如果上面的例子中,变量天气的特征互不相同变成:
天气 | 气温 | 湿度 | 风力 | 外出 |
---|---|---|---|---|
大晴 | 高温 | 高 | 无风 | 否 |
小晴 | 高温 | 高 | 有风 | 否 |
多云 | 高温 | 高 | 无风 | 是 |
大雨 | 温暖 | 高 | 无风 | 是 |
小雨 | 寒冷 | 正常 | 无风 | 是 |
那么天气的信息熵会等于0,计算如下,当天气为大晴时,的概率不外出,的概率外出,那么大晴的信息熵为:
同理天气条件下其他特征的信息熵都是,那么天气的信息熵,得到天气的信息增益就最大了。也就是说决策树给天气这个属性下的离散特征都单独分成了一个子类,也就是说有多少种天气情况就有多少种取值,那么原数据集中有多少个离散特征,就会被划分为多少个子类。虽然这种划分毫无意义,但是从信息增益准则来讲,这就是最好的划分结果。
对于这种结果看似完美划分了随机变量的特征,然而根据大数定律,只有当样本数足够多的时候,频率才可以准确的近似概率。也就是说,样本数越少,对概率的估计结果的方差就会越大,结果也就越不准。想象一下做抛硬币实验来近似正面向上的概率,如果只抛两次,那么得到的正面向上的概率可能会非常离谱。而如果抛1万次,不论何时何地几乎总能得到近似0.5的概率。
而C4.5就是为了解决信息增益准则对可取值数目较多的属性有所偏好这种问题,由此提出了信息增益比,计算公式为:
其中计算方式为:
等于随机变量的概率。当信息熵最小时,也就是分类各不相等的时候,也相对最小,这样就减轻了划分行为本身的影响。
有一个盒子里面放着小球,如果小球的颜色都是绿色,那么代表绿色的概率是,我们可以称随机变量是纯正的:
如果盒子里面的小球包含不同的颜色,不同颜色发生的概率为,我们可以称随机变量不纯正的:
假设某组样本存在多个随机变量,他们各自发生的概率是,那么这组样本的纯正程度可以使用基尼系数(Gini)衡量:
假设一个盒子存在3种颜色的小球,他们的概率是:,那么基尼系数为:。其他情况的基尼系数计算:
,
计算的基尼系数为:
基尼系数越大,代表样本越不纯净;基尼系数越小,代表样本越纯净,最终会找出指数最小的来作为最优的划分点
决策树算法为了避免过拟合和简化决策树模型,提出了剪枝的方法,剪枝分为预剪枝和后剪枝,剪枝的原理如下:
预剪枝:在构造决策树的同时进行剪枝,也就是在节点划分前进行判断。所有决策树的构建方法,都是在无法进一步降低熵的情况下才会停止创建分支的过程,为了避免过拟合,可以设定一个阈值,熵减小的数量小于这个阈值,即使还可以继续降低熵,也停止继续创建分支。但是这种方法实际中的效果并不好。
后剪枝:在决策树生长完全构造好了过后,对树进行剪枝。剪枝的过程是对拥有同样父节点的一组节点进行检查,判断如果将其合并,熵的增加量是否小于某一阈值。如果确实小,则这一组节点可以合并一个节点。后剪枝是目前最普遍的做法。
常见的剪枝有REP(Reduced Error Pruning)、PEP(Pessimistic Error Pruning)、CCP(Cost Complexity Pruning)和MEP(Minimum Error Pruning)算法,以下分别进行说明。
REP
是最简单粗暴的一种后剪枝方法,其目的减少误差样本数量。该算法是从下往上依次遍历所有的子树,直至没有任何子树可以替换使得在验证集上的表现得以改进时,算法就可以终止。
假设树存在很多结点,其中,表示节点下训练样本误判的数量,则有训练样本误判总数量为:
假设树去掉某个节点变成树,则有训练样本误判总数量为:
如果误判的数量降低,即剪枝之后使得误差降低:
那么代表剪枝后能使误差降低,剪枝成功。反复进行上面的操作,从底向上的处理结点,删除那些有害的结点,直到进一步修剪不能减低误差为止。例如:存在如下一棵树:
Step 1: 将节点4删掉替换成8和9,测试在验证集上的表现,若表现更好,则将节点4删掉并替换成8和9的并集,若表现不好则保留原树的形状
Step 2: 将节点2删掉替换成8、9和5,测试在验证集上的表现
Step 3: 将节点3删掉替换成6和7,测试在验证集上的表现
REP
是最简单的后剪枝方法之一,不过由于使用独立的验证集,如果数据量较少(数据要分为训练集、验证集、测试集三份),那么在使用验证集进行剪枝的时候,可能会在验证集出现的稀有实例,却在测试集中没有出现,那么就会存在过度剪枝的情况。如果数据集较小,通常不考虑采用REP算法。尽管REP有这个缺点,但还是能够解决一定程度的过拟合问题。
上文的REP
方法思想简单且易于使用,不过最大的问题在于它需要一个新的验证集来修正我们的决策树在,PEP
方法中不需要新的验证集,并且PEP
是自上而下剪枝的。PEP
的剪枝是把一颗子树(具有多个叶子节点)的分类用一个叶子节点来替代,如下所示:
原来子树1可以分成(4,5,6,7)多个类,但是剪枝变成只能分成一个类(1)。同样的样本子集,如果用子树分类可以分成多个类,准确率肯定要高一些,而用单颗叶子节点来分的话只能分成一个类,准确肯定要低一些,所以的误判率肯定是上升的。训练数据也带来错分误差偏向于训练集,为了解决这一问题,给每个子叶节点加入修正项。具有个节点的树的错误率为:
去掉节点个子叶节点之后T′个节点的树的错误率为:
其中表示节点下训练样本误判的数量,表示节点下训练样本的总数量。
PEP
悲观错误剪枝剪枝法定义,如果 E(剪枝后误判数均值)−E(剪枝前误判数均值)<std(剪枝前误判数标准差),则可以剪枝,假设在子树中每一个样本的误判服从一个二项分布,其中表示子树所包含的所有样本个数。
所以,在剪枝前,其期望的误判数为:
其误判的标准差为:
当剪枝后的误判数小于剪枝前的误判数一个标准差之后,就决定剪枝:
例如:存在如下一棵树:
对于节点T4而言,剪枝前后的计算过程如下:
已知,所以,剪枝前的误判概率为:
所以:
在我们对T4进行剪枝后,即将T4直接作为叶节点,剪枝后的误判概率:
剪枝后的误判期望数为:
因此满足条件,所以我们将把T4节点剪枝
该算法为子树定义了代价(cost)和复杂度(complexity),以及一个可由用户设置的衡量代价与复杂度之间关系的参数,其中,代价指在剪枝过程中因子树被叶节点替代而增加的错分样本,复杂度表示剪枝后子树减少的叶结点数,则表示剪枝后树的复杂度降低程度与代价间的关系,定义为:
其中,
CCP算法可以分为两个步骤,
Step 1: 按照上述公式从下到上计算每一个非叶节点的αα值,然后每一次都剪掉具有最小αα值的子树。从而得到一个集合,其中,表示完整的决策树,表示根节点
Step 2: 根据真实的错误率在集合选出一个最好的决策树
假设在下图中有数据100条,从下往上开始计算:
先以节点T4为例,那么可以得到:
REP | PEP | CCP | |
---|---|---|---|
剪枝方式 | 自底向上 | 自顶向下 | 自底向上 |
计算复杂度 | 0(n) | 0(n) | o(n2) |
误差估计 | 验证集上误差估计 | 使用连续纠正 | 标准误差 |
对一般三种决策树进行总结,区分如下所示:
算法 | 支持模型 | 特征选择 | 连续值处理 | 确实值处理 | 剪枝 |
---|---|---|---|---|---|
ID3 | 分类 | 信息增益 | 不支持 | 不支持 | 不支持 |
C4.5 | 分类 | 信息增益比 | 支持 | 支持 | 支持 |
CART | 分类、回归 | 基尼系数 | 支持 | 支持 | 支持 |
假设观测到历史有如下一组信息:
天气 | 气温 | 湿度 | 风力 | 外出 |
---|---|---|---|---|
晴朗 | 高温 | 高 | 无风 | 否 |
晴朗 | 高温 | 高 | 有风 | 否 |
多云 | 高温 | 高 | 无风 | 是 |
下雨 | 温暖 | 高 | 无风 | 是 |
下雨 | 寒冷 | 正常 | 无风 | 是 |
下雨 | 寒冷 | 正常 | 有风 | 否 |
多云 | 寒冷 | 正常 | 有风 | 是 |
晴朗 | 温暖 | 高 | 无风 | 否 |
晴朗 | 寒冷 | 正常 | 无风 | 是 |
下雨 | 温暖 | 正常 | 无风 | 是 |
晴朗 | 温暖 | 正常 | 有风 | 是 |
多云 | 温暖 | 高 | 有风 | 是 |
多云 | 高温 | 正常 | 无风 | 是 |
下雨 | 温暖 | 高 | 有风 | 否 |
可以看到这个树的根节点是外出
,而根节点往下的一个节点,该怎么定义用哪一个条件呢?决策树构建树节点的条件在于:该节点能最大程度的提供有用的信息,使得不确定性(信息熵)减少得最快。也就是说,天气、气温、湿度、风力这四个条件,哪一个条件为结果外出
提供更多的信息:
要计算哪一个条件能提供更多的信息,只要计算根节点和另一节点的信息熵差值,就是提供的信息增益了。由上面的表格可以知道,外出的概率为,不外出的概率是,很显然这是一个不均匀分布,要使用一般分布的信息熵计算:
则根节点外出的信息熵为:
接着分别计算各个条件的信息熵,先计算天气各个情况的信息熵:
而天气是 晴朗
的概率为5/14,天气是 多云
的概率为4/14,天气是 下雨
的概率为5/14,所以 天气
的信息熵为:
天气
的信息增益为:
同理计算得到,气温
的信息熵为:
气温
的信息增益为:
湿度
的信息熵为:
湿度
的信息增益为:
风力
的信息熵为:
风力
的信息增益为:
可以看到,信息增益:,所以对是否外出提供信息最多的条件是天气
。也就是说在决定是否外出的时候,天气是最重要的一个影响因素,天气的好坏很大程度决定了当天是否会外出,减少是否外出的不确定性。
但是天气分为多云、下雨、晴朗,由数据集可以知道多云的时候,无论气温、湿度和风力如何,都会外出;而下雨和晴朗的时候,会根据气温、湿度和风力的情况再来决定是否外出,因此决策树的构造变成了:
而下雨和晴朗的时候,会根据气温、湿度和风力的情况再来决定是否外出,这时就需要计算:
下雨时,选择信息增益最大的属性(气温、湿度或风力)来作为下一个节点
晴朗时,选择信息增益最大的属性(气温、湿度或风力)来作为下一个节点
在天气=下雨
的情况下:
由前面知道,在下雨时气温、湿度和风力外出情况如下所示:
天气 | 气温 | 湿度 | 风力 | 外出 |
---|---|---|---|---|
下雨 | 温暖 | 高 | 无风 | 是 |
下雨 | 寒冷 | 正常 | 无风 | 是 |
下雨 | 寒冷 | 正常 | 有风 | 否 |
下雨 | 温暖 | 正常 | 无风 | 是 |
下雨 | 温暖 | 高 | 有风 | 否 |
则气温
的信息熵为:
同理可得 湿度
的信息熵,风力
的信息熵,
在天气=晴朗
的情况下:
最大,那么新得到的树形如下所示:
而上面却发现没有气温
选项?因为在其他三个的条件下就能决定是否外出了,气温对于外出没有直接影响。
将文本转化为数字:
data[data == u'晴朗'] = 2
data[data == u'多云'] = 1
data[data == u'下雨'] = 0
data[data == u'高温'] = 2
data[data == u'温暖'] = 1
data[data == u'寒冷'] = 0
data[data == u'高'] = 1
data[data == u'正常'] = 0
data[data == u'有风'] = 1
data[data == u'无风'] = 0
data[data == u'是'] = 1
data[data == u'否'] = 0
基于信息熵来计算:
# 建立决策树模型,基于信息熵
dtc = DTC(criterion='entropy')
dtc.fit(x_train, y_train)
预测:
print(dtc.predict(x_test))
print(dtc.score(x_test, y_test))
这里样本比较小,所以得到的准确率分数为1.0
: