查看全课大纲(第 66 / 69 节)
  1. 1.NLP 的研究领域、应用与学习路径
  2. 2.自然语言的基本术语
  3. 3.信息论基础:信息熵与交叉熵
  4. 4.语言的进化与语义变迁
  5. 5.主观概率、客观概率与条件概率
  6. 6.贝叶斯原理与推理
  7. 7.随机变量:二项式、期望方差与联合概率
  8. 8.常见概率分布
  9. 9.辛普森悖论与贝叶斯解题实例
  10. 10.语言模型:语言概率与词袋模型
  11. 11.n-gram 语言模型(二元 / 三元 / N-Gram)
  12. 12.语言模型评价:交叉熵、困惑度与插值
  13. 13.朴素贝叶斯文本分类:垃圾邮件
  14. 14.拼写检查
  15. 15.中文分词:构词法、最大匹配、最优路径与 Jieba
  16. 16.词表示问题与语义相似度
  17. 17.TF-IDF 权重处理
  18. 18.One-Hot 表达与词表示的局限
  19. 19.Word2Vec 核心思想
  20. 20.Word2Vec 训练过程
  21. 21.Skip-gram 模型详解
  22. 22.词性标注任务与标注方法(规则 + N-Gram)
  23. 23.EM 算法与混合模型
  24. 24.高斯混合模型 GMM
  25. 25.从混合模型到隐马尔可夫模型
  26. 26.隐马尔可夫模型(HMM)推导与维特比解码
  27. 27.线性链条件随机场 CRF
  28. 28.贝叶斯网络与图模型
  29. 29.隐马尔可夫链与 HMM 推导
  30. 30.PLSA 主题模型
  31. 31.LSI 与 SVD 向量表达
  32. 32.LDA 与狄利克雷分布
  33. 33.主题模型实验复现(困惑度 / 分类 / 相似度 / 延展)
  34. 34.上下文无关文法(CFG)与句法分析
  35. 35.概率上下文无关文法 PCFG
  36. 36.统计机器翻译:词对齐与翻译模型
  37. 37.统计机器翻译中的对齐模型与参数估计
  38. 38.从统计机器翻译到神经机器翻译
  39. 39.神经元与反向传播速览(指向深度学习课)
  40. 40.CNN 原理及其在 NLP 中的应用
  41. 41.循环神经网络的基本原理与 Elman / Jordan 网络
  42. 42.LSTM:长短期记忆网络的核心思想与分步实现
  43. 43.Encoder-Decoder 框架
  44. 44.Seq2Seq 与早期注意力机制
  45. 45.注意力机制的背景与动机
  46. 46.注意力模型实现原理与匹配函数
  47. 47.记忆网络:组成与计算
  48. 48.注意力机制的应用与历史延展
  49. 49.Transformer 结构:Encoder-Decoder 栈与 Self-Attention
  50. 50.Multi-Head Attention、FFNN 与位置编码
  51. 51.层归一化、损失函数与 Universal Transformer 延展
  52. 52.预训练范式:从 ELMo 到 BERT
  53. 53.BERT 与掩码语言模型
  54. 54.BERT 下游任务微调与 GLUE 口径
  55. 55.GPT 与自回归语言建模
  56. 56.GPT-1 到 GPT-4 的演进
  57. 57.指令微调与 RLHF
  58. 58.分词器:BPE、WordPiece 与中文词表
  59. 59.上下文窗口与长文本处理
  60. 60.能力边界、成本与适用场景
  61. 61.文本分类实战:从朴素贝叶斯到逻辑回归
  62. 62.命名实体识别实战
  63. 63.文本生成与摘要实战
  64. 64.语义检索与向量库实战
  65. 65.RAG 知识库问答
  66. 66.NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估
  67. 67.幻觉与事实性问题
  68. 68.文本与知识结合(知识图谱压缩)
  69. 69.跨媒体信息检索与未来展望

NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估

小象实战讲义 · 自然语言处理

