数据挖掘(三)分类模型的描述与性能评估,以决策树为例

来源:互联网 发布:python怎么上传文件 编辑:程序博客网 时间:2024/04/28 11:09

关于分类的第一部分我们要讲一些关于分类的基本概念,然后介绍最基本的一种分类模型-决策树模型,再基于此讨论一下关于分类模型的性能评估。

===============================================================================

分类的直观理解就是给定样本的各类属性,根据我们的模型或者某种规则将样本划分到若干不同的类别中,划分目标是离散的就叫分类,如果是连续的我们就称之为回归。所以,分类的官方定义就是:通过学习得到一个目标函数f,把每个属性集x映射到一个预先定义的类标号中。

分类模型按照目的划分可分为描述性建模和预测性建模。所谓描述性就是建立了一个分类模型之后,我们就可以得到哪些特征对哪些类别有决定性的作用,是一个解释性的工具,而预测性是指建立完模型后我们拿来用在一个未知的样本,确定它的类别。还有要说的一点就是,分类技术适合用于二元或者标称类型的数据集,因为分类不考虑隐含在目标类中的序数关系,仅仅考虑样本各属性上的异同。

就像上面所说的,分类模型其实同时具有描述性和预测性,描述性要求模型可以最大程度上符合训练数据,而预测性则要求模型在未知数据上有较好的预测能力。因此训练误差不是我们唯一的判别标准,较好的泛化能力也是我们的主要诉求。所以在训练分类模型的时候,我们通常将数据划分成训练集和检验集,其中训练集用于建立模型,验证集用于检验模型的泛化能力,当然这其中训练集和检验集的划分还涉及到很多问题,比如说数据的不平衡啊,样本是否符合同一分布等等,这里我们先不讨论这些问题,但是得知道划分训练集和检验集的必要性。

对于二元分类模型的性能通常根据模型正确和错误预测样本计数进行评估,通常这些数字被存放在一个2*2的矩阵中,如下所示,我们称之为混淆矩阵。

 

Predicted

1

0

Actual

1

a

b

0

c

d

其中,a,b,c,d分别是预测为1,0而实际是1,0的样本个数,与混淆矩阵相关的概念有以下这么几个

准确率:正确预测数除以预测总数。(a+d)/(a+b+c+d)

错误率:错误预测数除以预测总数。(b+c)/(a+b+c+d)

精确率:正确预测为正类的样本数除以实际正类的的数目。a/(a+c)

召回率:正确预测为正类的样本数除以预测正类的数目。a/(a+b)

混淆矩阵确实叫人有点混淆,其中比较有意思的就是精确率和召回率了,通俗地讲,这两个对应的就是“找的准”和“找的全”,精确率是指预测为正的样本中真正为正的样本的概率,召回率是指实际为正的样本中被预测为正的样本的概率。在知乎上看到一位同学举得一个例子,预测一个人到底是不是罪犯的时候,我们抱着不冤枉一个好人的原则,可能要求模型的精确率比较高,这样找出来的罪犯基本都是罪犯,尽管有可能召回率低导致漏掉一些真正的罪犯,但这样可以尽量避免冤枉好人;另一个场景,预测地震的时候,我们希望召回率尽量高,因为这样我们可以避免漏过任何一次地震,尽管有可能导致精确率不高有很多次误报,但我们依然不希望有任何一次地震被漏报。说了这么多,相信你对这些二元分类模型的描述有点概念了,接下来我们讲讲如何在Python中划分训练检验集以及使用这些评价参数。这里用的一个数据集是sklearn自带的乳腺癌数据,根据30个特征将该肿瘤划分成初期和恶性,分类器用的决策树。

from sklearn.datasets import load_breast_cancerfrom sklearn.cross_validation import train_test_splitfrom sklearn import treefrom sklearn.metrics import confusion_matrix,classification_reportbreast_cancer=load_breast_cancer()data=breast_cancer.datatarget=breast_cancer.targetx_train,x_test,y_train,y_test=train_test_split(data,target,test_size=0.3,random_state=0)clf=tree.DecisionTreeClassifier(random_state=0)clf.fit(x_train,y_train)y_pred=clf.predict(x_test)print '============================================================'print 'Confusion Matrix'print confusion_matrix(y_test,y_pred,labels=[1,0])print '============================================================'print classification_report(y_test,y_pred,labels=[1,0])

结果如下

============================================================

Confusion Matrix

[[97 11]

 [ 459]]

============================================================

            precision    recall  f1-score  support

 

         1       0.96      0.90     0.93       108

         0       0.84      0.94     0.89        63

 

avg / total       0.92     0.91      0.91       171

 

通过confusion_matrix和classification_report这两个函数我们可以轻易地得到混淆矩阵及模型的精确率和召回率等等考虑在这个场景下,我们应该更注重模型的哪个参数呢?病人来看病我们的原则是啥,绝对不要漏掉一个真正的病患!因为有病误诊为没病可能会要了病人的命,而没病误诊为有病只会让别人喷我是个庸医,想忽悠别人多花点治疗费,所以从医生的立场来看,追求的是召回率一定要高,这才是负责任的做法。所以啊,学完这个是不是对医生有多点理解呢,毕竟别人背负了你生命的责任,条件允许的话就听医生的吧,该查的查,该治的治~

