【3】无监督学习--3--聚类--聚类评估

其目标是实现组内的对象相互之间是相似的(相关的),而不同组中的对象是不同的(不相关的)。组内的相似性越大,组间差别越大,聚类效果就越好。既然聚类是把一个包含若干文档的文档集合分成若干类,像上图如果聚类算法应该把文档集合分成3类,而不是2类或者5类,这就设计到一个如何评价聚类结果的问题。

分类器的分类情形

假如我们用NaiveBayes算法训练出一个系统,可以用来识别邮件是否为垃圾,对于一封邮件的识别,有如下4种情况:

邮件为垃圾,系统正确识别为垃圾 邮件为垃圾,系统错误识别为非垃圾 邮件不是垃圾,系统正确识别为非垃圾 邮件不是垃圾,系统错误识别为垃圾 如上的4种情形,如果用一个2X2的表格概括起来,就是大名鼎鼎的Confusion Matrix:

其中True和False是对于评价预测结果而言,也就是评价预测结果是正确的(True)还是错误的(False)。而Positive和Negative则是对分类器的预测结果而言,Positive为预测是垃圾,Negative是预测为非垃圾。为了表达方便,一般会以他们的缩写TP,FP,FN,TN代替。那么上面提到的邮件分类四种情形对应一下就变成如下:

  1. 邮件为垃圾,系统正确识别为垃圾(TP正确地预测为垃圾)
  2. 邮件不是垃圾,系统错误识别为垃圾(FP错误的预测为垃圾)
  3. 邮件不是垃圾,系统正确识别为非垃圾(TN正确地预测为非垃圾)
  4. 邮件为垃圾,系统错误识别为非垃圾(FN错误地预测为非垃圾)

一、灵敏度、特异性、精度、准确率

  • 灵敏度(Sensitivity) : TP/(TP+FN)
    • 也称为查全率(recall)
    • 数据集共有13只爬行动物,其中10只被正确预测为爬行动物,灵敏度为10/13
  • 特异度(Specificity): TN/(TN+FP)
    • 数据集中有10只非爬行动物,其中8只被预测为非爬行动物,特异度为8/10
  • 精度(Precision): TP/(TP+FP)
    • 分类器预测了12只动物为爬行动物,其中10只确实是爬行动物,精度为10/12
  • 准确率(Accuracy): (TP+TN)/(TP+TN+FN+FP)
    • 数据集包含23只动物,其中18只预测为正确的分类,准确率为18/23

Accuracy为什么还不够?

上面的Confusion Matrix只是对于一封邮件而言,那么对于多封邮件的预测结果情形,TP,FP,FN,TN则是各种情形的计数结果,假如我们有100封测试邮件,其中50封为垃圾50封为非垃圾,就可能会预测出TP=40封,TN=40封,FP = 10封,FN = 10封。那么用Accuracy来衡量的话,就会得到:

$$ Accuracy = \frac{TP+TN}{TP+TN+FN+FP} = 0.8$$

感觉没多大问题。上面的情形是我们假设预测结果中垃圾邮件和非垃圾邮件是比较均匀的,也就是数量是差不多的。但是真实情形有可能是100封测试邮件中,垃圾邮件为90封,非垃圾邮件为10封,这种也就是常常说的skewed distribution。那么如果有一个trivial分类器,他永远只输出垃圾,那么TP=98,TN=0,FN=0,FP=2,此时假如还是用Accuracy来评价的话,我们可以得到:

$$ Accuracy = \frac{TP+TN}{TP+TN+FN+FP} = 0.98$$

,结果出乎意料地好,如果我们只是用Accuracy作为唯一的判断标准,那么这样trivial的分类器定当入选,这就是经常被提到的Accuracy Paradox现象。我们发现Accuracy并不能很好体现TN=FN=0这种异常现象,所以我们有必要寻求其他Metric来更好的刻画Confusion Matrix所表达的内容。

Precision-Recall

上一小节我们通过Accuracy Paradox说明Accuracy作为唯一指标的不足,还有Accuracy有时并不能很好地刻画Confusion Matrix所表达的内容。这一小节我们将了解信息检索领域中研究者如何对Confusion Matrix进行拆解,得到了Precision-Recall,从而对质和量得以把控,还有如何组合Precision-Recall得到一个对Confusion Matrix的一个统一解释数字。