在自然语言处理(NLP)的模型开发与迭代中,如何科学、客观地评估模型生成文本的质量,是贯穿始终的核心问题。本节将系统梳理从机器翻译时代沿用至今的经典自动评估指标 BLEU 与 ROUGE,到基于预训练模型的语义评估指标 BERTScore,并最终回归到不可替代的人工评估。学完本节,你将掌握为不同 NLP 任务(如翻译、摘要、对话)选择合适的评估指标,并理解其背后原理与局限性的能力。

💡 核心导读

  • BLEU 的核心是“修正的 n-gram 精确率”:通过截断匹配计数和引入长度惩罚,为机器翻译评估提供了首个广泛使用的自动指标,但其对同义替换和语序调整不敏感。
  • ROUGE 从“精确率”转向“召回率”:以参考文本为分母,衡量生成内容对参考要点的覆盖程度,并引入最长公共子序列(LCS)来摆脱固定 n-gram 的束缚,常用于摘要评估。
  • BERTScore 用“语义相似度”替代“字面重叠”:利用预训练模型(如 BERT)的上下文向量计算 token 间的相似度,能更好地评估同义改写,但依赖于特定模型且不衡量事实正确性。
  • 自动指标是“代理指标”:所有自动指标都只是对“任务成功”这一终极目标的近似。评估工作的核心方法论是:先定义任务成功标准,再选择最接近该标准的代理指标,并最终通过人工评估进行校准和验证。

一、BLEU:修正的 n-gram 精确率与长度惩罚

BLEU(Bilingual Evaluation Understudy)由 Papineni 等人于 2002 年提出,最初用于机器翻译的自动评估。其核心思想是衡量机器翻译(候选译文)与一个或多个专业人工翻译(参考译文)之间的 n-gram 匹配程度。

1.1 修正的 n-gram 精确率(Modified n-gram Precision)

传统的 n-gram 精确率计算候选译文中匹配的 n-gram 数量与其 n-gram 总数的比值。但这种方法容易被“the the the”这类无意义重复刷高分。BLEU 对此进行了关键修正:匹配计数按参考译文中该 n-gram 的最大出现次数进行截断(clip)。

公式化定义: 对于给定的 n,令:

  • Countclip(n-gram)=min⁡(Countcand(n-gram),max⁡ref∈RCountref(n-gram))Count_{clip}(n\text{-gram}) = \min(Count_{cand}(n\text{-gram}), \max_{ref \in \mathcal{R}} Count_{ref}(n\text{-gram}))
  • pn=∑n-gram∈CandidateCountclip(n-gram)∑n-gram∈CandidateCount(n-gram)p_n = \frac{\sum_{n\text{-gram} \in \text{Candidate}} Count_{clip}(n\text{-gram})}{\sum_{n\text{-gram} \in \text{Candidate}} Count(n\text{-gram})}

其中,pnp_n 即为 n-gram 的修正精确率。

def modified_precision(candidate, references, n):
    """
    计算候选文本相对于一组参考文本的 n-gram 修正精确率。
    candidate: 字符串,候选文本。
    references: 字符串列表,参考文本列表。
    n: int, n-gram 的 n。
    返回: float, 修正精确率。
    """
    # 简单分词(按空格)
    cand_tokens = candidate.split()
    refs_tokens = [ref.split() for ref in references]

    # 生成候选文本的 n-gram 并计数
    from collections import Counter
    cand_ngrams = [tuple(cand_tokens[i:i+n]) for i in range(len(cand_tokens)-n+1)]
    cand_counter = Counter(cand_ngrams)
    total_cand_ngrams = len(cand_ngrams)

    # 计算截断计数
    clip_count = 0
    for ngram, cand_count in cand_counter.items():
        # 找出该 ngram 在所有参考中的最大出现次数
        max_ref_count = 0
        for ref in refs_tokens:
            ref_counter = Counter([tuple(ref[i:i+n]) for i in range(len(ref)-n+1)])
            max_ref_count = max(max_ref_count, ref_counter.get(ngram, 0))
        # 截断:取候选计数和最大参考计数中的较小值
        clip_count += min(cand_count, max_ref_count)

    if total_cand_ngrams == 0:
        return 0.0
    return clip_count / total_cand_ngrams

# 示例计算
candidate = "the cat the cat on the mat"
references = ["the cat is on the mat", "there is a cat on the mat"]