===============================================================================接下来我们介绍介绍决策树算法。之前在机器学习系列写的决策树在这里,传送门http://blog.csdn.net/sinat_22594309/article/details/59090895。

在那篇文章里我介绍了关于决策树两个最重要的部分,一是如何选择划分的属性,常用的标准有信息增益,信息增益比和Gini系数;二是决策树的损失函数,我们如何评价一个树的优劣,一方面看分类之后的信息增益,另一方面也要考虑模型的复杂度,同时这些也是我们优化决策树的依据。这些内容我在这里就不重复了,大家有兴趣可以自己去看看。

相信很多同学对熵总是有点不理解,虽然知道该他可以衡量一个系统的混乱度,但究竟是为什么,我这里可以给出一个比较直观的解释,相信可以帮助大家理解。

1 假设袋子里有四个球,A,B,C,D,每个被抽到的概率都为1/4,现在随机从袋子里拿一个,让你猜这是哪个球。

最佳的策略是啥呢?先猜是不是AB,是的话就在AB之中,再问是A吗就可确定;不是的话就在CD之中,再问是C吗就能确定。所以正确猜中的次数的期望就是1/4*2*4=2。

2 袋子里还是四个球,A,B,C,D,但是每个被抽中的概率变为1/2,1/4,1/8,1/8,还是随机从袋子里面拿一个猜是哪个球。

这个时候最佳的策略是啥?先猜是不是A,因为A概率最大很有可能一次猜中,不是的话就在BCD中,由于这三者中B概率最大,所以猜是不是B,不是的话,再猜是不是C就能确定是C还是D了。那么这样的话,猜中的次数的期望就是1/2*1+1/4*2+1/8*3*2=1.75。如果我们没有采取最佳的策略而是沿用1的策略呢,猜中的次数的期望就是1/2*2+1/4*2+1/8*2*2=2。

写到这里,不知道机智的你发现了没,每一种策略都对应了一个概率分布。以第二个问题为例,策略1对应的概率分布为(1/2,1/4,1/8,1/8),策略2对应的概率分布为(1/4,1/4,1/4,1/4),然后对应的猜中次数的期望就是数据的实际分布乘以策略对应概率分布倒数的对数之和,这个形式是不是有点眼熟呢,如果策略使用的是真是分布,那么这个猜中次数的期望就是熵。很明显,熵越大,猜中所需的次数越多,说明原始数据越混乱。但是如果采用策略所使用的不是真实分布,那么算出来的猜中次数期望会变大,我们将之称为交叉熵。然后不同策略对应的交叉熵之差,我们就称为相对熵。总结起来,如下:

真实分布为Pk(k=1,2,3,4…),策略所用分布为qk(k=1,2,3,4…)


此外,关于决策树的特点,我觉得可以稍微总结一下:

1 决策树是一种构建分类模型的非参数方法,它不假定类和其属性服从一定的概率分布。

2 决策树的建立并不需要特别大的计算开销,所以往往能很快完成构建,预测的速度也很快。

3 决策树相对比较容易解释,特别是在小数据集上。

4 决策树对冗余属性不是很敏感,因为一旦有某个属性被选中,与其相关的冗余属性会自动被忽略。

5 决策树的决策边界通常是直线,也就是只涉及到某一个属性,但其实也可以做斜决策树,边界为多个属性的组合,不过这样的计算开销会变得很大,因为属性组合是在太多,可以通过一开始构造符合属性然后使用正常决策树算法达到构建斜决策树的目的。

以上就是关于决策树的一些补充内容,很多人觉得决策树算法相对初级一点,实际使用也只是在一些比较小的数据集上,但是毕竟它是Random Forest,GDBT等各种树啊森林的基础,所以也别小看它啦。另外,只要你想,决策树在训练集上的正确率可以做到非常非常高,这就引来了我们下一个话题,过拟合及分类器性能评价。

首先什么是过拟合?通俗的来说,就是我们为了追求训练集上的错误率尽可能低,将模型构建的十分复杂,导致构建的模型在测试集上表现效果反而很差。导致这种情况的原因有哪些呢?

1、 噪声。由于过分追求训练集的正确率,导致训练集中的一些噪声或例外也被拟合在我们的模型之中,这样分类器在实际使用时出现较高的错误率就是不可避免的了。

2、 缺乏代表性的样本。这种情况通常出现在数据集比较小的情况,由于训练数据不够或者很不平衡,导致对于某些种类缺乏代表性样本,模型实际拿来预测的时候效果不好也就是意料之中的事儿了。

