评价指标:准确率的陷阱
做蛋白配体预测的同行,十有八九第一反应都是用准确率(Accuracy)看模型好不好。但我得说,这可能是最直观也最容易骗人的指标。你要是手头数据正负样本比例差个几十倍,准确率基本就变成了“瞎猜也能高”的摆设。
为什么准确率会骗人?
假设你的数据集里只有 5% 的活性分子,剩下 95% 都是非活性。你训练一个模型,啥都不学,直接预测所有样本为非活性——准确率就达到 95%。看着挺吓人吧?可实际上这个模型一个活性分子都找不到。
我个人习惯把这种场景叫做“高准确率低召回陷阱”。在蛋白配体对接、虚拟筛选这类任务里,活性分子往往就是那 5% 甚至更少,你真正关心的是能不能把少数类捡出来。所以单看准确率,等于闭着眼睛报喜不报忧。
那用什么指标?
以下几个指标是我在实战中经常组合使用的,每个都有它的脾气。
| 指标 | 全称 | 核心思想 | 对不平衡敏感? | 我常用的场景 |
|---|---|---|---|---|
| AUROC | ROC曲线下面积 | 真正类率 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 的计算要求所有类别都参与。如果某个类完全没有预测样本(比如实际活性但模型一个都没判出来),分母可能会出问题。需要先确认你的预测覆盖了所有类别。
我的小建议: 在蛋白配体项目里,最好同时报告 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 容忍误报)来组合选择。至少现在你知道该警惕什么了——下次看到项目报告里只写准确率,可以心里打一个问号。