利用Python中的numpy包实现PR曲线和ROC曲线的计算

闲来无事,边理解PR曲线和ROC曲线,边写了一下计算两个指标的代码。在python环境下,sklearn里有现成的函数计算ROC曲线坐标点,这里为了深入理解这两个指标,写代码的时候只用到numpy包。事实证明,实践是检验真理的唯一标准,在手写代码的过程中,才能真正体会到这两个评判标准的一些小细节,代码记录如下。

一、模拟一个预测结果

因为两个曲线都是用来判断一个分类器分类性能的,所以这里直接用随机数生成一组类别和对应的置信度。类别有0、1两个类别。置信度从0到1随机生成。

data_len = 50
label = np.random.randint(0, 2, size=data_len)
score = np.random.choice(np.arange(0.1, 1, 0.01), data_len)

生成结果如下:其中第一行代表真实的类别,第二行代表分类器判断目标是类别1的置信度。

label1010011……
score0.220.310.920.340.370.180.51……

因为我们的置信度是随机生成的,所以得到的结果等同于一个二分类器“瞎猜”的结果。

二、PR曲线

不管是PR曲线还是ROC曲线,首先要选定一个类别,然后针对这个类别具体计算。

该曲线的横坐标是召回率(R),纵坐标是精确度(P),故命名为PR曲线。
举一个简单的例子来说明P和R的定义:假设一个二分类器需要预测100个样本,这些样本中有80个类别1,20个类别0。当把置信度取某一个值S时,假设此时分类器认为有60个样本是类别1,在预测的这60个人样本中,有50个样本预测正确,其余10个样本预测错误。那么,对于类别1的P、R值计算如下:
P = 50 60 = 0.833 P =\frac{50}{60} = 0.833 P=6050=0.833
R = 50 80 = 0.625 R = \frac{50}{80} = 0.625 R=8050=0.625
即有0.667的概率预测正确,对于80个类别1的样本,分类器好比可以召唤神兽的魔法师,养了80只神兽,只召唤回来50只。所以召回率就是62.5%,其他的就被无情丢弃了。

对于类别0来说,既然二分类器认为类别1的有60个,那么反过来其余40个都认为是类别0,通过上述可以推出这40个只有10个是类别0,其余的是类别1,所以对于类别0的P、R值计算如下:

P = 10 40 = 0.25 P =\frac{10}{40} = 0.25 P=4010=0.25
R = 10 20 = 0.5 R = \frac{10}{20} = 0.5 R=2010=0.5

根据以上说明代码实现如下:

def PR_curve(y,pred):
    pos = np.sum(y == 1)
    neg = np.sum(y == 0)
    pred_sort = np.sort(pred)[::-1]  # 从大到小排序
    index = np.argsort(pred)[::-1]  # 从大到小排序
    y_sort = y[index]
    print(y_sort)

    Pre = []
    Rec = []
    for i, item in enumerate(pred_sort):
        if i == 0:#因为计算precision的时候分母要用到i,当i为0时会出错,所以单独列出
            Pre.append(1)
            Rec.append(0)


        else:
            Pre.append(np.sum((y_sort[:i] == 1)) /i)
            Rec.append(np.sum((y_sort[:i] == 1)) / pos)
    print(Pre)
    print(Rec)
## 画图
    plt.plot(Rec, Pre, 'k')
    # plt.legend(loc='lower right')

    plt.title('Receiver Operating Characteristic')
    plt.plot([(0, 0), (1, 1)], 'r--')
    plt.xlim([-0.01, 1.01])
    plt.ylim([-0.01, 01.01])
    plt.ylabel('Precision')
    plt.xlabel('Recall')
    plt.show()

画出的PR曲线:
在这里插入图片描述
注:在西瓜书里,PR曲线是过(1,0),(0,1)两个点的曲线,在原书中作者也说明了这个曲线是经过平滑处理的,但是自己算算就知道过(1,0)这个点得不太可能的,因为这个时候recall是1,也就意味着分类器把所有的样本都判定为正样本。精确度自然就是:所有正样本个数/总样本个数。除非负样本个数占比足够大,才能使这个点的纵坐标无限趋近于0。

三、ROC曲线

ROC曲线的纵坐标是TPR,横坐标是FPR(中文翻译太乱了,我还是习惯用英文表示)。TPR等同于PR曲线的召回率,FPR是所有被预测成正例的反例和真实反例的个数之比。

