关于评估指标的一点思考

今天在分析数据的时候遇到一个样本数据不平衡的问题,以前在学习机器学习的时候有学到样本数据不平衡的原因和解决方法,不过因为那时候并不在意,觉得数据多得是嘛,不平横就随机训练和丢弃一些就好了,根本没仔细考虑到数据量小的情况。

有时候数据量呈现8比2的比例,也就是正样本与负样本的数量比为8:2,这时候在做机器学习就需要考虑如何合理的训练与分配数据了,值得一提的是,样本不平衡的情况十分常见。

不过样本不平衡会带来什么问题呢,这也是今天想讲的一点故事,举个十分简单又实际的例子

假设有10个人 8个好人和2个坏人
如果存在一个机器学习模型,遇到人就说好人,那么模型的预测结果将会是 好人, 好人, ......

利用准确率的计算方式 ACC = 8个好人预测正确/总共有10个人 = 0.8 得这个机器模型的预测准确率为80%
有没有感觉很奇怪,我猜硬币才只有50%正确率,怎么这个模型只说好人,准确率这么高的?
而且,这个模型是不是不能够识别坏人?

其实原因有两个 1. 数据不平衡 2. 准确率不适合数据不平横的情况

准确率是非常直观的度量指标,也是我们接触的最多的,不过在对付数据不平衡的问题下,准确率就难以反映真实情况。

准确率,在二分类任务中,因为总共只有好或者坏,所以 准确率 = 准确预测好人的能力 + 准确预测坏人的能力

不知道大家有没有发现,虽然不能够识别坏人,但是准确预测好人的能力这个可以拉分,只要好人多过坏人,那么我就可以保证我的预测能力大于50%,如果全是好人,那么我的准确率就100%。准确率就是这么被直接拉高了。因为在数据不平衡的情况下,预测好人的能力的占比可能大于一半,从而掩盖了预测坏人能力比较弱的事实。

这就是为什么在数据不平衡的情况下,准确率无法体现模型的真实水平。
不过还是有多人直接在数据不平衡下用准确率作为指标,在某些准确率高达95,96%的实验结果上,连样本数据都是不平横的。这样的准确率是无意义的。

对于上述情况,我们可以从两个方面解决 1. 解决数据不平衡问题 2. 细化评估指标

  • 解决数据不平衡问题
    最简单的方法就是补6个坏人平衡数据,那么总共就有16个人,模型的准确率就变为 8/16 = 0.5,一半准确率那就等于随便猜了。数据不平横的方法还有很多,以后会深入研究

  • 细化评估指标
    在现实生活中,解决数据不平衡数据是一个比较麻烦的问题,如果可以通过其他更详细的指标来评估,而不是去修改数据本身,那么是比较节省时间的。

于是乎才有我们今天要说的东西 – 评估指标 (这才是今天的重点)

我们知道 准确率 = 准确预测好人的能力 + 准确预测坏人的能力。把准确率细化为两个指标,分别是 指标1. 准确预测好人的能力 , 指标2. 准确预测坏人的能力

其实,准确预测好人的能力实际上就是敏感度,而准确预测坏人的能力就是特异度,都是机器学习上非常常用的度量指标

  1. 敏感度 sensitivity
    样本实际值为好人,模型把它预测为好人的正确率。公式: Sensitivity = True positives / (True positives + False negatives)

  2. 特异度 specificity
    样本实际值为坏人,模型把它预测为坏人的正确率。公式:Specificity = True negatives / (True negatives + False positives)

如果敏感度高,特异度低,那么模型只会看好人,不会看坏人。
如果敏感度低,特异度高,模型只会看坏人,不会看好人。
只有敏感度和特异度都比较高,模型既可以分辨好人也可以分辨坏人,才是一个有用的模型。

我们将这两个指标应用到上面的例子上,敏感度 = 8/8 = 1 ,特异度 = 0/2 = 0
可以看出,虽然敏感度特别高,但是特异度为0,说明这个模型不具备识别坏人的能力,所以这个模型是没有用的。

如果理解了敏感度和特异度,我们就可以开始了解一下混淆矩阵,解释一下上面的公式在说什么

m

左边 Real 表示实际值, Predict 表示预测值
我们把0看为好人,1看为坏人,那么
+ a:实际为好人,预测也为好人 – 叫作 真阳性 True Positive
+ b:实际为好人,预测为坏人 – 叫作假阴性 False Negative,也叫作误报 Type 1 error
+ c:实际为坏人,预测为好人 – 叫作假阳性 False Positive,漏报 Type 2 error
+ d:实际为坏人,预测为坏人 – 叫做真阴性 True Negative

fromwiki.png

根据混淆矩阵,我们来看看上述准确率,敏感度等是怎么计算的

准确率 = 所有预测对的 / 所有样本 = (真阳性 + 真阴性)/ (真阳+真阴+假阳+假阴) 
敏感度 = 好人被预测对的 / 所有真实是好人 = 真阳 / (真阳 + 假阴)
特异度 = 坏人被预测对的 / 所有真实是坏人 = 真阴 / (真阴 + 假阳) 

不仅如此,我们还可以根据混淆矩阵得出其他指标,比如精确率(查出来的好人有多少真好人)
精确率 = 好人被预测对的 / 真实是好人 + 错认为好人 = 真阳 / (真阳 + 假阳)

这些概念特别容易混淆,所以多看看图理解是非常有用的。

敏感度也称为召回率,召回率和精准率这两个概念有一定联系
因为公式上分子都是真阳,不一样的是敏感度的分母是加上假阴,精确率是加上假阳
那么敏感度侧重于没认出来(假阴), 而精准度侧重于认过头了(假阳)