3、 多重比较过程。首先解释一下多重比较过程,假如预测某一件事成功的概率为0.1,对于某个个体来说,确实成功的概率不高,但假如让50个人同时来预测这件事,假设他们是独立的,那么50个人中有人成功的概率会很高。这在决策树构建的过程中也是这样,我们往往会设定一个阈值,然后用增益是否大于这个阈值来决定是否要拓展决策树,然而数据的属性是很多的,我们在选择的时候其实就相当于是一个多重比较的过程,这样找到一个划分节点的几率就会大大增加,但其实这样是有欺骗性的。

那么如何避免过拟合的情况发生呢?由于过拟合往往出现在对数据的过分拟合导致模型太复杂,所以在决策树中,决定是否添加一个节点的时候,除了训练误差我们还可以添加一个复杂度惩罚项,这样就综合考虑了训练误差和模型复杂度。

当然,在决策树种最常见的用于防止过拟合的方法就是剪枝了。

剪枝分为两种,一种是在决策树构建过程中进行剪枝。也就是通过设定增益的阈值或者叶节点的样本数等等,防止构建太深的决策树导致过拟合,但这也面临一个阈值选择的问题,因为阈值设置太大,导致对数据利用不足,容易欠拟合,但是阈值太小又起不到防止过拟合的作用。比较常见的做法是利用确认集,也就是将训练集划分成训练集和确认集,在训练集上用不同的超参数训练模型,然后在验证集上验证,取验证集上误差率最小的超参数所确定的模型。

既然说到了验证集,我们就顺便提一下交叉验证吧,交叉验证是一种很好的评估分类器性能的方法,当然根据这个性能评价我们可以完成比如上面所说的超参数选择,还有模型啊特征的选择。交叉验证具体的做法就是将训练集分成若干份,每次选择其中一份作为验证集,其他的用作训练集,这样的过程可以重复若干次,保证每份数据都被用作验证集一次,这样若干次计算出来的误差总和就可以作为我们评价一个分类器性能的参考。感觉平时最常用的就是10折交叉验证,极端情况也有留一验证,这样的做法虽然充分利用了数据来训练,但是检验的误差方差比较大。下面给出一个在Python中利用交叉验证来选择超参数的简单例子,还是用的上面的乳腺癌数据,优化的超参数是树的深度。

from sklearn.cross_validation import cross_val_scoreimport numpy as npfrom sklearn.datasets import load_breast_cancerfrom sklearn import treebreast_cancer=load_breast_cancer()data=breast_cancer.datatarget=breast_cancer.targetmax_depth=np.linspace(1,15,15,endpoint=True)scores_final=[]for depth in max_depth:    clf=tree.DecisionTreeClassifier(random_state=0,max_depth=depth)    scores=cross_val_score(clf,data,target,cv=10,scoring='accuracy')    print 'max_depth=%f ,scores=%f'%(depth,scores.mean())    scores_final.append(scores.mean())from matplotlib import pyplot as pltplt.plot(max_depth,scores_final,'r-',lw=2)plt.xlabel('max_depth')plt.ylabel('CV scores')plt.grid()plt.show()

结果如下

max_depth=1.000000 ,scores=0.892966

max_depth=2.000000 ,scores=0.921164

max_depth=3.000000 ,scores=0.910543

max_depth=4.000000 ,scores=0.922858

max_depth=5.000000 ,scores=0.928092

max_depth=6.000000 ,scores=0.924705

max_depth=7.000000 ,scores=0.919411

max_depth=8.000000 ,scores=0.917563

max_depth=9.000000 ,scores=0.917563

max_depth=10.000000 ,scores=0.917563

max_depth=11.000000 ,scores=0.917563

max_depth=12.000000 ,scores=0.917563

max_depth=13.000000 ,scores=0.917563

max_depth=14.000000 ,scores=0.917563

max_depth=15.000000 ,scores=0.917563


从上面画的图中我们可以看出,一开始由于树的深度比较浅,导致模型有点欠拟合,后来随着树的深度逐渐加深,我们看到交叉验证的得分逐渐上升,在树深度为5的时候得分达到最高。此后,树的深度继续增加但CV的得分反而降低了,原因就在于过于复杂的树有可能导致数据有点过拟合了,效果反而不好,这样我们最终选择的超参数-树的深度就是5。

还有一种就是在决策树完全构建完成后进行剪枝,通过用叶节点或者树中最常见的分代替子树,自下而上直至树不能被优化。后剪枝的效果往往比先剪枝好,但是当某个子树被剪后,用于它的计算资源就浪费了。

关于分类器的性能评价的话,除了上面所说的交叉验证之外,其它的多半是一些采样的方法,包括提前划分训练集和确认集啊,随机二次抽样或者说有放回抽样等等,我想大家有兴趣可以自己去看看,但最常用的应该还是交叉验证吧。

这次我们主要讨论了和分类相关的一些概念以及决策树模型的特点,最后讲了一些关于过拟合和分类器性能评估(主要是交叉验证)的内容,下一次会介绍一些所谓更加高级的分类算法,但是这次我们所提到的这些概念和方法下次还是用得到~

Have a nice day~~~


阅读全文
4 0