print("1-gram 修正精确率:", round(modified_precision(candidate, references, 1), 4))
print("2-gram 修正精确率:", round(modified_precision(candidate, references, 2), 4))
1-gram 修正精确率: 0.7143
2-gram 修正精确率: 0.6667

1.2 长度惩罚(Brevity Penalty)

如果候选译文比参考译文短,即使每个词都匹配,其信息量也可能不足。BLEU 引入了长度惩罚(BP)来惩罚过短的输出。

公式: 令 cc 为候选译文长度,rr 为与候选译文长度最接近的参考译文长度(通常取语料级总长度比)。则: BP={1if c>rexp⁡(1−r/c)if c≤rBP = \begin{cases} 1 & \text{if } c > r \\ \exp(1 - r/c) & \text{if } c \le r \end{cases}

当候选译文比参考译文短时,BP<1BP < 1,起到惩罚作用;当更长时,不奖励(BP=1BP=1)。

1.3 BLEU 分数的合成

最终的 BLEU 分数是各 n-gram 修正精确率的几何平均值,再乘以长度惩罚。

公式(Papineni et al., 2002, §2.3): BLEU=BP⋅exp⁡(∑n=1Nwnlog⁡pn)BLEU = BP \cdot \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)

通常 N=4N=4(即计算 1 到 4-gram),且权重 wnw_n 取等值 1/N1/N。由于是几何平均,只要任意一个 pnp_n 为 0,整体 BLEU 分数即为 0。

import math
from collections import Counter

def clip_precision(candidate, references, n):
    """n-gram 修正精确率:分子按参考中的最大出现次数截断。"""
    cand = candidate.split()
    refs = [r.split() for r in references]
    cand_grams = Counter(tuple(cand[i:i + n]) for i in range(len(cand) - n + 1))
    total = sum(cand_grams.values())
    if total == 0:
        return 0.0
    matched = 0
    for gram, cnt in cand_grams.items():
        best = 0
        for r in refs:
            rc = Counter(tuple(r[i:i + n]) for i in range(len(r) - n + 1))
            best = max(best, rc.get(gram, 0))
        matched += min(cnt, best)   # 截断
    return matched / total

candidate = "the cat the cat on the mat"
references = ["the cat is on the mat", "there is a cat on the mat"]

ps = [clip_precision(candidate, references, n) for n in range(1, 5)]
print("各阶修正精确率:", [round(p, 4) for p in ps])

c_len, r_len = len(candidate.split()), len(references[0].split())
bp = 1.0 if c_len > r_len else math.exp(1 - r_len / c_len)
print(f"长度: c={c_len}, r={r_len}, BP={round(bp, 4)}")

if 0.0 in ps:
    print("BLEU: 0.0(存在 p_n=0,几何平均直接归零)")
else:
    print("BLEU:", round(bp * math.exp(sum(0.25 * math.log(p) for p in ps)), 4))
各阶修正精确率: [0.7143, 0.6667, 0.4, 0.25]
长度: c=7, r=6, BP=1.0
BLEU: 0.4671

注意这里有两个细节:① 候选里 “the” 出现 3 次,跨参考取最大出现次数为 2(参考 1 含 the×2、参考 2 含 the×1),截断后 the 计 2 次;“cat” 出现 2 次、参考最多 1 次,再截断到 1 次,合计截断匹配 5 个,所以 1-gram 精确率为 5/7;② 二阶以上开始掉得快(0.6667 → 0.4 → 0.25),因为候选里 “the cat” 的重复拼接破坏了长片段匹配,而长度惩罚这一项反而没起作用(候选 7 个词比最接近的参考 6 个词更长,BP=1,不罚)。这正是 BLEU 的设计取向:它奖励”候选的片段都能在参考里找到出处”,不奖励长度。

1.4 BLEU 的结构性局限

必须清醒认识到 BLEU 的局限性:

  1. 召回率缺失:只衡量精确率(候选中的 n-gram 有多少出现在参考中),不直接惩罚漏译。漏译仅通过长度惩罚间接影响。
  2. 局部匹配:n-gram 只约束局部词序,对长距离语序调整不敏感。
  3. 同义替换惩罚:“快速”与“迅速”会被视为不同的 1-gram,导致分数降低。
  4. 句子级不稳定:BLEU 设计用于语料级评估,单个句子的 BLEU 分数波动极大,参考价值有限。
  5. 归一化敏感:分词、大小写、标点处理方式的不同会显著改变分数,跨研究比较时必须核对预处理口径。

