课程
机器学习(ML)算法日益用于自动化繁琐任务并从数据中识别隐藏模式。但其本质是概率性的,意味着预测并非总是正确。因此,您需要一种方法来评估 ML 模型的有效性,以便对这类系统建立信任。
常见的评估指标包括准确率(accuracy)、精确率(precision)、召回率(recall)、均方误差(MSE)、平均绝对百分比误差(MAPE)等,用于衡量模型表现。不同指标从不同的标准与视角衡量性能。
这些指标还能确保模型在既定任务上的学习不断提升。毕竟,如果无法度量,就无法改进 ML 系统的性能。
准确率是一个易于理解且适用于平衡数据集(即各类别样本量相当)的指标。然而,现实世界的现象并非均匀分布,因此很难获得平衡的数据集。准确率主要关注大多数样本是否被正确识别,而不考虑其所属类别。对于欺诈交易或广告点击这类重要的稀有事件,若模型将所有样本都预测为负类(即无欺诈或无点击),准确率会产生误导。鉴于这些局限,准确率并非最合适的指标。那么,我们应当用哪种指标来衡量模型表现呢?
精确率与召回率是评估不平衡分类模型(如预测客户流失)时广泛使用的指标。
精确率与召回率解析
精确率指的是:当模型预测为正类时,这一预测为真的置信度;召回率衡量的是:模型能从数据集中找出多少真实的正类实例。注意,正类是我们关注的目标类。
从经验上讲,借助包含四个关键术语的混淆矩阵,最容易理解精确率和召回率:
- 真正类(TP):被正确识别为正类的实例数量
- 假正类(FP):被错误识别为正类的负类实例数量
- 真负类(TN):被正确识别为负类的实例数量
- 假负类(FN):被错误识别为负类的正类实例数量

示意图由作者绘制
精确率与召回率可用上述四个术语进行数学表达:
精确率是 TP 占所有被预测为正类(TP+FP)的比例。召回率是 TP 占所有真实正类(TP+FN)的比例。
进一步用下图中的记号加以区分。两者的分子相同,都是实际正类与预测正类的交集,即 TP。

图片作者:Shruti Saxena
区别在于分母:精确率用所有被预测为正类的实例(TP+FP),而召回率用所有真实正类实例(TP+FN)。
精确率与召回率的直觉
混淆矩阵之所以得名,是因为初学这些概念时确实容易“困惑”。
让我们通过一个例子来内化这个概念。假设您拥有一家钢厂,工厂从铁矿石中提取铁,并与其他矿物和元素混合(有时是无意的)。聚焦在提取阶段,您对提取出的金属纯度和过程产生的浪费有如下几种取舍:
情景 1:只优先考虑提取铁的纯度,而不管过程中浪费了多少金属。
情景 2:要最大化效率,即每单位矿石提取的铁含量,而不考虑提取金属的纯度。
情景 3:两者兼顾,即尽可能保持提取金属的高纯度,同时减少浪费(最大化每单位矿石提取的铁)。
将高炉或电弧炉视为不同的建模方案,会产生不同结果(铁纯度与浪费)。假设某种提取方法产出 97.5% 纯铁,并在泥渣中损失 4% 的铁。于是我们可将精确率定义为提取金属中纯铁的比例,即 97.5%;而召回率则是从矿石中所有可用铁中被提取出来的比例,为 96%(4% 的铁被浪费)。

