第4章 BERT模型评估指标:准确率、F1、召回率、困惑度
评估一个BERT模型好不好用,不能光看loss降了多少。说句实话,我在生产线部署模型之前,至少得跑五六个指标才算放心。今天的四个核心指标——准确率、F1、召回率、困惑度——基本覆盖了分类任务和语言建模两个方向。
4.1 准确率(Accuracy)——最直白但也最容易骗人
准确率就是预测正确的样本数除以总样本数。公式简单:Accuracy = (TP + TN) / (TP + TN + FP + FN)。你在单标签分类里最常用它。
我曾经摔过一跤:做一个文本情感分类,正样本只占2%,模型把所有样本都判成「负向」,准确率竟然98%。老板看了报表直夸好,我吓得赶紧补了F1。记住:类别极度不平衡时,准确率就是数字游戏。
什么时候能用?测试集各类别比例接近、误分类代价差不多的时候。否则,你得多看几个角度。
4.2 精确率、召回率与F1——分类任务的铁三角
这三个指标都依赖混淆矩阵。你想想看,实际项目中PM问“模型准不准”,其实是在问精确率和召回率能不能兼顾。
| 指标 | 公式 | 关心的点 |
|---|---|---|
| 精确率(Precision) | TP / (TP + FP) | 预测为正的里面,多少是真的正 |
| 召回率(Recall) | TP / (TP + FN) | 所有正样本里,我找出了多少 |
| F1 | 2 * (P * R) / (P + R) | 精确率和召回率的调和平均 |
我个人习惯把F1当作单数字评判标准。但注意:F1有micro、macro、weighted三种平均方式。微调BERT做多分类时,我通常用macro F1,因为它对每个类一视同仁。遇到过小类别召回率极低但整体F1还挺好看的情况——那是weighted平均把大类的权重拉高了。
避坑指南:报告指标时一定要写明平均方式。我在项目文档里吃过亏,macro F1和weighted F1差了将近10个点,评审会上被追着问了一个小时。
4.3 困惑度(Perplexity)——语言模型的专属温度计
困惑度(PPL)是BERT预训练阶段的重要指标。公式:
PPL = exp( - (1/N) * Σ log P(w_i | context) )
说白了,它衡量模型对下一个的「惊讶程度」。PPL越低,模型预测越自信。我刚开始做BERT fine-tuning时,觉得PPL降到20就差不多了,结果下游分类任务还是拉胯。后来才悟了:PPL和下游任务相关但不,有时候PPL很低,但生成的句子语法正确却语义跑偏。
经验分享:如果你做的是MLM(掩码语言模型)评估,记得用transformers库里的evaluate模块算PPL,不要自己手写log-sum-exp,很容易溢出。我曾经在float16下算PPL,结果inf一片。
4.4 指标之间的关系与选择策略
下面这张图总结了四个核心指标的计算路径和适用场景。你一看就明白:
4.5 实战代码:用sklearn一键计算
我建议你在推理脚本里直接调用sklearn.metrics,别自己手搓公式——容易下标搞错。下面这段代码我在多个项目复用:
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
import numpy as np
def calc_classification_metrics(y_true, y_pred, average='macro'):
acc = accuracy_score(y_true, y_pred)
precision, recall, f1, _ = precision_recall_fscore_support(
y_true, y_pred, average=average
)
return {
'accuracy': acc,
'precision': precision,
'recall': recall,
'f1': f1
}
# 示例:5分类输出
y_true = [0,1,2,3,4,0,1,2,3,4]
y_pred = [0,1,2,3,4,0,1,1,3,4]
metrics = calc_classification_metrics(y_true, y_pred, average='macro')
print(metrics)
# 输出:{'accuracy': 0.9, 'precision': 0.875, 'recall': 0.9, 'f1': 0.884...}
注意参数average:二分类用'binary',多分类看业务选'macro'或'weighted'。
4.6 困惑度的快速验证
用Hugging Face的evaluate库最省事:
import evaluate
perplexity = evaluate.load("perplexity", module_type="metric")
results = perplexity.compute(predictions=output_logits, model_id="bert-base-uncased")
print(f"PPL: {results['perplexity']:.2f}")
曾经踩过的坑:predictions需要是logits(未归一化分数),而不是softmax后的概率。我第一次传了概率,算出来的PPL完全不对。另外,输入文本长度不同时,evaluate会自动padding,但你要确认pad__id设置正确。
4.7 总结一下,指标怎么选
我个人的工程习惯:先看任务类型。分类任务必配F1和混淆矩阵,语言任务盯住PPL。但上线之前,一定用真实场景数据跑一遍人工抽检——指标只能告诉你模型「数学上好」,不能告诉你「业务上靠谱」。
好了,这一章就到这里。你回去做实验时,记得把每个指标都打印出来对比,别偷懒。