还是以上那个例子,对于·类别1,两者的计算如下:
T P R = 10 20 = 0.5 TPR = \frac{10}{20} = 0.5 TPR=2010=0.5
F P R = 10 20 = 0.5 FPR = \frac{10}{20} = 0.5 FPR=2010=0.5

def ROC_curve(y,pred):
    pos = np.sum(y == 1)
    neg = np.sum(y == 0)
    pred_sort = np.sort(pred)[::-1]  #从大到小排序
    index = np.argsort(pred)[::-1]#从大到小排序
    y_sort = y[index]
    print(y_sort)
    tpr = []
    fpr = []
    thr = []
    for i,item in enumerate(pred_sort):
        tpr.append(np.sum((y_sort[:i] == 1)) / pos)
        fpr.append(np.sum((y_sort[:i] == 0)) / neg)
        thr.append(item)
    print(fpr)
    print(tpr)
    print(thr)
	
	#画图
    plt.plot(fpr, tpr, 'k')
	plt.title('Receiver Operating Characteristic')
	plt.plot([(0,0),(1,1)],'r--')
	plt.xlim([-0.01,1.01])
	plt.ylim([-0.01,01.01])
	plt.ylabel('True Positive Rate')
	plt.xlabel('False Positive Rate')
	plt.show()

结果如下:
在这里插入图片描述
可以看到,随机瞎猜的分类器得到的ROC曲线在y=x这条直线周围抖动。

如果我们把随机生成的置信度只保留小数点后一位,那么数据里有很多相同置信度的值。这种方式每次计算出来的ROC曲线会稍微有些差异,取决于排序的结果。

四、P-R曲线和ROC各自的优劣

实际的正负样本比例差距过大时,建议用ROC曲线。

附:

这几个值确实挺绕的,附一张公式表,便于搞混的时候查询:

1)分类结果混淆矩阵

在这里插入图片描述

2)P-R公式

P = T P T P + F P P= \frac{TP}{TP+FP} P=TP+FPTP
R = T P T P + F N R = \frac{TP}{TP+FN} R=TP+FNTP

3)TPR-FPR公式

T P R = T P T P + F N TPR= \frac{TP}{TP+FN} TPR=TP+FNTP
R = F P T N + F P R = \frac{FP}{TN+FP} R=TN+FPFP

ROC曲线PR曲线是两种不同的评估分类模型性能的方ROC曲线是以真阳性率(True Positive Rate)为纵轴,假阳性率(False Positive Rate)为横轴,绘制出来的曲线PR曲线是以精确率(Precision)为纵轴,召回率(Recall)为横轴,绘制出来的曲线。 对于已知logits的情况,可以通过将logits转换为概率值,然后将概率值与真实标签进行比较,得出分类结果。然后根据分类结果和真实标签,计算出TP、FP、FN、TN等指标,进而计算出TPR、FPRPrecision、Recall等指标。最后,可以使用这些指标绘制ROC曲线PR曲线。 在Python,可以使用sklearn库的roc_curve和precision_recall_curve函数来计算ROC曲线PR曲线。具体实现过程可以参考以下代码示例: ``` python import numpy as np from sklearn.metrics import roc_curve, precision_recall_curve # 假设已知logits为以下数组 logits = np.array([0.1, 0.4, 0.8, 0.2, 0.6, 0.3, 0.9, 0.7, 0.5, 0.2]) # 假设真实标签为以下数组,1表示正例,0表示负例 y_true = np.array([1, 1, 0, 0, 1, 0, 1, 0, 0, 1]) # 将logits转换为概率值 probs = 1 / (1 + np.exp(-logits)) # 计算FPR、TPR和阈值 fpr, tpr, thresholds = roc_curve(y_true, probs) # 计算Precision、Recall和阈值 precision, recall, thresholds = precision_recall_curve(y_true, probs) ``` 计算出FPR、TPRPrecision、Recall后,可以使用Matplotlib库将ROC曲线PR曲线绘制出来。具体实现过程可以参考以下代码示例: ``` python import matplotlib.pyplot as plt # 绘制ROC曲线 plt.plot(fpr, tpr, label='ROC Curve') plt.plot([0, 1], [0, 1], 'k--', label='Random') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver Operating Characteristic') plt.legend() plt.show() # 绘制PR曲线 plt.plot(recall, precision, label='PR Curve') plt.xlabel('Recall') plt.ylabel('Precision') plt.title('Precision-Recall Curve') plt.legend() plt.show() ``` 注意,以上代码仅为示例,实际应用需要根据具体情况进行适当修改。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值