第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)所有正样本里,我找出了多少
F12 * (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 指标之间的关系与选择策略

下面这张图总结了四个核心指标的计算路径和适用场景。你一看就明白:

BERT评估指标关系图 BERT模型输出 分类任务 混淆矩阵(TP/FP/FN/TN) 准确率 / 精确率 / 召回率 F1(Micro/Macro/Weighted) 语言建模任务 预测每个的概率分布 困惑度(Perplexity) 关键选择策略 分类任务:不平衡数据用macro F1,平衡数据用准确率 语言模型:PPL作为监控指标,但务必配合下游任务验证

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。但上线之前,一定用真实场景数据跑一遍人工抽检——指标只能告诉你模型「数学上好」,不能告诉你「业务上靠谱」。

好了,这一章就到这里。你回去做实验时,记得把每个指标都打印出来对比,别偷懒。