分类任务评估2——推导ROC曲线、P-R曲线和K-S曲线

本文通过实例深入解析ROC曲线、查全率-查准率曲线的绘制过程,展示了不同阈值下查全率、假报警率、查准率的变化,并探讨了它们的含义。结论指出,ROC曲线和查全率-查准率曲线反映了模型在分类任务中的性能,阈值变化影响着查全率和假报警率的平衡,而K-S曲线则揭示了模型对类别区分的最大能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.背景介绍

1.1数据来源

本文数据基于XGBoost模型调参、训练、评估、保存和预测的结果数据。真实结果及预测结果、预测为1的概率如下:

y_test = [1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0]
y_pred = [0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0]
y_pred_proba = [0.2704021632671356, 0.0351046547293663, 0.8921366333961487, 0.046649616211652756, 0.17434531450271606,
                0.21389029920101166, 0.6692600846290588, 0.8160045742988586, 0.8550902605056763, 0.9786821007728577,
                0.9571514129638672, 0.10502149909734726, 0.5201050043106079, 0.032392870634794235]

1.2评估指标的粗浅解释

正式开始前需要提前掌握的内容,如已掌握可跳过直接看推导:
分类任务评估1——推导sklearn分类任务评估指标:混淆矩阵、查准率、查全率、假报警率计算;通过sklearn复现ROC曲线、AUC值,即《ROC曲线和AUC值的粗浅解释》章节

1.3本文推导的前提

只借助3个最基础的工具包,不使用任何sklearn的模块

import pandas as pd
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings('ignore')
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']

二.结论先行

完整脚本在第三部分,这里先来写结论

2.1结果展示

2.1.1ROC曲线、P-R曲线和K-S曲线绘制

在这里插入图片描述

2.1.2不同阈值下基础指标

不同阈值下 查全率 tpr: [0.0, 0.1111111111111111, 0.2222222222222222, 0.3333333333333333, 0.4444444444444444, 0.5555555555555556, 0.6666666666666666, 0.7777777777777778, 0.8888888888888888, 0.8888888888888888, 0.8888888888888888, 1.0, 1.0, 1.0, 1.0, 1.0]
不同阈值下 假报警率 fpr: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.4, 0.4, 0.6, 0.8, 1.0, 1.0]
不同阈值下 查准率 precisions: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.8888888888888888, 0.8, 0.8181818181818182, 0.75, 0.6923076923076923, 0.6428571428571429, 0.6428571428571429]
不同阈值下 真阳性 tp: [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 9, 9, 9, 9]
不同阈值下 假阳性 fp: [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 4, 5, 5]
不同阈值下 假阴性 fn: [9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0]

2.2结论总结

2.2.1一切源于阈值的变化

1.不同阈值下,根据预测概率会得出一系列混淆矩阵,因此会有一系列的查全率、查准率、假报警率

# 阈值根据自己的预测概率结果,自行调整。数据越多,划分越细,就越接近于曲线
# 因为数值较少,按照下面阈值的设置,每次都会多1个被预测为真,即TP+FP每次增加1个
thresholds_self = [1, 0.97, 0.9, 0.88, 0.85, 0.8, 0.6, 0.5, 0.25, 0.2, 0.15, 0.1, 0.04, 0.035, 0.03, 0]

# 预测结果如 2.2.2不同阈值下基础指标 不再赘述

2.不同阈值下的查全率和假报警率构成ROC曲线
3.不同阈值下的查准率和查全率构成查准率-查全率曲线
4.参与预测的样本越多,阈值可划分的就更详细,得到的一系列指标也就更多,线的绘制就越接近于曲线。

2.2.2ROC曲线的结论

1.任何分类任务的目标都是追求高查全率(TPR)和低假报警率(FPR);

# 查全率/召回率
Recall = TP / (TP + FN)
print('Recall: %.2f%%' % (Recall * 100))

# 假报警率
FPR = FP / (FP + TN)
print('FPR: %.2f%%' % (FPR * 100))

2.在既定的测试结果概率下,阈值由大到小排列,随着阈值的缩小,TPR增大时,FPR不变或提升
3.曲线下面积AUC越大越好,最小值为蓝色虚线下面积0.5,值的变化范围在蓝色虚线和红色实线围成的面积0-0.5之间。

2.2.3查全率-查准率曲线的结论

1.查准率不包含任何假阴性样本(FN)的信息,查全率不包含任何假阳性样本(FP)的信息,两者均不能完整评估学习性能,且相互补充。

# 精准率
Precision = TP / (TP + FP)
print('Precision: %.2f%%' % (Precision * 100))

# 查全率/召回率
Recall = TP / (TP + FN)
print('Recall: %.2f%%' % (Recall * 100))

2.任何分类任务的目标都是追求高查全率和高查准率,但由查准率-查全率曲线查准率precisions和查全率 tpr数值可以发现,高查全率和高查准率这两个目标是冲突的。原因在于在既定的测试结果概率下,随着阈值的缩小,越来越多的样本被判断为真,随着TP增加,FP也增加(FP也可能不变,但不影响整体趋势)。