二、ROUGE:面向召回的摘要评估指标

ROUGE(Recall-Oriented Understudy for Gisting Evaluation)由 Chin-Yew Lin 于 2004 年提出,主要用于自动文摘(文本摘要)的评估。其设计哲学从 BLEU 的“精确率导向”转向“召回率导向”,即关注“参考摘要中的要点有多少被系统摘要覆盖了”。

2.1 ROUGE-N

ROUGE-N 的定义与 BLEU 的修正精确率形成对比(Lin, 2004, §2):

ROUGE-N=∑SCountmatch(gramn)∑SCount(gramn)ROUGE\text{-}N = \frac{\sum_{S} Count_{match}(gram_n)}{\sum_{S} Count(gram_n)}(分母对参考摘要 S 累加,分子只累加”能匹配上的” n-gram)

分母是参考摘要中的 n-gram 总数,因此它本质上是 n-gram 召回率。同样可以有 ROUGE-1、ROUGE-2 等。

2.2 ROUGE-L:基于最长公共子序列(LCS)

ROUGE-L(Lin, 2004, §3)使用最长公共子序列(Longest Common Subsequence, LCS)来评估。LCS 允许非连续匹配,对语序变化有一定容忍度,且不依赖预先设定的 n。

给定候选摘要 XX(长度 mm)和参考摘要 YY(长度 nn),其 LCS 长度为 LCS(X,Y)LCS(X, Y)。可以基于此计算 LCS 精确率、召回率和 F1 值:

  • Rlcs=LCS(X,Y)nR_{lcs} = \frac{LCS(X, Y)}{n} (召回率)
  • Plcs=LCS(X,Y)mP_{lcs} = \frac{LCS(X, Y)}{m} (精确率)
  • Flcs=(1+β2)RlcsPlcsRlcs+β2PlcsF_{lcs} = \frac{(1 + \beta^2) R_{lcs} P_{lcs}}{R_{lcs} + \beta^2 P_{lcs}}

通常 β\beta 设为 1(即 F1)。在比较不同论文的 ROUGE-L 分数时,必须确认对方报告的是 RlcsR_{lcs}、PlcsP_{lcs} 还是 FlcsF_{lcs}。

def lcs_length(x, y):
    """计算两个序列 x 和 y 的最长公共子序列长度(动态规划)。"""
    m, n = len(x), len(y)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if x[i-1] == y[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]

def rouge_l(candidate, reference, beta=1.0):
    """
    计算候选文本和参考文本之间的 ROUGE-L (F1分数)。
    """
    cand_tokens = candidate.split()
    ref_tokens = reference.split()

    lcs_len = lcs_length(cand_tokens, ref_tokens)
    m, n = len(cand_tokens), len(ref_tokens)

    if n == 0 or m == 0:
        return 0.0

    r_lcs = lcs_len / n
    p_lcs = lcs_len / m

    if (r_lcs + p_lcs) == 0:
        return 0.0
    f_lcs = ((1 + beta**2) * r_lcs * p_lcs) / (r_lcs + beta**2 * p_lcs)
    return round(f_lcs, 4)

candidate_summary = "the cat sat on the mat"
reference_summary = "the cat is on the mat"
print("ROUGE-L F1:", rouge_l(candidate_summary, reference_summary))
ROUGE-L F1: 0.8333

2.3 ROUGE 的其他变体与局限

  • ROUGE-W(Lin, 2004, §4):加权 LCS,给连续的匹配子串更高的权重。
  • ROUGE-S(Lin, 2004, §5):Skip-bigram 共同出现统计,允许中间跳过若干个词。

ROUGE 的局限:它仍然是基于字面重叠的指标。如果摘要“抓住了要点但完全换了说法”,ROUGE 分数会很低。同时,它对冗余(重复的句子)不敏感,可能需要配合压缩率等指标使用。

