Rouge 评价指标

Rouge 评价指标

在自然语言处理(NLP)任务里,怎么自动评估文本生成的质量,一直是个核心问题。比如机器翻译、自动摘要、文本生成这类任务,我们都需要度量生成文本和参考文本之间的相似度。Rouge(Recall-Oriented Understudy for Gisting Evaluation)就是一种常用的文本评估指标,特别适合用来评摘要生成的质量。

Rouge 主要衡量生成文本和参考文本之间的 n-gram 匹配情况,常见的变体有 Rouge-N、Rouge-L、Rouge-W 和 Rouge-SU 等。

  • Rouge-N:计算 n-gram(n 个连续词)的匹配情况,最常用的是 Rouge-1 和 Rouge-2。
  • Rouge-L:基于最长公共子序列(LCS),用来衡量顺序上的相似度。
  • Rouge-W:是 Rouge-L 的加权版本,对较长的连续匹配赋予更高权重。
  • Rouge-SU:计算 Skip-Bigram(跳跃二元组)的匹配,同时引入单字 unigram。

pip install rouge

1. Rouge-N

Rouge-N 是 ROUGE 评测方法中的一种,主要用在 自动摘要、机器翻译、文本生成 等任务。它衡量生成文本(候选文本)和参考文本(标准答案)之间的 N-gram 重叠率,也就是算两者的 相似度。

N-gram 是 NLP 里的一个概念,指由 连续 N 个词(或字符)组成的片段。例如文本 ["我", "是", "中国", "人"],拆成不同 N-gram 的样子如下:

  • 1-gram(Unigram):以每个单独的词为单位,["我", "是", "中国", "人"]
  • 2-gram(Bigram):以两个连续的词为单位,["我是", "是中国", "中国人"]
  • 3-gram(Trigram):以三个连续的词为单位,["我是中国", "是中国人"]

Rouge-N 算的就是 参考文本和生成文本之间的 N-gram 重叠率。

假设我们拿到的生成文本和参考文本如下:

  • 机器摘要(Hypothesis): "我是中国人"
  • 标准摘要(Reference): "我是个地地道道堂堂正正的中国人"

对文本做分词(基于 Jieba)

  • 机器摘要(Hyp): ["我", "是", "中国", "人"]
  • 标准摘要(Ref): ["我", "是", "个", "地地道道", "堂堂正正", "的", "中国", "人"]

先做 1-gram 的统计:

  • 匹配内容: ["我", "是", "中国", "人"]
  • 机器摘要: ["我", "是", "中国", "人"]
  • 标准摘要: ["我", "是", "个", "地地道道", "堂堂正正", "的", "中国", "人"]

再做 2-gram 的统计:

  • 匹配内容: ["我是", "中国人"]
  • 机器摘要: ["我是", "是中国", "中国人"]
  • 标准摘要: ["我是", "是个", "个地地道道", "地地道道堂堂正正", "堂堂正正的", "的中国", "中国人"]

从这个例子能看出来,Rouge-N 是一个关注 Recall 的自动摘要评估指标。

注意:用 Rouge-N 评估前,应该先把停用词去掉。

示例代码:

import rouge
import jieba
import pandas as pd


def test01():
    my_rouge = rouge.Rouge(metrics=["rouge-1", "rouge-2"], stats=["r", 'p'])
    # hyps: 生成摘要
    # refs: 参考摘要
    hyp = jieba.lcut('我是中国人')
    ref = jieba.lcut('我是个地地道道堂堂正正的中国人')
    print('hyp 分词:', hyp)
    print('ref 分词:', ref)
    scores = my_rouge.get_scores(hyps=[' '.join(hyp)], refs=[' '.join(ref)])
    print(scores)

if __name__ == '__main__':
    test01()
[{'rouge-1': {'r': 0.5, 'p': 1.0}, 'rouge-2': {'r': 0.2857142857, 'p': 0.666666666}}]

前面我们都假设参考摘要只有一条。如果参考摘要有好几条,机器生成的这条摘要 Rouge-N 该怎么算呢?其实就是拿它和每条参考各算一遍,取最高的那个得分作为最终得分。

2. Rouge-L

Rouge-L 也是 ROUGE 评测方法中的一种,同样用在自动摘要、机器翻译、文本生成等任务。它靠计算生成文本和参考文本之间的最长公共子序列(LCS, Longest Common Subsequence)来评估相似度。

最长公共子序列(LCS)是指,给定两个序列(比如两个字符串),从中找出一个最长的子序列,让它在两个序列里都出现,而且元素顺序保持不变。例如:

  • A = “ABCBDAB”
  • B = “BDCABB”

它们的最长公共子序列是 “BCAB”,因为 “BCAB” 是这两个字符串里都出现过的最长子序列。

由此能看出,Rouge-L 关注的是词序上的匹配,而不只是词有没有共现。具体做法是用 LCS 的长度来算召回率和精确率,再通过 F1 分数综合评估生成文本和参考文本的相似度。

这个方法的好处是能捕捉句子级别的词序信息,不需要预先指定 N-gram 的长度。它的局限也很明显,只算一条最长子序列,忽略了其他可能子序列的贡献。

先做 1-gram 的统计:

  • LCS内容: ["我", "是", "中国", "人"]
  • 机器摘要: ["我", "是", "中国", "人"]
  • 标准摘要: ["我", "是", "个", "地地道道", "堂堂正正", "的", "中国", "人"]

from rouge import Rouge
import jieba
import logging
jieba.setLogLevel(logging.CRITICAL)


def demo():

    rouge = Rouge(metrics=['rouge-l'], stats=['r', 'p'])

    hyps = [' '.join(jieba.lcut('我是中国人'))]
    refs = [' '.join(jieba.lcut('我是一个地地道道堂堂正正的中国人'))]

    scores = rouge.get_scores(hyps=hyps, refs=refs, avg=False)
    print(scores)


if __name__ == '__main__':
    demo()

程序执行结果:

[{'rouge-l': {'r': 0.5, 'p': 1.0}}]

3. 其他 Rouge 变体

Rouge 系列里,除了上面两个,还有 Rouge-W、Rouge-S、Rouge-SU 等,但大多数 rouge 的实现里都没包含它们。原因大致是:

  • 它们比基本的 Rouge-N、Rouge-L 更复杂,计算开销更大。
  • Rouge-N(尤其是 Rouge-1 和 Rouge-2)已经能覆盖大部分评估需求。
  • 这些指标在某些场景下可能确实更有优势,但并不是文本生成任务通用的评估标准。
  • Rouge-N 是最常见、接受度最高的评估方式,更复杂的 Rouge 指标在实现上往往不一致,也难标准化,所以没被广泛采用。

从实践经验看,Rouge-1、Rouge-L 在非常短的摘要里表现更好,比如标题级别。Rouge-2、Rouge-L 在单文档摘要上表现很不错。