不同阈值下 真阳性 tp: [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 9, 9, 9, 9]
不同阈值下 假阳性 fp: [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 4, 5, 5]
不同阈值下 假阴性 fn: [9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0]

3.作为查全率和查准率的折中,F值定义为查全率和查准率的调和平均数。F1便是其中一个调和平均数,是查准率和查全率同等重要的情况。
特殊情况说明:本例中出现,查全率上升、查准率也上升的情况(precision=0.8,recall=tpr=0.8888)——>(precision=0.81818,recall=tpr=1.0),查数可知:threshold_self=0.15到threshold_self=0.1时,TP增加,FP不变,所以precision有提升,但随着阈值缩小并没有影响随着查全率上升、查准率下降的整体趋势。
在这里插入图片描述
阈值threshold_self=0.2时,precision=TP/(TP+FP)=8/(8+1)=0.8888,recall=TP/(TP+FN)=8/(8+1)=0.8888
在这里插入图片描述
阈值threshold_self=0.15时,precision=TP/(TP+FP)=8/(8+2)=0.8,recall=TP/(TP+FN)=8/(8+1)=0.8888
在这里插入图片描述
阈值threshold_self=0.1时,precision=TP/(TP+FP)=9/(9+2)=0.8181,recall=TP/(TP+FN)=9/(9+0)=1
在这里插入图片描述

2.2.4K-S曲线的结论

1.K-S曲线绘制的丑是丑了点,但不影响结论,当参与的样本足够多时,自然会是3条平滑曲线,如下图(百度图片)。
在这里插入图片描述
2.K-S曲线的横轴阈值是预测为正的概率阈值,由高到低降序排列,从1到0不能反,反了就彻底错了。
3.由2.1.1中绘制的K-S曲线图可以看出,阈值为0.25时(thresholds_self[ks.index(max(ks))]),KS曲线获得最大值0.888(max(ks))。在预测为正的概率阈值=0.25时,模型将好坏样本最分得开,区分如下图,阈值0.5时,2个样本区分错误,阈值0.25时仅1个样本区分错误。所以可以得出结论,KS值反应的是模型对类别区分的最大能力
在这里插入图片描述
4.KS曲线与ROC曲线使用的都是tpr和fpr绘制,KS值是KS曲线的最大值max(abs(fpr - tpr)),AUC值是ROC曲线下面积。两个曲线对应的值都可以用来评估模型的好坏,具体使用没有绝对区分,可以和准确率一起综合来判断模型好坏

三.计算和曲线绘制

3.1计算推导脚本

import pandas as pd
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings('ignore')
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']

# 数据
y_test = [1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0]
y_pred = [0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0]
y_pred_proba = [0.2704021632671356, 0.0351046547293663, 0.8921366333961487, 0.046649616211652756, 0.17434531450271606,
                0.21389029920101166, 0.6692600846290588, 0.8160045742988586, 0.8550902605056763, 0.9786821007728577,
                0.9571514129638672, 0.10502149909734726, 0.5201050043106079, 0.032392870634794235]

# 空列表准备
fpr = []
tpr = []
precisions = []
tp = []
fp = []
fn = []

# 阈值准备
thresholds_self = [1, 0.97, 0.9, 0.88, 0.85, 0.8, 0.6, 0.5, 0.25, 0.2, 0.15, 0.1, 0.04, 0.035, 0.03, 0]
# 不同阈值的FPR\TPR\查准率生成
for threshold_self in thresholds_self:
    # 当前阈值下的预测结果
    pred = [1 if i >= threshold_self else 0 for i in y_pred_proba]
    print('pred_list:', pred)
    # print('threshold_self:', threshold_self, '\t', 'pred_1_cnt:', sum(pred))

    # 生成混淆矩阵
    feature_data = [pred, y_test]
    df = pd.DataFrame(feature_data).T.reset_index().rename(columns={0: 'pred', 1: 'true'})
    ptl = pd.pivot_table(df, values='index', index='pred', columns='true', aggfunc='count', fill_value=0)
    # 全部预测为真,或全部预测为假时,混淆矩阵的补充
    if ptl.shape[0] == 1:
        if ptl.index == 0:
            ptl.loc[1] = [0, 0]
        elif ptl.index == 1:
            ptl.loc[0] = [0, 0]
            ptl = ptl.sort_index()
    print('混淆矩阵:', '\n', ptl)

    # 从混淆矩阵提取基础值
    TP = ptl.iat[1, 1]
    FN = ptl.iat[0, 1]
    FP = ptl.iat[1, 0]
    TN = ptl.iat[0, 0]

    # 单一指标计算
    FPR = FP / (FP + TN)
    TPR = TP / (TP + FN)
    precision = TP / (TP + FP)

    # 不同阈值下的计算数据收集
    fpr.append(FPR)
    tpr.append(TPR)
    tp.append(TP)
    fp.append(FP)
    fn.append(FN)
    precisions.append(precision)

# 查准率空值特殊处理,替换为1;也可以删除,绘制查全率-查准率曲线时,同时删除相应位置查全率值即可
precisions = [i if i > -0.1 else 1.0 for i in precisions]
ks = [m - n for m, n in zip(tpr, fpr)]