对于信息检索问题,我们更关注搜索到的内容与用户想要搜索的内容是否相关。如果有测试数据的话,我们是希望搜索引擎返回的内容与用户要找的越相关越好,另一方面我们想让搜索引擎返回越多的相关内容越好,所以就有了Precision和Recall这两个指标。这两个指标中文翻译很多,但是很难有直观地翻译道明其中奥妙,所以这里也不尝试翻译,大家心领神会吧。但是Precision和Recall的侧重点是不一样的:

  • Precision是返回的相关文献(TP)占总返回文献(TP+FP)的比例,所以Precision越大,返回的结果质量越高
  • Recall是返回的相关文献(TP)占总相关文献(TP+FN)的比例,所以Recall越大,返回相关的文献就越多

一个是强调质量(返回结果只要给我相关的就好),一个是强调数量(返回相关的结果要尽可能越多越好)。按照上面的定义,我们根据Confusion Matrix可以给出Precision和Recall的数学形式:

$$ Precision = \frac{TP}{TP+FP} $$ $$ Recall = \frac{TP}{TP+FN} $$

对于生硬的数学形式,wiki上面有一幅插图很形象地表达了Precision和Recall的含义:

这两个指标哪个值高更好呢?在实际应用中,必须根据具体需求设定,举个例子,对于识别是否为超市会员和非会员的系统,我们当然希望Recall越高越好,虽然这样会混进一些非会员,但是对于超市来说并非损失,就给个折扣而已,还是赚钱。但是对于一个银行的门禁系统,肯定是要Precision越高越好,Recall可以不用那么严格,这样虽然会让你打卡打多几次,但是总比把一个外人放进来安全吧。所以根据实际业务需求,在Precision和Recall上做权衡是很有必要的。有的人说,我很懒,我不想自己手工做权衡,能不能综合Precision和Recall得到一个指标呢?答案是有的。

备注

对于非平衡的数据集,以上指标并不能很好的评估预测结果。 非平衡的数据集是指阳性数据在整个数据集中的比例很小。比如,数据集包含10只爬行动物,990只非爬行动物,此时,是否预测正确爬行动物对准确率影响不大。

更平衡的评估标准包括:马修斯相关系数(Matthews correlation coefficient)和 ROC曲线。

马修斯相关性系数定义为

Matthews的原文中是这样描述这个系数的:

A correlation of: 
C = 1 indicates perfect agreement, 
C = 0 is expected for a prediction no better than random, and 
C = -1 indicates total disagreement between prediction and observation

也就是说系数为1的时候,分类器是完美的,0的时候分类器和随机分类器没差,-1的时候分类器是最差的,所有预测结果和实际相反。那这样的系数是如何解决imbalance data问题呢?我们紧接着看他的数学形式:

$$ MCC = \frac{TP * TN - FP * FN}{ \sqrt{(TP +FP)(TP +FN)(TN + FP)(TN +FN) }}$$

需要注意的是,分母中任意一对括号相加之和如果为0,那么整个MCC的值就为0。我们再回归我们的老问题,对于TP=98,TN=0,FN=0,FP=2,由于TN,FN同时为0,那么MCC则为0,说明我们简单粗暴的方式和随机分类器没有异同。 MCC本质上是一个观察值和预测值之间的相关系数,维基百科上是这样评价MCC的:

While there is no perfect way of describing the confusion matrix of true and false positives and negatives by a single number, the Matthews correlation coefficient is generally regarded as being one of the best such measures

至此,我们解决了Accuracy Paradox问题,那么是不是说明我们可以只用MCC作为唯一标准呢,聪明的读者应该学会说不是了吧,Precsion和Recall可是很好地强调质和量的关系,并且能够通过F Measure体现出来,在很多情况下,用Accuracy作为指标也是一个不错的简单选择。

二、ROC 曲线

三、purity评价法:

purity方法是极为简单的一种聚类评价方法,只需计算正确聚类的文档数占总文档数的比例:

其中Ω = {ω1,ω2, … ,ωK}是聚类的集合ωK表示第k个聚类的集合。C = {c1, c2, … , cJ}是文档集合,cJ表示第J个文档。N表示文档总数。

如上图的purity = ( 3+ 4 + 5) / 17 = 0.71

其中第一类正确的有5个,第二个4个,第三个3个,总文档数17。