三、BERTScore:基于语义相似度的评估

随着预训练语言模型(如 BERT)的出现,研究者开始利用模型的上下文表示来评估生成文本的语义质量。BERTScore(Zhang et al., 2019, arXiv:1904.09675)便是这一方向的代表。

3.1 核心机制

BERTScore 的核心思想是:用预训练模型(如 BERT)为候选文本和参考文本的每个 token 生成上下文向量,然后计算 token 间的余弦相似度矩阵,并通过贪心匹配(或最优匹配)计算精确率、召回率和 F1 值(Zhang et al., 2019, §3)。

  1. 表示:将候选文本 x^\hat{x} 和参考文本 xx 输入 BERT,获取每个 token 的上下文向量。
  2. 相似度计算:计算候选中每个 token 向量与参考中每个 token 向量的余弦相似度,得到一个相似度矩阵。
  3. 匹配与分数计算:
    • 精确率(PBERTP_{BERT}):对候选中的每个 token,找到参考中与之最相似的 token,取相似度值,然后对所有候选 token 取平均。衡量“候选的每个词是否都能在参考中找到语义相近的词”。
    • 召回率(RBERTR_{BERT}):对参考中的每个 token,找到候选中与之最相似的 token,取相似度值,然后对所有参考 token 取平均。衡量“参考的每个要点是否都被候选语义覆盖了”。
    • F1 值(FBERTF_{BERT}):PBERTP_{BERT} 和 RBERTR_{BERT} 的调和平均。
  4. 可选 IDF 加权:在求平均时,用 token 的逆文档频率(IDF)进行加权,降低高频虚词(如“的”、“是”)的权重,提升内容词的权重。
# BERTScore 伪代码示意(实际使用需调用 transformers 等库)
# 以下为逻辑描述,非可执行代码。

输入:候选文本 candidate, 参考文本 reference
输出:BERTScore 的 P, R, F

1. 加载预训练的 BERT 模型和分词器(例如 'bert-base-uncased')。
2. 对 candidate 和 reference 分别进行分词,并获取每个 token 的 BERT 最后一层隐藏状态作为向量表示。
   - 注意处理 [CLS], [SEP] 等特殊 token。
3. 计算相似度矩阵 M,其中 M[i][j] = cosine_sim(vec_cand[i], vec_ref[j])。
4. 计算 P_BERT:
   - 对 candidate 的每个 token i,找出在 reference 中相似度最高的值:max_j M[i][j]
   - P_BERT = average( max_j M[i][j] for i in candidate_tokens )
5. 计算 R_BERT:
   - 对 reference 的每个 token j,找出在 candidate 中相似度最高的值:max_i M[i][j]
   - R_BERT = average( max_i M[i][j] for j in reference_tokens )
6. 计算 F_BERT = 2 * P_BERT * R_BERT / (P_BERT + R_BERT) (如果分母不为零)。
7. (可选)在步骤4、5的 average 中,使用基于大型语料库计算的 IDF 值对每个 token 进行加权。

注意:实际应用中,通常使用官方实现的 `bert_score` 库,并指定具体的模型(如 `roberta-large`)。

3.2 BERTScore 的优势与代价

优势:

  • 语义感知:能识别同义替换和语序调整,例如“手机”和“智能手机”可能获得较高的相似度。
  • 与人工评价相关性高:在许多文本生成任务上,BERTScore 与人类判断的相关性优于 BLEU、ROUGE 等基于 n-gram 的指标。

代价与注意事项:

  1. 模型依赖性:分数依赖于所使用的预训练模型。换一个模型,分数就不可直接比较。报告 BERTScore 时必须注明模型名称(如 BERTScore (RoBERTa-large))。
  2. 不衡量事实正确性:BERTScore 衡量的是“语义相近”,而不是“事实正确”。一句流畅但事实错误的句子(如“太阳从西边升起”)可能因为与某个合理参考句语义相似而获得高分。
  3. 需要基准校准:BERTScore 的绝对值(如 0.9)本身没有明确意义。需要通过在一组随机句子或反义句上计算分数,建立一个基准(下界),才能解释分数的相对高低。

四、人工评估:不可替代的金标准