print('不同阈值下 查全率 tpr:', tpr)
print('不同阈值下 假报警率 fpr:', fpr)
print('不同阈值下 查准率 precisions:', precisions)
print('不同阈值下 真阳性 tp:', tp)
print('不同阈值下 假阳性 fp:', fp)
print('不同阈值下 假阴性 fn:', fn)

3.2绘制三个曲线

# 直接接上面的代码即可直接运行
# ROC曲线
plt.figure(figsize=(24, 6))
plt.subplot(131)
plt.plot(fpr, tpr, marker='p', color='r')
plt.plot([0, 1], [0, 1], '--')
plt.xlim([-0.002, 1])
plt.ylim([-0.002, 1.005])
plt.xlabel('假报警率')
plt.ylabel('查全率(真正利率)')
plt.title('查全率与假报警率——ROC曲线')

# 查全率-查准率曲线
plt.subplot(132)
plt.plot(tpr, precisions, marker='o', color='g')
plt.xlim([-0.002, 1])
plt.ylim([-0.002, 1.005])
plt.xlabel('查全率')
plt.ylabel('查准率')
plt.title('查准率与查全率曲线')

# KS曲线
plt.subplot(133)
plt.plot(thresholds_self, tpr, marker='^', color='b', label='tpr')
plt.plot(thresholds_self, fpr, marker='*', color='y', label='fpr')
plt.plot(thresholds_self, [m - n for m, n in zip(tpr, fpr)], marker='', color='r', label='ks')
plt.axvline(x=thresholds_self[ks.index(max(ks))], ymax=max(ks), color='k', linestyle='--')
plt.axhline(y=max(ks), xmax=1-thresholds_self[ks.index(max(ks))], color='k', linestyle='--')
plt.xlim([1, -0.002])
plt.ylim([-0.002, 1.005])
plt.xlabel('阈值')
plt.ylabel('真正例率/假正例率')
plt.title('KR曲线')
plt.legend(loc="upper left")
plt.show()

声明:本文所载信息不保证准确性和完整性。文中所述内容和意见仅供参考,不构成实际商业建议,可收藏可转发但请勿转载,如有雷同纯属巧合。
### 如何使用 Python Pandas 读取 CSV 文件并绘制 ROC P-R 曲线 #### 数据准备 为了绘制 ROC Precision-Recall (P-R) 曲线,首先需要加载数据。可以利用 `pandas` 的 `read_csv()` 方法来完成这一操作。 ```python import pandas as pd # 加载数据 data = pd.read_csv('your_data_file.csv', encoding='utf-8') data = data.fillna(-1) # 处理缺失值 print(data.head()) # 查看前几行数据 ``` 上述代码片段展示了如何从本地路径加载 CSV 文件,并填充其中的缺失值[^2]。 --- #### 划分训练集测试集 在构建机器学习模型之前,通常会将数据划分为训练集测试集: ```python from sklearn.model_selection import train_test_split X = data.iloc[:, :-1] # 特征列 y = data.iloc[:, -1] # 标签列 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) ``` 这里我们按照 70% 训练集 30% 测试集的比例分割数据。 --- #### 构建分类模型 假设已经选择了某种分类算法(如逻辑回归),可以通过以下方式拟合模型并获取预测概率值: ```python from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_curve, precision_recall_curve, auc model = LogisticRegression() model.fit(X_train, y_train) # 获取预测的概率值 y_prob = model.predict_proba(X_test)[:, 1] ``` 此处,`predict_proba()` 函数返回的是正类别的概率估计值[^3]。 --- #### 绘制 ROC 曲线 ROC 曲线用于评估分类模型的表现,其核心指标是假阳率 (False Positive Rate, FPR) 真阳率 (True Positive Rate, TPR)。 ```python fpr, tpr, thresholds = roc_curve(y_test, y_prob) roc_auc = auc(fpr, tpr) plt.figure(figsize=(8, 6)) plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})') plt.plot([0, 1], [0, 1], color='navy', lw=2, 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 Curve') plt.legend(loc="lower right") plt.show() ``` 这段代码实现了 ROC 曲线的绘制,并标注了 AUC 值作为性能衡量标准。 --- #### 绘制 Precision-Recall 曲线 Precision-Recall 曲线适用于类别分布不均衡的情况,它反映了精确率 (Precision) 随召回率 (Recall) 变化的趋势。 ```python precision, recall, _ = precision_recall_curve(y_test, y_prob) pr_auc = auc(recall, precision) plt.figure(figsize=(8, 6)) plt.plot(recall, precision, color='blue', lw=2, label=f'PR curve (area = {pr_auc:.2f})') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('Recall') plt.ylabel('Precision') plt.title('Precision-Recall Curve') plt.legend(loc="upper right") plt.show() ``` 此部分代码完成了 Precision-Recall 曲线的可视化,并同样计算了曲线下面积 (AUC)[^3]。 --- ### 总结 通过以上步骤,能够成功地使用 Python 中的 Pandas 库加载 CSV 文件,并借助 Matplotlib 或 Seaborn 完成 ROC Precision-Recall 曲线的绘制。这些图表对于评估分类模型的效果至关重要[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值