zoukankan      html  css  js  c++  java
  • 机器学习性能指标之ROC和AUC理解与曲线绘制

    一. ROC曲线

    1、roc曲线:接收者操作特征(receiveroperating characteristic),roc曲线上每个点反映着对同一信号刺激的感受性。

    横轴:负正类率(false postive rate FPR)特异度,划分实例中所有负例占所有负例的比例;(1-Specificity)

    纵轴:真正类率(true postive rate TPR)灵敏度,Sensitivity(正类覆盖率)

    2、针对一个二分类问题,将实例分成正类(postive)或者负类(negative)。但是实际中分类时,会出现四种情况.

    (1)若一个实例是正类并且被预测为正类,即为真正类(True Postive TP)

    (2)若一个实例是正类,但是被预测成为负类,即为假负类(False Negative FN)

    (3)若一个实例是负类,但是被预测成为正类,即为假正类(False Postive FP)

    (4)若一个实例是负类,但是被预测成为负类,即为真负类(True Negative TN)

    TP:正确的肯定数目
    
    FN:漏报,没有找到正确匹配的数目
    
    FP:误报,没有的匹配不正确
    
    TN:正确拒绝的非匹配数目
    

    列联表如下,1代表正类,0代表负类:

    由上表可得出横,纵轴的计算公式:

    (1)真正类率(True Postive Rate)TPR: TP/(TP+FN),代表分类器预测的正类中实际正实例占所有正实例的比例。

    (2)负正类率(False Postive Rate)FPR: FP/(FP+TN),代表分类器预测的正类中实际负实例占所有负实例的比例。

    (3)真负类率(True Negative Rate)TNR: TN/(FP+TN),代表分类器预测的负类中实际负实例占所有负实例的比例,TNR=1-FPR。

    假设采用逻辑回归分类器,其给出针对每个实例为正类的概率,那么通过设定一个阈值如0.6,概率大于等于0.6的为正类,小于0.6的为负类。对应的就可以算出一组(FPR,TPR),在平面中得到对应坐标点。随着阈值的逐渐减小,越来越多的实例被划分为正类,但是这些正类中同样也掺杂着真正的负实例,即TPR和FPR会同时增大。阈值最大时,对应坐标点为(0,0),阈值最小时,对应坐标点(1,1)。

    如下面这幅图,(a)图中实线为ROC曲线,线上每个点对应一个阈值。

    横轴FPR:1-TNR,1-Specificity,FPR越大,预测正类中实际负类越多。

    纵轴TPR:Sensitivity(正类覆盖率),TPR越大,预测正类中实际正类越多。

    二. 如何画roc曲线

    假设已经得出一系列样本被划分为正类的概率,然后按照大小排序,下图是一个示例,图中共有20个测试样本,“Class”一栏表示每个测试样本真正的标签(p表示正样本,n表示负样本),“Score”表示每个测试样本属于正样本的概率。

    接下来,我们从高到低,依次将“Score”值作为阈值threshold,当测试样本属于正样本的概率大于或等于这个threshold时,我们认为它为正样本,否则为负样本。举例来说,对于图中的第4个样本,其“Score”值为0.6,那么样本1,2,3,4都被认为是正样本,因为它们的“Score”值都大于等于0.6,而其他样本则都认为是负样本。每次选取一个不同的threshold,我们就可以得到一组FPR和TPR,即ROC曲线上的一点。这样一来,我们一共得到了20组FPR和TPR的值,将它们画在ROC曲线的结果如下图:

    上图ROC曲线一些点怎么得到的分析

    具体分析如下:
    可以看出实际类别为P的有10个,实际类别为N的也有10个

    • Score = 0.9时,样本1被认为(预测)是正类(P)。此时根据混淆矩阵有: TP = 1, FN = 9, FP = 0,TN = 10。
      这是因为样本1为正类(P)被正确预测为正类(P),因此TP = 1; 其他实际正类类别(class为P的)样本有9个,它们被预测为负类(N)了,因此FN = 9; 样本实际为负类(N)的,被预测为正类(P)的没有一个,因此FP = 0, 其他实际负类类别(class为N的)样本有10个,它们全部被预测为负类(N)了,因此TN = 10 。故TPR = 1/(1+9) = 0.1
    • Score = 0.8时, 同理有: TP = 2, FN = 8,FP = 0, TN = 10。 故TPR = 2/(2+8) = 0.2
    • Score = 0.7时,有: TP = 2, FN = 8,FP = 1, TN = 9。 故TPR = 2/(2+8) = 0.2
    • Score = 0.6时,有:TP = 3, FN = 7,FP = 1, TN = 9。 故TPR = 3/(3+7) = 0.3
      以此类推

    AUC(Area under Curve):Roc曲线下的面积,介于0.1和1之间。Auc作为数值可以直观的评价分类器的好坏,值越大越好。

    首先AUC值是一个概率值,当你随机挑选一个正样本以及负样本,当前的分类算法根据计算得到的Score值将这个正样本排在负样本前面的概率就是AUC值,AUC值越大,当前分类算法越有可能将正样本排在负样本前面,从而能够更好地分类。

    三 为什么使用Roc和Auc评价分类器

    既然已经这么多标准,为什么还要使用ROC和AUC呢?因为ROC曲线有个很好的特性:当测试集中的正负样本的分布变换的时候,ROC曲线能够保持不变。在实际的数据集中经常会出现样本类不平衡,即正负样本比例差距较大,而且测试数据中的正负样本也可能随着时间变化。下图是ROC曲线和Presision-Recall曲线的对比:

    在上图中,(a)和(c)为Roc曲线,(b)和(d)为Precision-Recall曲线。

    (a)和(b)展示的是分类其在原始测试集(正负样本分布平衡)的结果,(c)(d)是将测试集中负样本的数量增加到原来的10倍后,分类器的结果,可以明显的看出,ROC曲线基本保持原貌,而Precision-Recall曲线变化较大。

    绘制ROC曲线计算AUC值

    输入的数据集可以参考svm预测结果

    • case1: 只去前面36行样本数据,txt文件名为:evaluate_result2
    '''
    这里的.txt文件格式如:http://kubicode.me/img/AUC-Calculation-by-Python/evaluate_result.txt
    '''
    #绘制二分类ROC曲线
    import matplotlib.pyplot as plt 
    from math import log, exp, sqrt
    
    evaluate_result = './evaluate_result2.txt'
    
    db  = [ ]   #[score,nonclk,clk]
    pos, neg = 0, 0
    with open(evaluate_result, 'r') as fs:
        for line in fs:
            nonclk, clk, score = line.strip().split('	')  #变成列表list形式
            nonclk = int(nonclk)
            clk = int(clk)
            db.append([score,nonclk,clk])  
    
            score = float(score)
            neg = neg + nonclk
            #print("neg数量====", neg, end = '')
            pos = pos + clk
            #print(",  pos数量====", pos)
            
    #print("db= ",db)
    
    db = sorted(db, key=lambda x:x[0], reverse=True)  #对ad的score进行降序排序
    #print("db_reverse= ",db)
    
    #计算ROC坐标点
    xy_arr = [ ]
    tp, fp = 0., 0.
    
    for i in range(len(db)):
        fp += db[i][1]
        #print("fp ===", fp, end = '')
        tp += db[i][2]   
        #print(",   tp ===",tp)
        xy_arr.append([fp/neg, tp/pos])   #fp除以negative数目, tp除以positive数目。作为坐标点
    #print(xy_arr)  #[[0.06666666666666667, 0.0], [0.06666666666666667, 0.029411764705882353],...]
    
    # 计算曲线下面积
    auc = 0.
    prev_x = 0
    for x, y in xy_arr:   #  x= fp/neg, y = tp/pos
        if x != prev_x:
            auc += (x - prev_x) * y  #矩形面积累加
            prev_x = x
    
    print("The auc is %s" %auc)
    
    x = [ v[0] for v in xy_arr]
    y = [ v[1] for v in xy_arr]
    plt.title("ROC curve of %s (AUC = %.4f)" % ('svm',auc))
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")
    plt.plot(x, y)
    plt.show()
    

    输出:
    The auc is 0.43137254901960786

    • case2: 取全部数据,txt文件名为:evaluate_result
    '''
    这里的.txt文件格式如:http://kubicode.me/img/AUC-Calculation-by-Python/evaluate_result.txt
    '''
    #绘制二分类ROC曲线
    import matplotlib.pyplot as plt 
    from math import log, exp, sqrt
    
    evaluate_result = './evaluate_result.txt'
    
    db  = [ ]   #[score,nonclk,clk]
    pos, neg = 0, 0
    with open(evaluate_result, 'r') as fs:
        for line in fs:
            nonclk, clk, score = line.strip().split('	')  #变成列表list形式
            nonclk = int(nonclk)
            clk = int(clk)
            db.append([score,nonclk,clk])  
    
            score = float(score)
            neg = neg + nonclk
            #print("neg数量====", neg, end = '')
            pos = pos + clk
            #print(",  pos数量====", pos)
            
    #print("db= ",db)
    
    db = sorted(db, key=lambda x:x[0], reverse=True)  #对ad的score进行降序排序
    #print("db_reverse= ",db)
    
    #计算ROC坐标点
    xy_arr = [ ]
    tp, fp = 0., 0.
    
    for i in range(len(db)):
        fp += db[i][1]
        #print("fp ===", fp, end = '')
        tp += db[i][2]   
        #print(",   tp ===",tp)
        xy_arr.append([fp/neg, tp/pos])   #fp除以negative数目, tp除以positive数目。作为坐标点
    #print(xy_arr)  #[[0.06666666666666667, 0.0], [0.06666666666666667, 0.029411764705882353],...]
    
    # 计算曲线下面积
    auc = 0.
    prev_x = 0
    for x, y in xy_arr:   #  x= fp/neg, y = tp/pos
        if x != prev_x:
            auc += (x - prev_x) * y  #矩形面积累加
            prev_x = x
    
    print("The auc is %s" %auc)
    
    x = [ v[0] for v in xy_arr]
    y = [ v[1] for v in xy_arr]
    plt.title("ROC curve of %s (AUC = %.4f)" % ('svm',auc))
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")
    plt.plot(x, y)
    plt.show()
    

    输出:
    The auc is 0.5788039199235478

    参考资料:

  • 相关阅读:
    UIView用户事件响应
    登录模块之静态登录
    对C# 中Readonly的再认识
    [Android学习笔记4]四大应用组件之一:Service 上
    修改XPMenu让ToolButton在Down=True时正确显示
    通过程序预览Office文档
    发掘ListBox的潜力(三):显示即时提示(Tips)
    从网络上下载文件的两种方法
    解决窗体闪烁问题的方法
    C/C++中动态链接库的创建和调用
  • 原文地址:https://www.cnblogs.com/HongjianChen/p/9612882.html
Copyright © 2011-2022 走看看