BLEU (其全称为Bilingual Evaluation Understudy), 其意思是双语评估替补。所谓Understudy (替补),意思是代替人进行翻译结果的评估。尽管这项指标是为翻译而发明的,但它可以用于评估一组自然语言处理任务生成的文本。
1. Bilingual Evaluation Understudy: BLEU在自然语言处理中的机器翻译任务中, BLEU非常常见, 它是用于评估模型生成的句子(candidate)和实际句子(reference)的差异的指标.
它的取值范围在0.0到1.0之间, 如果两个句子完美匹配(perfect match), 那么BLEU是1.0, 反之, 如果两个句子完美不匹配(perfect mismatch), 那么BLEU为0.0.
虽然这个指标不够完美, 但是它有5个非常引人注目的好处(compelling benefits):
BLEU值是2002年由IBM 科学家 Kishore Papineni在其论文[2]中提出的: “BLEU: a Method for Automatic Evaluation of Machine Translation“.
BLEU方法的实现是分别计算candidate句和reference句的N-grams模型[3], 然后统计其匹配的个数来计算得到的. 显然, 这种比较方法, 是与语序无关的.
论文对匹配的 N-grams 计数进行了修改,以确保它考虑到reference文本中单词的出现,而非奖励生成大量合理翻译单词的候选结果。本文将其称为修正的 N-grams 精度。
此外, 有一种改进版的通过normalize N-grams的改进版BLEU. 其目的是提升对多个句子组成的块(block)提升翻译效果.
在实践中得到完美的分数是不太可能的,因为翻译结果必须与reference句完全匹配。甚至人工翻译都不可能做到这一点。而且, 由于不同的数据集reference句的质量和数量不同, 所以跨数据集计算BLEU值可能带来一些麻烦.
对机器翻译来说, 我们可以使用BLEU得分来评价其它的语言生成任务, 比如:
语言生成图像标题生成文本总结语音识别 2. 计算BLEU得分Python自然语言工具包库: NLTK, 提供了BLEU得分计算的实现, 所以如果你只是像译者一样使用的话, 可以先不去了解其内部的实现.
2.1NLTK提供了sentence_bleu()来评估一组candidate句和reference句的BLEU得分情况. 其中, 这两个句子都必须用一系列tokens表示:
>>> 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这个例子的结果为1.0, 因为candidate与reference的第1个句子perfect match了.
2.2 Corpus(语料库)的BLEU值NLTK同样提供了corpus_bleu()函数, 它用于来对多个句子比如一个段落甚至一篇文章进行得分评价.
参考句必须由一系列documents组成, 每个document都应该由一系列references组成, 同样的, 每个句子都应该拆分为一个个token. 如下, 其实比上一部分多了一个维度.
可能上面的解释还是容易让人困惑, 下面是一篇文档的2个参考句的示例(我的理解: 比Sentence的都多加了一个维度):
# 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 3 累积BLEU和独立BLEU在NLTK中, 其允许用户显式指定不同的N-grams的权重以便来计算BLEU的值. 这使得用户可以灵活的计算不同类型的BLEU值, 比如独立的BLEU或者累积的BLEU.
3.1 独立N-Gram得分一个独立的N-gram得分是对gram是否按特定顺序排列的评估方式, 比如1-gram和2-gram (2-gram又成为bigram)[3]:
# 1-gram individual 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=(1, 0, 0, 0))>>> print(score)0.75>>> candidate = ['this', 'test', 'is', 'a']>>> score = sentence_bleu(reference, candidate, weights=(1, 0, 0, 0))>>> score0.75可以看到, 1-gram有3/4个词完全对应上, 所以得分为0.75, 那么如果我们试试把candidate的顺序打乱呢? 结果仍为0.75~
下面, 作者测试了1-4gram如下:
>>> from nltk.translate.bleu_score import sentence_bleu>>> reference = [['this', 'is', 'a', 'test']]>>> candidate = ['this', 'is', 'a', 'test']>>> print('Individual 1-gram: %f' % sentence_bleu(reference, candidate, weights=(1, 0, 0, 0)))>>> print('Individual 2-gram: %f' % sentence_bleu(reference, candidate, weights=(0, 1, 0, 0)))>>> print('Individual 3-gram: %f' % sentence_bleu(reference, candidate, weights=(0, 0, 1, 0)))>>> print('Individual 4-gram: %f' % sentence_bleu(reference, candidate, weights=(0, 0, 0, 1)))Individual 1-gram: 1.000000Individual 2-gram: 1.000000Individual 3-gram: 1.000000Individual 4-gram: 1.000000 3.2 累积N-Gram得分累积N-Gram得分指的是为各个gram对应的权重加权, 来计算得到一个加权几何平均(weighted geometric mean). 默认情况下, sentence_bleu()和corpus_bleu()都是计算累积的4-gram BLEU分数的, 也称之为BLEU-4.
BLEU-4的加权策略如下: 1/4 (25%) 或者 0.25 对 1-gram, 2-gram, 3-gram and 4-gram 的得分.
下面, 我们分别计算从BLEU-1到BLEU-4的累积N-Gram得分:
>>> print('Cumulative 1-gram: %f' % sentence_bleu(reference, candidate, weights=(1, 0, 0, 0)))Cumulative 1-gram: 0.750000>>> print('Cumulative 2-gram: %f' % sentence_bleu(reference, candidate, weights=(0.5, 0.5, 0, 0)))Cumulative 2-gram: 0.500000>>> print('Cumulative 3-gram: %f' % sentence_bleu(reference, candidate, weights=(0.33, 0.33, 0.33, 0)))Cumulative 3-gram: 0.000000>>> print('Cumulative 4-gram: %f' % sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25)))Cumulative 4-gram: 0.000000通常情况下, 我们会统计BLEU1到BLEU4的累积值作为评估 文本生成系统(text generation system) 的效果.
4. 样例本部分, 我们将对BLEU值的设计develop further intuition(通过一些例子), 以一个单句的例子为例:
the quick brown fox jumped over the lazy dog
首先, 让我们看看完美匹配的得分:
# prefect match>>> from nltk.translate.bleu_score import sentence_bleu>>> reference = [['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog']]>>> candidate = ['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog']>>> score = sentence_bleu(reference, candidate)>>> print(score)1.0接下来, 我们把quick同意替换为fast, 看看效果:
# one word different...>>> candidate = ['the', 'fast', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog']>>> score = sentence_bleu(reference, candidate)>>> print(score) 0.7506238537503395接着在此基础上再改一个单词, 把lazy 改成 sleepy
# two words different...>>> candidate = ['the', 'fast', 'brown', 'fox', 'jumped', 'over', 'the', 'sleepy', 'dog']>>> score = sentence_bleu(reference, candidate)>>> print(score) 0.4854917717073234可以看到, BLEU值出现了下降, 因为match的对越来越少了. 现在, 我们可以试试这种形式: 将candidate句变短, 再跟reference进行比较
# 删除candidate句子最后两个单词, 即让candidate变短...>>> candidate = ['the', 'fast', 'brown', 'fox', 'jumped', 'over', 'the']>>> score = sentence_bleu(reference, candidate)>>> print(score)0.7514772930752859那么, 如果 将candidate句变长, 再跟reference进行比较呢?
...>>> candidate = ['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog', 'from', 'space']>>> score = sentence_bleu(reference, candidate)>>> print(score)0.7860753021519787我们可以看到, 分数反而越来越高啦~, BLEU中的数学理论[2, 4]是相当简单的,我也鼓励你阅读这篇论文,并探索自己在电子表格中计算句子水平分数。
参考[1] A Gentle Introduction to Calculating the BLEU Score for Text in Python
[2] BLEU: a Method for Automatic Evaluation of Machine Translation
[3] 蕉叉熵: N-grams说明
[4] BLEU 维基百科