用 Python 计算文本 BLEU 分数和 ROUGE 值
- 2020年 08月13日
文本生成是自然语言处理 (NLP) 中常见的一类任务,例如机器翻译、自动摘要、图片标题生成等等。如何评估生成文本的质量,或者说衡量生成文本与参考文本之间的差异,是一个必须考虑的问题。目前比较常见的评估方法就是计算 BLEUBLEU 分数和 ROUGEROUGE 值。
BLEU
BLEUBLEU (Bilingual Evaluation Understudy, 双语评估替换) 一开始是为翻译工作而开发的,是一个比较候选文本翻译与其他一个或多个参考翻译的评价分数。完美匹配的得分为 1.01.0,完全不匹配则得分为 0.00.0。尽管它还没做到尽善尽美,但它具有计算速度快、容易理解、与具体语言无关等优点。
翻译系统的 BLEUBLEU 得分不可能为 1,除非它们与参考翻译完全相同。通常一个人类翻译在四个参考翻译下的得分为 0.34680.3468,在两个参考翻译下的得分为 0.25710.2571。
BLEUBLEU 评分由 Kishore Papineni 等人在 2002 年的论文《BLEU: a Method for Automatic Evaluation of Machine Translation》中提出。这种评测方法通过对候选翻译与参考文本中的相匹配的 n𝑛 元组进行计数,其中一元组 (1-gram1-gram / unigramunigram) 比较的是每一个单词,而二元组 (bigrambigram) 比较的则是每个单词对,以此类推,并且这种比较是不管单词顺序的。匹配个数越多,表明候选翻译的质量就越好。
同时为了避免机器翻译系统为了追求高精度而生成过多的“合理”单词,从而导致翻译结果不恰当。在识别出匹配的候选单词之后,相应的参考单词就被视为用过了,以确保不会对产生大量合理词汇的候选翻译进行加分。在论文中这被称之为修正的 n𝑛 元组精度。
BLEU 评分是用来比较语句的,但是又提出了一个能更好地对语句块进行评分的修订版本,这个修订版根据 n 元组出现的次数来使 n 元组评分正常化。首先逐句计算 n 元组匹配数目,然后为所有候选句子加上修剪过的 n 元组计数,并除以测试语料库中的候选 n 元组个数,以计算整个测试语料库修正后的精度分数 pn。
ROUGE
ROUGEROUGE 受到了 BLEUBLEU 的启发,不同之处在于 ROUGEROUGE 采用召回率来作为指标。基本思想是将模型生成的摘要与参考摘要的 n𝑛 元组贡献统计量作为评判依据。
ROUGEROUGE 由 Chin-Yew Lin 在 2004 年的论文《ROUGE: A Package for Automatic Evaluation of Summaries》中提出。与 BLEUBLEU 类似,通过统计生成的摘要与参考摘要集合之间重叠的基本单元(n𝑛 元组)的数目来评估摘要的质量,该方法已成为自动文摘系统评价的主流方法。
常见的 ROUGEROUGE 评价指标有 ROUGE-NROUGE-N 和 ROUGE-LROUGE-L。其中,ROUGE-NROUGE-N 计算生成的摘要与参考摘要的 n-gramn-gram 召回率,通常用 ROUGE-1/2ROUGE-1/2 来评估,而 ROUGE-LROUGE-L 则匹配两个文本单元之间的最长公共序列 (LCS, Longest Common Sub sequence)。
ROUGE-NROUGE-N 的计算方法非常简单,就是计算 n𝑛 元组的召回率:
ROUGE-N=∑S∈{Ref Summaries}∑n-gram∈SCountmatch(n-gram)∑S∈{Ref Summaries}∑n-gram∈SCount(n-gram)ROUGE-N=∑𝑆∈{Ref Summaries}∑n-gram∈𝑆𝐶𝑜𝑢𝑛𝑡𝑚𝑎𝑡𝑐ℎ(n-gram)∑𝑆∈{Ref Summaries}∑n-gram∈𝑆𝐶𝑜𝑢𝑛𝑡(n-gram)
其中 Ref SummariesRef Summaries 表示参考摘要集合,Countmatch(n-gram)𝐶𝑜𝑢𝑛𝑡𝑚𝑎𝑡𝑐ℎ(n-gram) 表示生成的摘要和参考摘要中同时出现的 n-gramn-gram 的个数,Count(n-gram)𝐶𝑜𝑢𝑛𝑡(n-gram) 则表示参考摘要中出现的 n-gramn-gram 个数。因此分子就是生成摘要与参考摘要相匹配的 n-gramn-gram 个数,分母就是参考摘要中所有的 n-gramn-gram 个数。
Rouge-LRouge-L 使用了最长公共子序列,L 即是 LCS (longest common subsequence, 最长公共子序列) 的首字母,其计算方式如下:
Rlcs=LCS(X,Y)mPlcs=LCS(X,Y)nFlcs=(1+β2)RlcsPlcsRlcs+β2Plcs𝑅𝑙𝑐𝑠=𝐿𝐶𝑆(𝑋,𝑌)𝑚𝑃𝑙𝑐𝑠=𝐿𝐶𝑆(𝑋,𝑌)𝑛𝐹𝑙𝑐𝑠=(1+𝛽2)𝑅𝑙𝑐𝑠𝑃𝑙𝑐𝑠𝑅𝑙𝑐𝑠+𝛽2𝑃𝑙𝑐𝑠
其中 LCS(X,Y)𝐿𝐶𝑆(𝑋,𝑌) 是 X𝑋 和 Y𝑌 的最长公共子序列的长度,m,n𝑚,𝑛 分别表示参考摘要和生成摘要的长度(一般就是所含词的个数),Rlcs,Plcs𝑅𝑙𝑐𝑠,𝑃𝑙𝑐𝑠 分别表示召回率和准确率。最后的 Flcs𝐹𝑙𝑐𝑠 即是我们所说的 ROUGE-LROUGE-L。通常 β𝛽 被设置为一个很大的数,所以 ROUGE-LROUGE-L 几乎只考虑了 Rlcs𝑅𝑙𝑐𝑠,也就是召回率。
计算 BLEU 分数
NLTK 自然语言工具包库提供了 BLEUBLEU 评分的实现,你可以使用它来评估生成的文本。
语句 BLEU 分数
NLTK 提供了 sentence_bleu()
函数,用于根据一个或多个参考语句来评估候选语句。参考语句必须为语句列表,其中每个语句是一个词语列表。候选语句则直接是一个词语列表。例如:
from nltk.translate.bleu_score import sentence_bleu reference = [['this', 'is', 'a', 'test'], ['this', 'is' 'test']] candidate = ['this', 'is', 'a', 'test'] score = sentence_bleu(reference, candidate) print(score) # > 1.0
语料库 BLEU 分数
NLTK 还提供了一个称为 corpus_bleu()
的函数来计算多个句子的 BLEUBLEU 分数。
参考文档必须为文档列表,其中每个文档是一个参考语句列表,每个参考语句是一个词语列表。候选文档必须被指定为句子列表,每个句子是一个词语列表。以下是一个候选文档、两个参考文档的例子。
# two references for one document from nltk.translate.bleu_score import corpus_bleu references = [[['this', 'is', 'a', 'test'], ['this', 'is' 'test']]] candidates = [['this', 'is', 'a', 'test']] score = corpus_bleu(references, candidates) print(score) # > 1.0
运行这个例子就像之前一样输出满分
设置 N-Gram 权重
NLTK 中提供的 BLEUBLEU 评分方法还允许你在计算 BLEUBLEU 分数时为不同的 n𝑛 元组指定不同的权重。权重被指定为一个数组,其中每个索引对应相应次序的 n𝑛 元组,然后通过计算加权几何平均值来对它们进行加权计算。默认情况下,sentence_bleu()
和 corpus_bleu()
分数计算累加的 4 元组 BLEUBLEU 分数,也称为 BLEU-4BLEU-4 分数。
例如 BLEU-4BLEU-4 对 1 元组,2 元组,3 元组和 4 元组分数的权重都为 1414:
# 4-gram cumulative BLEU from nltk.translate.bleu_score import sentence_bleu reference = [['this', 'is', 'small', 'test']] candidate = ['this', 'is', 'a', 'test'] score = sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25)) print(score) # > 0.707106781187
累加的 1 元组和单独的 1 元组 BLEUBLEU 使用相同的权重,也就是 (1,0,0,0)
。累加的 2 元组 BLEUBLEU 分数为 1 元组和 2 元组分别赋 50%50% 的权重,计算累加的 3 元组 BLEU 为 1 元组,2 元组和 3 元组分别为赋 33%33% 的权重。
例如计算 BLEU-1,BLEU-2,BLEU-3,BLEU-4BLEU-1,BLEU-2,BLEU-3,BLEU-4 的累加得分:
# cumulative BLEU scores from nltk.translate.bleu_score import sentence_bleu reference = [['this', 'is', 'small', 'test']] candidate = ['this', 'is', 'a', 'test'] print('Cumulative 1-gram: %f' % sentence_bleu(reference, candidate, weights=(1, 0, 0, 0))) print('Cumulative 2-gram: %f' % sentence_bleu(reference, candidate, weights=(0.5, 0.5, 0, 0))) print('Cumulative 3-gram: %f' % sentence_bleu(reference, candidate, weights=(0.33, 0.33, 0.33, 0))) print('Cumulative 4-gram: %f' % sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25))) # > Cumulative 1-gram: 0.750000 # > Cumulative 2-gram: 0.500000 # > Cumulative 3-gram: 0.632878 # > Cumulative 4-gram: 0.707107
可以看到结果的差别很大,比单独的 n-gramn-gram 分数更具有表达性。在描述文本生成系统的性能时,通常会报告从 BLEU-1BLEU-1 到 BLEU-4BLEU-4 的累加分数。
计算 ROUGE 值
考虑到实践中最经常计算的就是 ROUGE-1ROUGE-1、ROUGE-2ROUGE-2 和 ROUGE-LROUGE-L 这三个评估指标,因此简单地使用 rouge 库就可以了,它虽然只能计算 ROUGE 1/2/LROUGE 1/2/L,但是十分方便。
先通过 pip install rouge
安装,然后就可以直接调用 Rouge.get_scores()
计算了:
from rouge import Rouge candidate = ['i am a student from xx school'] # 预测摘要, 可以是列表也可以是句子 reference = ['i am a student from school on china'] #真实摘要 rouge = Rouge() rouge_score = rouge.get_scores(hyps=candidate, refs=reference) print(rouge_score[0]["rouge-1"]) print(rouge_score[0]["rouge-2"]) print(rouge_score[0]["rouge-l"]) # > {'f': 0.7999999950222222, 'p': 0.8571428571428571, 'r': 0.75} # > {'f': 0.6153846104142012, 'p': 0.6666666666666666, 'r': 0.5714285714285714} # > {'f': 0.7929824561399953, 'p': 0.8571428571428571, 'r': 0.75} candidates = ['i am a student from xx school', 'happy new year'] references = ['i am a student from school on china', 'happy birthday'] rouge_score = rouge.get_scores(hyps=candidates, refs=references, avg=True) print(rouge_score["rouge-1"]) print(rouge_score["rouge-2"]) print(rouge_score["rouge-l"]) # > {'r': 0.625, 'p': 0.5952380952380952, 'f': 0.5999999951111111} # > {'r': 0.2857142857142857, 'p': 0.3333333333333333, 'f': 0.3076923052071006} # > {'r': 0.625, 'p': 0.5952380952380952, 'f': 0.5999999951111111}
结果会输出在参考摘要的 1-gram1-gram 和 2-gram2-gram 的准确率、召回率和 F1 值。ROUGE-LROUGE-L 类似,最长公共子序列在生成的摘要中所占比例是准确率,在参考摘要中所占比例是召回率,然后可以计算出 F1 值。
注意:对于英文直接把句子输入即可,中文如果没有空格就无法识别词,所以得先分词之后再计算 ROUGEROUGE 值。如果以字为单位也得每个两个字中间加一个空格。
参考
[1] A Gentle Introduction to Calculating the BLEU Score for Text in Python
[2] 浅谈用Python计算文本BLEU分数
[3] ROUGE评价方法详解(一)
[4] 自动文摘评测方法:Rouge-1、Rouge-2、Rouge-L、Rouge-S
[5] pyrouge和rouge,文本摘要评测方法库