示意图由作者绘制
设想您选择第二种方法,因为您希望从炉中获得更纯的铁,但这意味着在提取过程中会有更多浪费。假设将铁的纯度提高 0.5% 至 98%,金属浪费增加 11%。这意味着当精确率为 98% 时,对应的召回率为 85%。这种以更高精确率换取更低召回率(反之亦然)的“易货”关系体现了精确率与召回率的反向关系。
如果能知道所有精确率和其对应召回率的取值,岂不是更容易做出最符合目标的决策?
精确率-召回率曲线正是帮助做出这一选择的工具。接下来,我们将深入了解。但在此之前,先理解 PR 曲线的核心概念——阈值。
阈值的概念
以欺诈交易识别为例,来理解阈值(或截止值)的作用。下表包含 4 笔交易,交易 ID 为 1 到 4,每笔交易要么为欺诈,要么为正常。欺诈检测模型输出交易为欺诈的概率(黄色列高亮),同时也隐含着为正常交易的概率。
当欺诈交易概率列中的概率大于所选阈值时,该笔交易被预测为欺诈,否则判为正常。
|
Transaction ID |
Probability of Fraud transaction |
Probability of regular transaction |
Is fraudulent? (Threshold = 0.9) |
Is fraudulent? (Threshold = 0.5) |
Is fraudulent? (Threshold = 0.4) |
|
1 |
0.3 |
0.7 |
No |
No |
No |
|
2 |
0.4 |
0.6 |
No |
No |
Yes |
|
3 |
0.6 |
0.4 |
No |
Yes |
Yes |
|
4 |
0.95 |
0.05 |
Yes |
Yes |
Yes |
当应用一个严格到 0.9 的阈值时,前三笔交易被标记为正常,最后一笔被标记为欺诈。如此高的阈值让预测更“自信”,对应高精确率的情形。作为代价,您会错过一些欺诈交易,牺牲了召回率。
尽管精确率很高,这种情况并不理想。您能想到原因吗?
因为业务会因此承担更高的漏判成本,而识别欺诈正是建立此类模型的根本目的。请注意,单笔欺诈交易的代价远高于将正常交易误拦(即 FP)所带来的成本。
现在看另一端:设定 0.4 的低阈值,会将后三笔交易标记为欺诈。如此“宽松”的阈值会拦截大多数交易,令许多客户反感;且人工核查被标记交易以识别真正欺诈的负担也会显著增加。
因此,企业需要在权衡以下成本的前提下,设定目标指标及其期望值,以求兼顾两端:
- 将欺诈交易识别为正常的成本(假负,FN)
- 将正常交易识别为欺诈的成本(假正,FP)
回看精确率的定义可知,假正位于分母中,这意味着最小化假正将最大化精确率。同样地,最小化假负将最大化召回率。
因此,一笔交易被预测为欺诈还是正常,很大程度上取决于阈值。
什么是精确率-召回率曲线?
精确率-召回率曲线可帮助您基于期望的精确率与召回率来决定阈值。它也常用于比较不同模型的表现,通过计算“精确率-召回率曲线下面积”(AUC)。
如前所述,对于不同阈值,二分类模型会得到不同的 TP、FP、TN 和 FN;每一个阈值都会对应一对精确率与召回率。
将召回率绘于 x 轴、对应的精确率绘于 y 轴,即得到 PR 曲线,通常呈负斜率。它表示给定模型在“提高精确率(减少 FP)”与“提高召回率(减少 FN)”之间的权衡。考虑到二者的反向关系,曲线通常是非线性的,即提升一个指标会降低另一个,但下降幅度未必成比例。

图片来源:StackExchange
假设某组织设定最低可接受召回率为 98%。在图中以星号标注的“低阈值、低精确率、高召回率”交点处,对应的精确率是 30%。
另外两个极端点代表阈值过低与过高的情况:
- 阈值过低 → 高召回率:人工成本上升、客户不满
- 阈值过高 → 高精确率:欺诈成本与声誉损失上升
如何使用 PR 曲线
除了据此确定与业务指标一致的合适阈值外,还可以通过比较多个模型或算法的精确率-召回率曲线下面积来评估其性能。与仅比较单一的精确率、召回率或 F1 分数(精确率与召回率的调和平均)相比,使用 PR 曲线尤为有用。