purity方法的优势是方便计算,值在0~1之间,完全错误的聚类方法值为0,完全正确的方法值为1。同时,purity方法的缺点也很明显它无法对退化的聚类方法给出正确的评价,设想如果聚类算法把每篇文档单独聚成一类,那么算法认为所有文档都被正确分类,那么purity值为1!而这显然不是想要的结果。

四、RI评价法:

实际上这是一种用排列组合原理来对聚类进行评价的手段,公式如下:

其中TP是指被聚在一类的两个文档被正确分类了,TN是只不应该被聚在一类的两个文档被正确分开了,FP只不应该放在一类的文档被错误的放在了一类,FN指不应该分开的文档被错误的分开了。对上图

TP+FP = C(2,6) + C(2,6) + C(2,5) = 15 + 15 + 10 = 40

其中C(n,m)是指在m中任选n个的组合数。

TP = C(2,5) + C(2,4) + C(2,3) + C(2,2) = 20

FP = 40 - 20 = 20

同理:

TN+FN= C(1,6) * C(1,6)+ C(1,6)* C(1,5) + C(1,6)* C(1,5)=96

FN= C(1,5) * C(1,3)+ C(1,1)* C(1,4) + C(1,1)* C(1,3)+ C(1,1)* C(1,2)=24

TN=96-24=72

所以RI = ( 20 + 72) / ( 20 + 20 + 72 +24) = 0.68

五 F值评价法(F-Measure)

这是基于上述RI方法衍生出的一个方法,

注:p是查全率,R是查准率,当β>1时查全率更有影响,当β<1时查准率更有影响,当β=1时退化为标准的F1,详细查阅机器学习P30

RI方法有个特点就是把准确率和召回率看得同等重要,事实上有时候我们可能需要某一特性更多一点,这时候就适合F值方法

对于想自动化权衡Precision和Recall的人,F Measure是他们的福音。首先介绍最简单也是最常用的合并Precision和Recall的方式——F1meaure或F1score,办法很简单,就像两个电阻并联求总电阻的形式相似,将Precision和Recall并联一下,就近似得到它们的加权平均:

$$ F1 = 2∗ \frac{Precision∗Recall}{Precision+Recall} $$

,这样的其实是均匀地加权平均,利用这个指标,我们回归到上面提到的Accuracy缺陷的例子,假如分类器一直输出为垃圾,对于有100封测试邮件其中垃圾为98封的测试集:TP=98,TN=0,FN=0,FP=2,那么

$$ Accuracy = \frac{TP+TN}{TP+TN+FN+FP} = 0.98$$

我们计算一下Precision和Recall,发现

$$ Precision = \frac{TP}{TP+FP} =0.98 $$ $$ Recall = \frac{TP}{TP+FN} = 1 $$

那么F1计算会得到:

$$ F1 = 2∗ \frac{Precision∗Recall}{Precision+Recall} = 2 * \frac{0.98*1}{1+0.98} = 0.989$$

。看来利用Precision-Recall或者是它们的组合F1都无法解决上面提到的skewed distribution问题(网上大量文章都是以Accuracy Paradox的问题引出Precision-Recall到F measure,让人以为F measure可以应对那种问题,其实不然)。但是我们通过拆分Confusion Matrix,再进行组合,可以使得我们更好得把握质和量,所以种种迹象都表面Precision-Recall和F measure比简单的Accuracy具有更细粒度的解释效果。

四、聚类分析结果的降维可视化—TSNE

我们总喜欢能够直观地展示研究结果,聚类也不例外。然而,通常来说输入的特征数是高维的(大于3维),一般难以直接以原特征对聚类结果进行展示。而TSNE提供了一种有效的数据降维方式,让我们可以在2维或者3维的空间中展示聚类结果。

示例代码:接上文示例代码

#-*- coding: utf-8 -*-
#接k_means.py
from sklearn.manifold import TSNE
tsne = TSNE()
tsne.fit_transform(data_zs) #进行数据降维
tsne = pd.DataFrame(tsne.embedding_, index = data_zs.index) #转换数据格式
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
#不同类别用不同颜色和样式绘图
d = tsne[r[u'聚类类别'] == 0]
plt.plot(d[0], d[1], 'r.')
d = tsne[r[u'聚类类别'] == 1]
plt.plot(d[0], d[1], 'go')
d = tsne[r[u'聚类类别'] == 2]
plt.plot(d[0], d[1], 'b*')
plt.show()

参考资料:

个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn

Sam avatar
About Sam
专注生物信息 专注转化医学