所以敏感度和精准度可以看做天平的两端,如果敏感度过高,几乎所有好人都被认出来,好人的标准变低了,但是就很容易误把坏人也当好人
反之,如果精准率过高,预测好人的标准变高,就容易把好人当坏人了。

当然不是说是不好的意思,只是在这两个指标相比较中,可以看出你的模型是倾向于那种情况。(好人的标准的高低)

但是当这两个指标发生冲突时,我们很难在模型之间进行比较。比如,我们有如下两个模型A、B,A模型的召回率高于B模型,但是B模型的精准率高于A模型,A和B这两个模型的综合性能,哪一个更优呢

人们就提出了F分数,意思是: 你们两个别争了,平均一下得了

F-socre

F分数为精准率与召回率的调和平均值,所以一般就看这个综合数值就可以了,不过如果你想具体到细节问题,还是需要看召回率和精准率的。

不过,召回率和精准率是基于好人的标准上的,对于坏人的识别能力,还是得看我们的特异度

我们之前的“下一代”智能模型(全部认好人)的召回率高达 100%,精准率高达 80% !!!
但是还是没有用,对于一个模型,你得考虑他两方面的能力,只会说好人,不会认坏人从一定程度上来讲这个模型还是没有分辨力的。(不过上述例子是一个特例,不能说这两个指标没有用)

所以我们一般同时考虑敏感度与特异度,也就是既要会认好人,同时也要会认坏人,才可以说这个模型是具有分辨力的。

有没有一个指数类似于F分数,综合一下这两位得出综合性能呢?
好像是没有,…..

不过,我们有ROC曲线!在说ROC曲线之前,我们先来谈一谈什么会同时影响敏感度与特异度

答案就是 好人标准,我们已经知道好人标准会影响敏感度(召回率),同时,好人标准也会影响特异度,因为好人的标准越高,就越多坏人可以被认出来,特异度就变高(真阴变多,假阳减少),但是召回率就变低,因为有些不是特别好的好人可能也被当为坏人了。相反,好人标准变低,坏人容易被认为好人(真阴减少,假阳变多),特异度就变低,同时召回率就升高了(真阳变多,假阴变少)。

所以 特异度和敏感度其实某种意义上也是相对概念

在固定模型下,只需要调整好人标准,那么就可以调整模型的敏感度和特异度

而这个好人标准,就是机器学习中所说的阈(yu)值

  • 这个人是好人的概率 小于 阈值 – 模型就认为这个人是坏人
  • 这个人是好人的概率 大于 阈值 – 模型就认为这个人是好人

阈值一般用0.5,因为概率最大是1,最小是0,如果概率大于或者小于一半(0.5)就说明更偏向于另一方。

有没有这么一个东西,可以很直观方便的看出不同好人标准(阈值)下对应的敏感度和特异度是多少呢?

就是ROC曲线

TIM截图20181218142950.png

纵坐标是敏感度,横坐标是假阳性(1- 特异度),我们可以直观从图看出,每个点对应敏感度与特异度
所以图中最靠左上的点,也就是敏感度和特异度同时最高的点,那时候的阈值是综合来说最好的。

同时,ROC曲线可以比较模型之间的性能,比如一个模型的ROC曲线完全包住另一个模型的ROC曲线,那就说明这个模型的敏感度和特异度整体上高于另一个模型,所以模型性能也更好。如果不相上下,就比图如两条曲线,那么这个模型其实是差不多的。

接下来我们就尝试用PYTHON画一些ROC曲线
做一些前期的准备工作,比如数据生成,导入库之类的

import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, auc, accuracy_score
from sklearn.cross_validation import train_test_split
from sklearn.svm import SVC
import random
import numpy as np

positive_sample = [random.randint(1,15) for i in range(1000)] # 正样本为1-15
negative_sample = [random.randint(5,20) for i in range(1000)] # 负样本为5-20

p_y = [0 for i in range(1000)]
n_y = [1 for i in range(1000)]

X = np.array(positive_sample + negative_sample).reshape(-1,1)
y = p_y + n_y

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3,random_state=1)

def plot (fpr, tpr, fpr_2, tpr_2):
    plt.figure()
    lw = 2
    plt.figure(figsize=(10,10))
    plt.plot(fpr, tpr, color='darkorange',
             lw=lw, label='ROC curve LR')
    plt.plot(fpr_2, tpr_2, color='darkgreen',
             lw=lw, label='ROC curve SVM')
    plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Receiver operating characteristic example')
    plt.legend(loc="lower right")
    plt.show()

正样本:生成1000个数值1-15之间
负样本:生成1000个数值在5-20之间

接下来对数据进行训练

clf_1 = LogisticRegression()
clf_2 = SVC()
# 训练两个模型,方便等下的对比

clf_1.fit(X_train, y_train)
clf_2.fit(X_train, y_train)

score_1 = clf_1.decision_function(X_test)
fpr, tpr, threshold = roc_curve(y_test, score_1)
score_2 = clf_2.decision_function(X_test)
fpr_2, tpr_2, threshold_2 = roc_curve(y_test, score_2)

fpr是假阳性(1-特异性),tpr是召回率,threshold是阈值

exp

你会看到多组 假阳性-召回率-阈值的组合

这是因为在不同阈值条件下,敏感度和特异度是会变化的。

接着我们来绘制ROC曲线

output_6_1.png

可见,黄色曲线已经包住了绿色曲线,所以黄色曲线对应的模型的综合性能更好
一般我们用AUC,也就是ROC曲线下围成的面积,来表示模型的性能。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据