图片来源:Stackoverflow
这类指标依赖阈值,阈值变化会改变 TP、FP 与 FN 的分布。两个不同模型在同一阈值下的表现可能不同,这使 PR 曲线成为稳健的模型对比手段。上图展示了同一数据集上两种算法对应的 PR 曲线——算法二的 AUC 大于算法一,因此在该问题上更优。
在 Python 中实现精确率-召回率曲线
既然我们已经了解了精确率-召回率曲线及其用途,接下来看看如何在 Python 中绘制它。
步骤 1:导入必要的 Python 包
我们以乳腺癌检测的数据集为例,其中 “class 1” 表示确诊为癌症,“class 0” 表示未患癌症。
第一步从 “sklearn.datasets” 导入数据集,包含自变量与目标变量。由于这是一个大多数算法都容易学会的示例数据集,我们选择了从 sklearn 导入的朴素贝叶斯算法 “GaussianNB”。
接着导入 “Precision_Recall_curve”,用于输出不同阈值及其对应的精确率与召回率。
from sklearn.datasets import load_breast_cancer
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import precision_recall_curve
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
步骤 2:准备训练集与测试集
自变量存储在 “data” 字典的键 “data” 中,目标变量存储在键 “target” 中。随后通过传入 test_size=0.3 将数据拆分为训练集与测试集。
# Load model breast cancer data
data = load_breast_cancer()
X = data['data']
y = data['target']
我们先看看正负类(即乳腺癌与非乳腺癌案例)的分布。
import numpy as np
unique, counts = np.unique(y, return_counts=True)
plt.pie(counts, labels=unique, autopct='%.0f%%');

使用 sklearn 的 model_selection 模块中的 train_test_split 方法将数据拆分为训练集与测试集。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
步骤 3:模型训练
将训练数据传入 GaussianNB() 以启动模型训练。随后使用拟合后的模型对象在训练集与测试集上获取概率形式的预测。
# Fit a Naive Bayes model
clf = GaussianNB().fit(X_train, y_train)
步骤 4:生成预测结果
使用训练集与测试集生成预测概率,这些概率将用于在不同阈值下计算精确率与召回率。
# Predict probability
y_prob_train = clf.predict_proba(X_train)[:,1]
y_prob_test = clf.predict_proba(X_test)[:,1]
步骤 5:绘制 PR 曲线
Precision_Recall_curve() 方法接收两个输入——训练集的预测概率,即 y_prob_train,以及真实标签,返回三个值:精确率、召回率与阈值。
precision, recall, thresholds = precision_recall_curve(y_train, y_prob_train)
plt.fill_between(recall, precision)
plt.ylabel("Precision")
plt.xlabel("Recall")
plt.title("Train Precision-Recall curve");
利用精确率与召回率向量即可在不同阈值下绘制 PR 曲线,如下所示:

PR 曲线由作者生成
精确率-召回率曲线是否优于 ROC 曲线?
ROC 曲线与 PR 曲线类似,但在不同阈值下绘制真正率(TPR)与假正率(FPR)。我们已经知道 TPR,就是召回率。下面关注 FPR,它表示来自负类的误分类数量。延续欺诈示例,FPR 表示被标记为欺诈的正常交易所占的比例。

图片作者:Albert Um
至此我们知道,召回率(即 TPR)在 PR 与 ROC 两条曲线中是共通的;关键差异在于前者使用精确率,后者使用 FPR。下面看看为何 PR 曲线相对更具信息量。
这是因为与 ROC 曲线相比,P-R 曲线能就关注的目标类提供更有意义的洞见。以欺诈交易预测为例,在不平衡数据集中,真负类(TN)通常非常多,使得 FPR 往往很小。但这能说明模型在预测欺诈交易方面的表现吗?
并不能!相反,使用精确率作为指标之一,可以反映模型在正类(欺诈交易)上的表现,描述模型在标记欺诈交易时是保守还是宽松。
ROC 曲线的第二大缺点是对不平衡数据“免疫”。从上图可见,FPR 是仅针对负类的指标,意味着 FP 的变化预期会与 FP+TN(全部负类实例)的变化成比例。因此,如果底层数据的类别分布发生变化,ROC 变化并不明显。对类别分布不敏感,使得 PR 曲线较 ROC 曲线更胜一筹。
总结
精确率与召回率是衡量机器学习分类模型性能的关键评估指标。然而,两者之间的权衡取决于业务诉求,且最适合通过 PR 曲线来解决。本文解释了如何解读 PR 曲线并选择合适的阈值以满足业务目标,同时通过 Python 演示了绘制步骤。我们还讨论了为何 PR 曲线比 ROC 曲线更具信息性。