机器学习:python绘制P-R曲线与ROC曲线

Python绘制P-R曲线与ROC曲线

查准率与查全率

  P-R曲线,就是查准率(precision)与查全率(recall)的曲线,以查准率作为纵轴,以查全率作为横轴,其中查准率也称为准确率,查全率称为召回率,所以在绘制图线之前,我们先对这些进行大概的介绍。


  对于二分类问题,我们可以将样例根据其真实类别与学习器预测类别的组合划分为真正例(true positive),假正例(false positive),真反例(true negative),以及假反例(false negative)四种情形,我们也相应的用TP,FP,TN,FN来分别表示其对应的样例的数目,那么这四者相加起来的总和就是样例的总数目。这里我们可以使用混淆矩阵来表示分类结果,使其更加直观:
在这里插入图片描述
由图我们可以更加清晰了解这四种情形的含义。

TP:实际是正例预测结果也是正例。
FN:实际是正例,但是预测结果是反例。
FP:实际是反例,但是预测结果是正例。
TN:实际是反例并且预测结果也是反例。


  同样,查准率和查全率就是根据这些数据进行计算。书上对这两者的介绍采用西瓜模式,即查准率就是我们挑西瓜的时候我们挑出的西瓜中有多少比例是好瓜,查全率是所有的好瓜中有多少比例被挑出来了。可能这样说并不是很好理解,那我们可以这样想,查准率就是我们预测是对的中有多少是真的对了,也就是你认为是对的样例中有多少是真的对了;查全率则是在所有是真的样例中,你找出了多少,即你判断对了多少

  依据上面的介绍,我们也就可以相应的写出查准率和查全率的表达式。
查准率P的表达式
P = T P T P + F P P=\frac{TP}{TP+FP} P=TP+FPTP
查全率R的表达式
P = T P T P + F N P=\frac{TP}{TP+FN} P=TP+FNTP
因为由定义和上图我们可以得知TP+FP就是预测为正的实例数目,而TP+FN就是实际为正的实例数目。

  我们可以举个简单的例子,假设有100个西瓜,其中有80个好瓜,20个坏瓜,设置阈值,我们使用分类器预测,其预测60个好瓜,在这60个好瓜中,有50个是真的好瓜,也就是说50个预测正确,10个预测错误,所以 P = 50 50 + 10 = 0.833 P=\frac{50}{50+10}=0.833 P=50+1050=0.833
R = 50 50 + 30 = 0.625 R=\frac{50}{50+30}=0.625 R=50+3050=0.625

P-R曲线的绘制

  了解了查全率和查准率的相应的概念,我们就可以进行P-R曲线的绘制。我们知道,算法对样本进行分类的时候,都会有置信度,也就是表示该样本为正例的概率。然后通过选取合适的阈值,对样本概率进行划分,比如阈值为50%时,就是其置信度大于50%,那么它就是正例,否则它就是反例。

  这里绘制图线同样的道理,我们要产生随机的概率,表示每个样本例子为正例的概率,然后通过这些概率进行从大到小的排序,再按此顺序逐个样本的选择阈值,大于阈值的概率的样例为正例,后面的全部为反例。我们将数据中每个样例的概率作为阈值,然后得到相应的查全率和查准率,这样我们可以许多数据,根据这些数据绘制图线,我们给出代码:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties


def plot(dict,lists):#画出函数图像
    fig = plt.figure()
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    plt.xlabel('查全率(R)',fontproperties=font)
    plt.ylabel('查准率(P)',fontproperties=font)
    x = np.arange(0,1.0,0.2)
    y = np.arange(0,1.0,0.2)
    plt.xticks(x)
    plt.yticks(y)
    plt.plot(dict,lists)
    plt.show()


def caculate():

    num_real = 0
    #初始化样本标签,假设1为正例,0为负例
    trainlabel = np.random.randint(0,2,size=100)

    #产生100个概率值(置信度),即单个样本值为正例的概率
    traindata = np.random.rand(100)

    #将样本数据为正例概率从大到小排序返回索引值
    sortedTraindata = traindata.argsort()[::-1]

    k = []
    v = []
    #统计样本中实际正例的数量
    num = np.sum(trainlabel==1)
    for i in range(100):
        num_guess = i+1#假设为真的数量
        for j in range(0,i+1):
            a = sortedTraindata[j]
            if trainlabel[a] == 1:
                num_real += 1#假设为真中实际也为真的数量
        p = float(num_real/(num_guess))
        r = float(num_real/(num))
        v.append(p)
        k.append(r)
        num_real = 0
    plot(k,v)

if __name__=='__main__':
    caculate()

运行之后的曲线如图:
在这里插入图片描述
当然每次的运行结果也都不尽相同,因为数据都是随机产生的,但是这些图像和书中的还是有些出入的,所以我为了验证,搜索了些其他人的例子,但是大都是用sklearn库来绘制,所以也就有更方便的画法:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
from sklearn.metrics import precision_recall_curve

