当前课程知识点:R语言数据分析 > 下部:博术 > 第12章 既是世间法、自当有分别 > 12.5 决策树(II)
大家好
欢迎来到《R语言数据分析》课程
今天继续和大家交流决策树相关的内容
在我们前面课程里面讲到了
当我一棵完全生长了决策树
因为它太过茂密的时候
很多属性考虑太多 分枝太多
这个时候容易怎么样
容易产生过拟合的现象
就是对我这个训练集来讲
它可能是很准确的
但是它的推广泛化能力可能相对比较差了
这个时候我们就涉及到决策树剪枝的过程
好 我们看看所谓决策树剪枝是什么意思
这是一个我们已经生长好的树
所谓剪枝的话它相当于什么
相当于把某一些子树
比如说这都是子树对吧
都是子树
将某些子树
我收缩为一个节点
比如说现在我们这里面一直往下
细分 细分 细分……
一直往这里细分的时候
我细分到这个节点的时候
我还要继续细分什么
看看我这个数学是否是大于等于92
是的话是理科
否的话是文科
假如剪枝的话怎么剪
到这一步我就不再做分裂了
我直接判断什么
直接判断我这个相应的节点类标签就是文科
好 同样的
假如你在往上剪枝的话
将这一部分再继续怎么
这个子树继续收缩为一个节点
或者说再往上走
这其实(就)是所谓决策树剪枝的过程
就将子树收缩为一个节点这么一个过程
我们前面讲到了所谓决策树剪枝的目的
是为了提高它的泛化能力
对于我新的未来的测试集
我同样是有更好的性能的
也就要求我们这个模型不要太复杂
也就所谓的结构风险最小化
我们可以定义一个[代价]函数
它由两部分组成
第一部分就是我这个[模型]本身的一个误差
第二部分
用来衡量我这个模型的复杂程度
当然对一棵树模型来讲
它有很多衡量它的复杂程度的方法
比如我的层级
比如我结点的个数
当然我们这里面
通过叶子结点的个数
来衡量整个模型的复杂度
好 这两部分相加的时候
我们有一个权值是α
毫无疑问 这个α是什么呢
α是用来权衡这个拟合程度
和这个模型复杂度的就平衡它们
当我的α比较小的时候
那我其实
倾向于我这个树相对是比较茂盛一点
当我这个α比较大的时候
为了实现我这个[代价]函数最小
那我这个树相对比较小一点好
极端的情况之下
比如α等于0的时候
毫无疑问我整个这个树就整体树应该是最优的
用通过这个损失函数
我们这个目标函数来讲的话
就整体树应该最优
但假如我这个α不断增加 不断增加
增加得非常大的时候
大到某一个程度
或者说比如说我这个α取无穷的时候
毫无疑问那怎么样
我这个时候应该就是单个节点应该就是最优的
我就不再做任何分裂
这个时候我复杂程度最小对吧
所以我们针对不同的α
我这边整个最好的树它有不同的最优子树
但应该说更准确的讲应该
是α落入不同的区间的时候
它对应着不同的最优子树
这什么意思呢
因为我这个α在增加的时候
比如从0增加到0.0001
然后从0.0001继续往上增加的时候
并不是每增加一个值 一个微小值
然后我这边都就直接导致我这边要剪掉某一个分支
而是说什么它大到一定程度的时候
比如由α等于零的时候变成α1的时候
这时候我才需要剪掉某一个分枝
剪掉某个子树
从而保证我这个损失函数是最小
我们看一看
具体的例子
还是以我们这个决策树为例
我们现在有不同的α的取值
我们把它放到一个数轴上面来
毫无疑问刚开始这个点是一个α=0
这个时候我整个整体树是最优的
然后我这个α不断往这边增长
α的值不断增加的时候
为了保证我这个损失函数最小
这个目标函数最小
那怎么样呢
我增加增加增加一定程度
比如说这个增加到α1的时候
我就需要剪掉一部分的树
这时候我这个复杂度
因为它剪枝完之后复杂度减小
我相应的这个损失函数是最小的
也就是说这个时候我在哪
我在0到α1之间
这个时候是整体树最优的
当我一旦达到α1之后
再下一个值之前
我把这棵树减掉之后它是最优的
而且把这部分收缩为一点
这个子树是最优的
同样假如我这个α继续增大的话
增加到α2
那毫无疑问我这个时候
我这一部分还得
继续剪 继续收缩为一个节点
也就说在这个α2到
未来的一个节点α3之前
这个树
应该收缩完之后收缩成这么一棵树的时候
它应该是一个最优子树
就相对我们前面定义的这个损失函数来讲
同样我要再继续往上增加的时候
我这一块可以继续收缩
所以我们可以看得出来
它其实在不同的区间里面
对应着不同的最优子树
那这个时候其实我们就
从零增加到无穷大的时候
我并不需要考虑遍历
从零到无穷大的所有数值
而是 其中有若干个
若干个端点若干个切分点
切分成不同的区间
在不同区间里面生成了不同的最优子树
那我想找到最终性能表现最好的那棵怎么办
我再把所有这个子树这么多一个集合
说一个子树的序列进行一个交叉验证就可以
看看哪个子树它的泛化性能最好
那我就最终把那一棵树作为什么
作为我剪枝之后的树
好 那这个α除了α0之外
α1 α2 α3一直到αn
我如何确定呢
是需要我人工来确定吗
不需要
它是结合这棵树本身 结合这个数据信息本身
我可以确定他
咱们看看具体过程
对我们T0这个完全生长的树
它内部的任意节点T
假如说我把这个节点这个相应的子树
我收缩成一个节点的话
就是说以T为单节点的树的损失函数
我们可以这样定义
按照我们刚才的损失函数
一个是这个错误率
一个表示这个复杂程度的
好 因为这个时候它只有一个节点
毫无疑问这个叶子节点的个数就是1
所以α后面没有了
只有一个α对吧
假如这个相应这个内部节点
以及它相应的这个子树我不收缩的话
那它相应损失函数是多少
这个Cα(Tt)当然也是包含两部分
一部分是表示这个错误率
另外一部分表示这个复杂程度
也就是这个子树没有收缩的时候
它叶子节点的个数
好 我们现在比较这两个损失函数
就这个Cα(Tt)和Cα(t)
假如我们这个α比较小
甚至说这个α为零的时候
毫无疑问谁更小一点
应该是Cα(Tt)更小一点
当我这个α充分小的时候
毫无疑问是Cα(Tt)是小于Cα(t)
但是假如我这个α不断增加
α 不断增大的时候
毫无疑问怎么样
增加到某一个α的时候
这个Cα(Tt)就和Cα(t)相等
假如我再进一步增加的话
毫无疑问它就反向
这个不等式就反向了
好 我们现在关注一下
当取α某个值的时候
Cα(Tt)等于Cα(t)
等于这个时候也就说我
我令这个α等于下面这个公式
这两个相等的时候
我把这两个式子相减
左右相减毫无疑问就可以得到
相应的α可以求出来
好 当我α取这个值的时候
我们可以考虑一下
我们这个时候是否应该剪枝
这两棵树 一个是生长的 一个是收缩的
这两棵子树
毫无疑问
当它的损失函数相等的时候
我肯定要
我肯定要取
取这个相对比较简单的那棵
按照这个奥卡姆的剃刀原理
那好 假如我有一个相同的损失函数(取值)
然后剪完了之后
也就收缩成一个节点之后的怎么样
它的结点数更少
毫无疑问
我应该剪枝
因为这个为单节点的数是更可取的
也就是说这个时候的α
当α增加到这个值的时候
从零慢慢增加慢慢增加
一直增加到什么
C(t)-C(Tt) 然后除以|Tt|-1
当它增加到这个时候
毫无疑问我就可以剪掉相应的这棵子树了
好 这是我们对于某个节点来讲是这样的
那好假如我面对所有的
面对我整个这个生长树里面任意节点的话
我来怎么剪 具体剪的过程我们再看一下
我对任意节点我先计算一下
就令刚才这个α等于多少
将它定义为g(t)
将它定义为g(t)
我将所有的节点的这个g(t)我都算出来
除了叶子节点之外的所有的内部节点我都算出来
算完之后因为我这个α的从小往大增加
然后我根据它增加的到
α1到α2到αn的时候
我分别来进行
得到相应的最优子树
然后进行剪枝
那好毫无疑问
我这个内部节点里面
我可以找到一个最小的那一个
找到g(t)最小的那一个
找到它之后我怎么样
我将它收掉
将那个子树
剪完之后
我令剪完之后的那个树
为T1
毫无疑问这个T1
它是当这个α1
当这个α1
当α的取值大于等于α1的时候
怎么样这个T1
毫无疑问属于这个最优子树
假如是α0的话
α0到α1之间
这个时候还是完全生长的树是最好的
然后它增加增加增加增加到α1的时候
毫无疑问应该T1是最好对不对
那好可以
可以继续增加α继续增长的时候
那毫无疑问
我将那个T1作为新的一棵树
针对这个T1来讲
我再计算里面
它的内部的任意节点
它的相应的
这个g(t)的值
再找出一个最小的一个
再进行剪枝
再收缩成一个单个节点
那这个时候相应的α就是什么
就是α2了对不对
那这个时候α2
它对应的这个区间里面
所对应的这个最优子树是谁
就是T2 对不对
所以按照这个步骤不断的这样剪枝下去
当然它是有限的
因为它内部节点数也是有限
毫无疑问怎么样
我就可以得到什么
T0 T1 T2 Tn
得到有限个最优子树的序列对吧
这就和我们刚才讲到的这个
相应的α0是0
因为刚开始已经有了
就是直到α1 α2 一直到αn
相对应的最优子树序列求到了
一旦得到这个最优子树之后
也就是说α取值不一样的时候
我所有的最优子树我都知道了
那好
一直剪下去之后
我得到了这么一个相应的α的序列
也得到一个相应的子树的序列
最后我就通过一个交叉验证的方法来看一看
究竟哪一个子树泛化能力是最强的
测试误差是最小的 就可以了
当然 这个时候怎么办
我们可以看得出来
机器学习里面 到后面做选择的时候
很大一部分都是 通过实验的方法
而不是一个纯粹理论推导的方法来计算
好 这是一个剪枝的方法
就是通过代价复杂度 通过定义这么一个函数
一个损失函数 来进行剪枝
我们看看具体算法
输入的时候是一个完全生长的一个决策树T0
然后我要输出的话就是要剪枝之后的
一个决策树Tα
我有这么几个步骤
具体要做剪枝的话
首先是设K=0
这个时候 T=T0了
当前这个数是T等于T0
并且我令这个α等于无穷大
自下而上对内部的各个节点计算
我们刚才这个g(t)
也就这个我们想得到这个α的值
计算一下
将这个α的值设为什么
设为所有节点里面最小的那一个
然后怎么样
我对这个g(t)等于α的这个内部节点进行剪枝
并且按照这个叶子结点的个数
少数服从多数的原则
这里边又回到我们
之前讲到的有生于无
通过“少数服从多数”方式来进行
叶子节点的标签的表决
得到一棵树T
我们设K=K+1
然后αK=α
TK=T
这个时候就由T0生成了T1
由α0又进一步递推到α1了
如此迭代下去
就假如它这目前这个T还不是什么根节点
及两个叶子结点所组成的树的话
我再回到这第二个步骤
再进行下一轮的迭代
就在针对T来讲
我又在找内部节点的每一个节点
它相应的g(t)的值
一直迭代下去之后怎么样
得到了α1 α2 αn
也得到相应的T0 T1 T2 Tn
通过交叉验证的方法
找到最优的那一棵子树
这是所谓的通过代价复杂度的方法进行剪枝
好 咱们看看在R里面是如何实现的
R里面我们首先可以通过printcp这个函数来看一看
关于这个代价复杂度相应的一些信息
这个刚才我们建好的模型
就这个完全生长的这个树imodel
这其中
有着一个CPtable
我们刚才讲了
通过不同的剪枝的话
它最后生成一棵子树序列
那这里面最终要看选择哪棵树的话
我怎么做的
交叉验证的方法对不对
交叉验证方法的时候
我们看一下
这里面有个xerror什么意思
就是X交叉的意思对吧
交叉验证
通过交叉验证的方法来看
目前这个子树它相应的错误率
当然因为你的交叉验证的话
每一折不一样
它同样还有一个
标准差的问题
也就说最终我们选择哪一棵子树最好的时候
一般就判断
这个交叉验证的结果
我们当然希望它的结果是最小的
并且这个偏差也是相对比较小的
就是在这个子树上它的
减掉之后进行交叉验证的时候
发现
总体上平均值它是最小
同时它波动相对小
相对比较稳定
好 我们看一下
可以通过这个plotcp来看一看具体的
我们相应的一些那个
在不同的子树下面它相应的表现
plotcp它得这么一个图
这边有不同的树的规模size of tree
那好这个我们看得出来所谓的偏差主要
看箱线图一样看它长短
越长的话 表示偏差越大
当然这个交叉验证的那个平均值的话
就是看这个具体的点了
我们当然希望找到最低的这个点
就是我这个偏差是
是我这交叉验证它测试的误差是最小的
当然对于我们这个数据集来讲
毫无疑问它就相当于没有剪枝
我这个叶子节点为14的时候
它是最优的
这个size of tree
是指叶子节点的意思
就是当我的叶子节点为14的时候
其实也就是我们原始这棵树
没有进行剪枝的时候效果最好
当然即便我们这棵树针对我们这个问题来讲
它没有进行剪枝
但是假如我们采用决策树进行建模的话
除了树的生长之外
剪枝应该是一个必须考虑的步骤
咱们看一看这个具体如何剪枝
我们刚才已经看了一下大概的性能表现
我们要剪枝的话
可以利用这里面这个函数
imodel到里面的相应得到一个结果叫cptable
利用这里面的信息进行剪枝
具体的利用过程是
它具体如何来利用cptable的信息
我们刚才讲了这里面有个交叉验证的结果
那其实我就想找到什么
究竟哪一个CP值和我对应到了
相应的最小的测试误差
找到它之后怎么样
我将这个相应的CP值拿过来得到它
就从这个数据框里面得到
相应的这个CP值
得到之后我调用这个prune()
就是剪枝这个函数
对imodel的之前已经完全生长的树进行剪枝
根据这个CP值进行剪枝就可以了
然后这是一个最后剪枝的结果
好 这就是我们在树生长之后
再进行剪枝的一个最基本的原理
以及R里面一个最基本的
实现的一个相应的一些函数
好 在我们进行了生长和剪枝之后
基本上说我们这个树的模型的建立
就已经完成了
咱们来看看这棵树具体的它的一个
可视化的效果
除了我们前面看到这个控制台的输出之外
我们还希望看到这个树究竟长什么样子
咱们看一看如何来绘制这个决策树
假如是用rpart这个包的话
里面有两个函数是最基本的
一个是plot(imodel)
再加上什么 text(imodel)
这个时候
生成这么一棵树的结构
相对比较简单
只是线条表示不同的分枝
上面有相应的属性的分裂
属性不同在取值的时候
它走不同的方向
满足它走左边
不满足走右边
咱们可以调用专门的什么rpart.plot
这个包里面相应的这个函数
这个函数名称也叫rpart.plot
我们可以绘制一下这个最后的模型
看它长什么样子
当然这里边有一些具体参数的设置
我们可以查看一下相应的帮助文档
看看每个参数设置都是什么含义
这是最后我们绘制的效果
显然我们这个通过rpart.plot绘制的图形
就相对于这个rpart里面这基本的函数的话
那效果就好得多了
基本上已经属于这个商业级或者工业级的一个结果
我们这个结果应该是可以
直接放到我们分析报告里面去的
好 这就是我们生成完这个树之后
调用这个rpart.plot进行绘图
需要补充的什么
这里面其实要绘这么个图形的话
它其实里面参数设置
还是相对比较复杂的
当然其实我也建议大家是这种
就是拿一个已有的模板
比如说我们现在已经查找这个
rpart.plot相应的这个一些文档
看以前已有的示例
在这示例的基础之上再做相应的调整
也就是说站在别人肩膀上再进一步
假如你自己需要调整
这里面比如说我这个字体的大小 颜色
对其中某一些参数进行调整也可以
而不是从零开始来设rpart.plot那么多的参数
这就是我们关于rpart.plot
绘图的一个最基本的一个介绍
当然我们也讲了
其实每一个决策树
在一次做判断的时候都会看成
一条条决策规则所组成的
就是一条路径下来其实就一条决策规则
我们说看这个树之外我们还希望
把决策规则导出来
怎么导我们需要借助一个包
这个包的名字叫rattle
这么一个包加载之后里面专门有个
asRules就将我们这棵树
我将它转换成规则
这个规则就比较容易读了
比如数学小于85.5并且性别为女
那是文科
包括类似下面的这些
就是如何得到不同的属性组合
最后判断它是否是文理分科
这些结论性的意见的话
可能更加具有可读性
好 这就是[决策规则] 一个导出的过程
导完之后我们也可以将这个规则
放到我们的数据分析报告里面去
当然我们前面讲到的这个模型的验证也好
包括我们那个决策树的一个构建也好
不管是训练集上的表现
还是测试集上的表现
其实都是通过一个
留出法的方法来实现
按照我们前面的那个分析的套路怎么样
在后面我们继续要做一个什么
就一个交叉验证 k折交叉检验
这个时候还是调用我们前面的框架
对于每一折进行一个训练和测试
就是kfolds当前这一则然后进行训练
划分出训练集测试集
并且对什么
调用rpart这个包
进行一个模型的训练
当然也有我们相应的一些剪枝的过程
然后对这个训练集进行一个预测
也是调用这个predict()这个函数
大家注意这个参数type等于class
这个type需要设置一下
否则的话它输出的话就是叶子节点上
不同类别的这个分类的比例了
或者说概率
不是这个具体类标签
一旦得到这个预测结果之后
再调用我们前面定义的这个函数叫imetrics
我们将这个方法的名称设为rpart
类别是什么
训练集上的一个表现
将这个具体预测结果和实际结果传递过去
将当前这一折训练集上的表现
rbind叠加到global_performance
总体性能指标里面去
同样
对于我们这个测试集也是一样
也得到相应的结果
并且叠加到我们总体的全局变量
global_performance里面去
这个得到结果之后
我们可以在最后所有算法讲完之后
我们再看哪一个模型
哪个算法它最终的性能表现最好
当然我们可以看一看
目前这个global_performance
对于rpart来讲
它的一些结果
同样 也是包含多少呢
也是包含这么20条记录
因为它是总共有10折
然后什么
每一折有什么训练集 有测试集
它相应的性能指标我们都可以看得出来
比如说它的错误率是0.30
这一折的话错误率多少
训练集是0.18 但测试集是0.25等等
当然最终的表现的话我们
都得看它的一个总体的一个平均值
在训练集上的平均值
在测试集上平均值
本次课到此结束
谢谢大家
-第1章 气象万千、数以等观
--第1章 作业
-第2章 所谓学习、归类而已
--第2章 作业
-第3章 格言联璧话学习
--第3章 作业
-第4章 源于数学、归于工程
--第4章 作业
-讨论题
-第5章 工欲善其事、必先利其器
--第5章 作业
-第6章 基础编程——用别人的包和函数讲述自己的故事
--6.1 编程环境
--6.4 控制流
--第6章 作业
-第7章 数据对象——面向数据对象学习R语言
--第7章 作业
-第8章 人人都爱tidyverse
--第8章 作业
-第9章 最美不过数据框
--第9章 作业
-第10章 观数以形
--第10章 作业
-第11章 相随相伴、谓之关联
--11.1 导引
--第11章 作业
-第12章 既是世间法、自当有分别
--12.1 导引
--第12章 作业
-第13章 方以类聚、物以群分
--13.1 导引
--第13章 作业
-第14章 庐山烟雨浙江潮
--第14章 作业