评价指标:准确率的陷阱

做蛋白配体预测的同行,十有八九第一反应都是用准确率(Accuracy)看模型好不好。但我得说,这可能是最直观也最容易骗人的指标。你要是手头数据正负样本比例差个几十倍,准确率基本就变成了“瞎猜也能高”的摆设。

为什么准确率会骗人?

假设你的数据集里只有 5% 的活性分子,剩下 95% 都是非活性。你训练一个模型,啥都不学,直接预测所有样本为非活性——准确率就达到 95%。看着挺吓人吧?可实际上这个模型一个活性分子都找不到。

我个人习惯把这种场景叫做“高准确率低召回陷阱”。在蛋白配体对接、虚拟筛选这类任务里,活性分子往往就是那 5% 甚至更少,你真正关心的是能不能把少数类捡出来。所以单看准确率,等于闭着眼睛报喜不报忧。

那用什么指标?

以下几个指标是我在实战中经常组合使用的,每个都有它的脾气。

指标全称核心思想对不平衡敏感?我常用的场景
AUROCROC曲线下面积真正类率 vs 假正类率中等偏不敏感初步筛选,快速看排序能力
AUPRC精确率-召回率曲线下面积精确率 vs 召回率非常敏感正样本极少时的必看指标
F1分数F1 Score精确率和召回率的调和平均敏感需要平衡查准和查全时
G-mean几何平均sqrt(灵敏度 × 特异度)较敏感希望两类性能差距小时
MCC马修斯相关系数综合多类预测质量最稳定数据集极度不均衡时的黄金标准

注意看最后一列——AUROC 明明对不平衡没那么敏感,为什么我还用它?因为它不受阈值影响,能整体评估模型的排序能力。但千万记住:正样本比例低于 10% 时,AUROC 常常虚高,这时候 AUPRC 才是真实写照。

我的血泪教训: 曾经在激酶抑制剂预测项目中,用 AUROC = 0.92 吹了半天,结果换成 AUPRC 掉到 0.38。后来一查,原来那 92% 是靠大量负样本堆出来的。从此我养成了同时看 AUROC 和 AUPRC 的习惯。

F1分数:简单但别滥用

F1 是精确率和召回率的调和平均,你想想看,如果我把阈值调高,精确率高但召回率低;调低则反过来。F1 在两者之间取了个平衡。但注意:F1 默认认为精确率和召回率同等重要。在药物筛选里,漏掉一个活性分子可能比误报十个非活性分子更严重,所以有时需要调整 Fβ(β 控制权重)。我一般先用 β=2 让召回率权重更高。

G-mean:让你的模型不偏科

G-mean = sqrt(灵敏度 × 特异度)。我特别喜欢它的一点是:如果模型某类表现极差,G-mean 会迅速下降。比如灵敏度 0.99,特异度 0.1,G-mean 只有 0.31,直接暴露模型忽略了负类。这在蛋白配体对接中其实很实用——既要筛出活性分子,也需要负类别太离谱。

MCC:不均衡数据的老大哥

MCC 范围 [-1, 1],0 表示随机猜测,1 表示完美。它是基于混淆矩阵四个值的相关性计算,对不平衡的鲁棒性最强。如果是发论文,审稿人现在普遍要求 MCC。我在做 GPCR 配体预测时,数据不平衡比高达 1:50,最后靠 MCC 和 AUPRC 一起说服了审稿人。

注意: MCC 的计算要求所有类别都参与。如果某个类完全没有预测样本(比如实际活性但模型一个都没判出来),分母可能会出问题。需要先确认你的预测覆盖了所有类别。

类别不平衡下的指标选择流程 评价指标选择流程(类别不平衡维度) 评估类别不平衡程度 不平衡程度? 轻度(<5%) 可用准确率+混淆矩阵 中度(5%~20%) AUROC + AUPRC 严重(>20% 或极少数) AUPRC, F1, G-mean, MCC AUPRC 正样本极稀少时首选 F1分数 平衡查准查全 G-mean 防止偏科 MCC 极度不均衡的黄金标准 ⚠ 常见陷阱:别只看准确率 准确率在严重不平衡时无效 → 务必组合使用上述指标 示例:正样本2%时,模型全猜负类准确率98%,但 AUPRC可能仅0.2

我的小建议: 在蛋白配体项目里,最好同时报告 AUROC + AUPRC + MCC 三个数。前者看排序能力,中间看召回潜力,后者给总体相关系数。这样审稿人挑不出毛病,你自己也心里有底。

实战中怎么算?

用 Python scikit-learn 几行就搞定。给你一个我项目里经常用的计算片段:

from sklearn.metrics import roc_auc_score, average_precision_score, f1_score, matthews_corrcoef, confusion_matrix
import numpy as np

y_true = np.array([0]*950 + [1]*50)  # 模拟5%正样本
y_pred = np.random.rand(1000)        # 模拟预测概率

# AUROC
auroc = roc_auc_score(y_true, y_pred)
# AUPRC
auprc = average_precision_score(y_true, y_pred)
# F1(需要阈值0.5)
y_pred_bin = (y_pred > 0.5).astype(int)
f1 = f1_score(y_true, y_pred_bin)
# MCC
mcc = matthews_corrcoef(y_true, y_pred_bin)
# G-mean
cm = confusion_matrix(y_true, y_pred_bin)
tn, fp, fn, tp = cm.ravel()
sensitivity = tp / (tp + fn) if (tp+fn) else 0
specificity = tn / (tn + fp) if (tn+fp) else 0
gmean = np.sqrt(sensitivity * specificity)

print(f"AUROC: {auroc:.3f}, AUPRC: {auprc:.3f}, F1: {f1:.3f}, MCC: {mcc:.3f}, G-mean: {gmean:.3f}")

我通常会在训练过程中保存这几个指标的变化曲线,比单看 loss 更实用。

避坑指南

  • 别只用 AUROC 做结论:正样本比例 < 10% 时,AUROC 容易虚高。我曾经一篇论文被拒, reviewer 要求我补 AUPRC,果然掉了一半。
  • F1 别迷信默认阈值:很多库的 F1 用 0.5 阈值,但蛋白配体场景可能需要调阈值使召回率更高。我通常会在验证集上扫一遍阈值,选 F1 最高或 Fβ 最高的那个。
  • MCC 计算时注意零分母:如果某一类压根没预测到,MCC 会报错或返回 NaN。我习惯在代码里加个异常捕获。
  • G-mean 对极端值敏感:灵敏度或特异度有一个为0,G-mean 直接变0。这其实是好事,能立刻暴露模型缺陷。

说白了,这些指标没有万能的。你要根据自己数据的不平衡程度、业务侧重点(漏掉活性分子 vs 容忍误报)来组合选择。至少现在你知道该警惕什么了——下次看到项目报告里只写准确率,可以心里打一个问号。