def plot(precision,recall):#画出函数图像
    fig = plt.figure()
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    plt.xlabel('查全率(R)',fontproperties=font)
    plt.ylabel('查准率(P)',fontproperties=font)
    x = np.arange(0,1.0,0.2)
    y = np.arange(0,1.0,0.2)
    plt.xticks(x)
    plt.yticks(y)
    plt.plot(recall,precision)
    plt.show()

def caculate():

    #初始化样本标签,假设1为正例,0为负例
    trainlabel = np.random.randint(0,2,size=100)

    #产生100个概率值(置信度),即单个样本值为正例的概率
    traindata = np.random.rand(100)

    precision,recall,thresholds = precision_recall_curve(trainlabel, traindata)
    #计算不同的阈值的查全率和查准率,此实现仅限于二进制分类任务,第一个参数是二进制标签,第二个参数
    #是估计的概率,第三个参数是正类的标签,默认值是1,返回值是p,r,
    plot(precision,recall)

if __name__=='__main__':
    caculate()

这里使用的是sklearn库中的函数,用来求查全率和查准率的,相应的介绍和参数使用可去官方文档中查看,给出地址文档地址,运行之后的结果:
在这里插入图片描述

ROC曲线的绘制

  ROC曲线与P-R曲线很类似,我们根据学习器的预测结果对样例进行排序,按此顺序逐个把样本作为正例进行预测,每次计算其横纵坐标的值,就可以得到ROC曲线,但是与P-R曲线的不同是,ROC曲线横轴使用的是“假正例率”,纵轴使用的是“真正例率”,我们同样可以写出它们的计算表达式。

  真正例率其实和查全率R一样,即
T P R = T P T P + F N TPR=\frac{TP}{TP+FN} TPR=TP+FNTP
而假正例率是
F P R = F P T N + F P FPR=\frac{FP}{TN+FP} FPR=TN+FPFP
对于上面举的例子,TPR就是R,FPR就是
F P R = 10 10 + 20 = 0.33 FPR=\frac{10}{10+20}=0.33 FPR=10+2010=0.33
分母其实就是实际的反例数目,所以如此我们也就可以绘制ROC曲线:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties

def plot(tpr,fpr):#画出函数图像
    fig = plt.figure()
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    plt.xlabel('假正例率(FPR)',fontproperties=font)
    plt.ylabel('真正例率(TPR)',fontproperties=font)
    x = np.arange(0,1.1,0.2)
    y = np.arange(0,1.1,0.2)
    plt.xticks(x)
    plt.yticks(y)
    plt.plot(fpr,tpr)
    x1 = np.arange(0, 1.0, 0.1)
    plt.plot(x1, x1, color='blue', linewidth=2, linestyle='--')
    plt.show()


def caculate():

    tp = 0
    #初始化样本标签,假设1为正例,0为负例
    trainlabel = np.random.randint(0,2,size=100)

    #产生100个概率值(置信度),即单个样本值为正例的概率
    traindata = np.random.rand(100)

    #将样本数据为正例概率从大到小排序返回索引值
    sortedTraindata = traindata.argsort()[::-1]

    k = []
    v = []
    #统计样本中实际正例的数量
    num = np.sum(trainlabel==1)
    num1 = 100 - num
    for i in range(100):
        num_guess = i+1#假设为真的数量
        for j in range(0,i+1):
            a = sortedTraindata[j]
            if trainlabel[a] == 1:
                tp += 1#假设为真中实际也为真的数量
        fp = num_guess - tp
        fpr = float(fp/(num1))
        tpr = float(tp/(num))
        v.append(fpr)
        k.append(tpr)
        tp = 0
    plot(k,v)

if __name__=='__main__':
    caculate()

在这里插入图片描述

同样对于ROC的真正例率和假正例率sklearn库中也有函数可以实现,roc_curve,给出官方文档地址文档地址,给出实现代码:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
from sklearn.metrics import roc_curve

def plot(fpr,tpr):#画出函数图像
    fig = plt.figure()
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    plt.xlabel('假正例率(FPR)',fontproperties=font)
    plt.ylabel('真正例率(TPR)',fontproperties=font)
    x = np.arange(0,1.1,0.2)
    y = np.arange(0,1.1,0.2)
    plt.xticks(x)
    plt.yticks(y)
    plt.plot(fpr,tpr)
    x1 = np.arange(0,1.0,0.1)
    plt.plot(x1,x1,color='blue',linewidth=2,linestyle='--')
    plt.show()

def caculate():

    #初始化样本标签,假设1为正例,0为负例
    trainlabel = np.random.randint(0,2,size=100)

    #产生100个概率值(置信度),即单个样本值为正例的概率
    traindata = np.random.rand(100)

    fpr,tpr,thresholds = roc_curve(trainlabel,traindata)

    plot(fpr,tpr)

if __name__=='__main__':
    caculate()

运行结果:
在这里插入图片描述


  本篇博客参考的是周志华的《机器学习》一书,希望博客能给大家帮助,同时博客中出现的问题和错误也希望大家指出,共同进步!

猜你喜欢

转载自blog.csdn.net/gwk1234567/article/details/108668120
今日推荐