尽管自动指标高效、可重复,但人工评估始终是衡量 NLP 系统输出质量的最终金标准。

4.1 评估范式

  • 直接评估:评估员对单个系统输出在多个维度上打分(如 1-5 分 Likert 量表)。常见维度包括:
    • 充分性/忠实度:生成内容是否准确、完整地反映了源文信息或事实?是否包含幻觉(虚构内容)?
    • 流畅性:生成文本是否通顺、自然、符合语法?
  • 成对/多系统比较:评估员同时看到同一输入下多个系统的输出,并选出“更好”的一个,或进行排序。这种方法通常比直接评估更可靠。

4.2 确保评估质量

  • 标注一致性:必须报告评估员间的一致性,例如通过计算一致同意比例或 Cohen‘s Kappa 等统计量。低一致性意味着评估指南模糊或任务本身主观性强。
  • 透明报告:应详细说明样本量、抽样方法、评估员背景、评估指南和报酬,以确保评估结果的可信度和可复现性。

在实践中,成熟的 NLP 项目通常采用“小样本人工评估 + 大样本自动指标监控”的组合策略:用人工评估来校准方向、验证关键改进,用自动指标进行快速的日常回归测试和迭代。

五、核心结论:评估指标的方法论

本节并非简单的指标罗列,而是希望传达一套评估 NLP 系统的核心方法论:

  1. 自动指标都是代理指标(Proxy):BLEU、ROUGE、BERTScore 衡量的都是“与参考文本的相似度”,而我们真正关心的是“任务是否成功”(如翻译是否准确传达了意思,摘要是否抓住了核心)。因此,选择评估指标的第一步,是明确你的任务的成功标准是什么。
  2. 指标与任务匹配:
    • 机器翻译:传统上依赖 BLEU,现在可结合 BERTScore 等语义指标。
    • 文本摘要:ROUGE 系列是主流,常与 BERTScore 结合看。
    • 开放式生成(对话、创作):自动指标往往力不从心,更依赖人工评估或“使用模型作为评委”(如 GPT-4 作为裁判),但必须对评委模型进行偏差检查和校准。
  3. 验证指标的相关性:在你自己任务的数据上,抽取 50-100 个样本进行人工评估,计算自动指标分数与人工分数的相关系数(如 Spearman 秩相关)。如果相关性不合格,说明该自动指标不适合你的任务,应该更换。
  4. 反向使用指标进行诊断:评估指标不应只用于在论文中报告一个最终数字。在开发过程中,指标的变化(如改了一版提示词后 BLEU 掉了 2 个点)和在不同类型样本上的表现(如在长文本上 ROUGE 下降明显),能为模型改进提供宝贵的诊断信息。

📝 动手练一练

  1. 概念辨析:BLEU 的“修正精确率”和 ROUGE-N 在公式定义上最根本的区别是什么?这种区别反映了它们各自设计初衷的何种不同?
👉 点击查看参考答案

最根本的区别在于分母不同。

  • BLEU 的修正精确率:分母是候选译文中的 n-gram 总数。公式为:pn=∑Countclip(n-gram)∑candCount(n-gram)p_n = \frac{\sum Count_{clip}(n\text{-gram})}{\sum_{cand} Count(n\text{-gram})}。它衡量的是“候选译文中有多少 n-gram 是合理的(出现在参考中)”,是精确率导向。
  • ROUGE-N:分母是参考摘要中的 n-gram 总数。公式为:ROUGE-N=∑ref∑Countmatch(n-gram)∑ref∑Count(n-gram)ROUGE\text{-}N = \frac{\sum_{ref} \sum Count_{match}(n\text{-gram})}{\sum_{ref} \sum Count(n\text{-gram})}。它衡量的是“参考摘要中的要点(n-gram)有多少被系统摘要覆盖了”,是召回率导向。

这种区别反映了它们的设计初衷:BLEU 为机器翻译设计,更关注生成内容的“正确性”和“流畅性”(避免胡言乱语);ROUGE 为文本摘要设计,更关注生成内容对原文要点的“覆盖度”。

  1. 手算练习:给定候选译文 "the black cat sat on the mat",参考译文 "a black cat is sitting on the mat"。请手算(或写简单代码验证)其 1-gram 的 BLEU 修正精确率(modified_precision)和 ROUGE-1 的分数。假设分词按空格进行。
👉 点击查看参考答案

首先,分词:

  • 候选: ['the', 'black', 'cat', 'sat', 'on', 'the', 'mat']
  • 参考: ['a', 'black', 'cat', 'is', 'sitting', 'on', 'the', 'mat']

计算 BLEU 1-gram 修正精确率:

  • 候选 1-gram 及计数: {'the':2, 'black':1, 'cat':1, 'sat':1, 'on':1, 'mat':1},总数 = 7。
  • 参考 1-gram 计数: {'a':1, 'black':1, 'cat':1, 'is':1, 'sitting':1, 'on':1, 'the':1, 'mat':1}。
  • 截断匹配计数:
    • 'the': min(2, 1) = 1
    • 'black': min(1, 1) = 1
    • 'cat': min(1, 1) = 1
    • 'sat': min(1, 0) = 0 (参考中没有’sat’)
    • 'on': min(1, 1) = 1
    • 'mat': min(1, 1) = 1
    • 其他词候选计数为0不考虑。
    • 总截断计数 = 1+1+1+0+1+1 = 5
  • BLEU 1-gram 修正精确率 = 5 / 7 ≈ 0.7143

计算 ROUGE-1:

  • 匹配的 1-gram: {'black', 'cat', 'on', 'the', 'mat'}。注意:'the'在参考中出现1次,匹配1次。
  • 匹配总数 = 5。
  • 参考 1-gram 总数 = 8。
  • ROUGE-1 = 5 / 8 = 0.625
# 验证代码(自包含:不依赖上文块的函数)
from collections import Counter

def clip_precision(candidate, references, n):
    cand = candidate.split()
    refs = [r.split() for r in references]
    cand_grams = Counter(tuple(cand[i:i + n]) for i in range(len(cand) - n + 1))
    total = sum(cand_grams.values())
    if total == 0:
        return 0.0
    matched = 0
    for gram, cnt in cand_grams.items():
        best = 0
        for r in refs:
            rc = Counter(tuple(r[i:i + n]) for i in range(len(r) - n + 1))
            best = max(best, rc.get(gram, 0))
        matched += min(cnt, best)
    return matched / total

def rouge_n(candidate, reference, n):
    cand_grams = Counter(tuple(candidate.split()[i:i + n]) for i in range(len(candidate.split()) - n + 1))
    ref_grams = Counter(tuple(reference.split()[i:i + n]) for i in range(len(reference.split()) - n + 1))
    match_count = sum((ref_grams & cand_grams).values())
    ref_total = sum(ref_grams.values())
    return match_count / ref_total if ref_total > 0 else 0.0

cand = "the black cat sat on the mat"
ref = "a black cat is sitting on the mat"
print("BLEU 1-gram P:", round(clip_precision(cand, [ref], 1), 4))
print("ROUGE-1:", round(rouge_n(cand, ref, 1), 4))
BLEU 1-gram P: 0.7143
ROUGE-1: 0.625

本章小结

本节系统讲解了 NLP 中评估文本生成质量的经典与前沿方法。我们从基于 n-gram 字面重叠的 BLEU(精确率导向+长度惩罚)和 ROUGE(召回率导向+LCS)出发,认识到它们对同义替换和语序调整的局限性。进而,我们介绍了基于预训练模型语义表示的 BERTScore,它提供了更接近人类语义判断的评估方式,但也带来了模型依赖性和不衡量事实正确性的新挑战。最后,我们回归到人工评估这一不可替代的金标准,并强调了自动指标本质上是“任务成功”的代理指标这一核心观点。

📋 行动清单

  • 为你的任务定义成功标准:在开始评估前,用一两句话明确写出什么样的生成结果算是“好”的。
  • 选择并验证代理指标:根据任务类型(翻译、摘要等)初选 1-2 个自动指标,并抽取小样本计算其与人工评估的相关性。
  • 设计并执行小规模人工评估:制定清晰的评估指南,招募评估员,对关键模型版本或争议样本进行人工评判,并计算评估员间一致性。

—— 小象教研组