21章  如何使用BLEU分数评估生成的文本

BLEU或双语评估是用于将文本的候选翻译与一个或多个参考翻译进行比较的分数。虽然是为翻译而开发的,但它可用于评估为一系列自然语言处理任务生成的文本。在本教程中,您将了解使用Python中的NLTK库评估和评分候选文本的BLEU分数。完成本教程后,您将了解:

  • 平缓地引入BLEU分数和直观上理解其计算方法。
  • 如何使用在Python中使用NLTK库计算句子和文档BLEU分数。
  • 如何使用一组小示例来直观地了解候选文本和参考文本之间的差异如何影响最终的BLEU分数。

21.1 教程概述

本教程分为以下几部分:

  1. 双语评估得分
  2. 计算BLEU分数
  3. 累积和单个BLEU分数
  4. 工作的例子

21.2 双语评估得分

双语评估得分,或简称BLEU,是用于评估生成的句子到参考句子的度量。完美匹配得分为1.0,而完全不匹配得分为0.0这个分数是用来评估自动机器翻译系统所做的预测指标,它并不完美,但确实提供了5个引人注目的好处:

  • 计算速度快,成本低廉。
  • 这很容易理解。
  • 它与语言无关。
  • 它与人类的评估高度相关。
  • 它已被广泛采用。

BLEU评分由Kishore Papineni等人提出,在他们2002年的论文BLEU:一种自动评估机器翻译的方法。该方法通过匹配候选翻译中的n-gram和参考文本中的n-gram计数来进行工作,其中1-gramunigram将是每个标记,并且bigram比较将是每个单词对,这种比较不考虑词序

BLEU实现者的主要编程任务是将候选者的n-gram与参考翻译的n-gram进行比较并计算匹配数。这些匹配与位置无关。匹配越多,候选翻译就越好。

BLEU: a Method for Automatic Evaluation of Machine Translation, 2002.

修改匹配的n-gram的计数,以确保它考虑到参考文本中出现的单词,而不会对产生的大量合理词汇的候选翻译计分,这在文章中被称为修改的n-gram精度

不幸的是,MT系统可能会过度生成合理的单词,导致不可能但高精度的翻译[...]直观地,问题很明显:在识别出匹配的候选词之后,应该认为参考词已经用过了。我们将这种直觉形式化为修改后的单字组精度。

BLEU: a Method for Automatic Evaluation of Machine Translation, 2002.

该分数用于比较句子,但是为了更好地对多个句子进行评分,我们还提出了一种改进的方法,即根据n-grams出现的次数对进行规范化。

我们首先逐句计算n-gram匹配。接下来,我们为所有候选句子添加剪切的n-gram计数,并除以测试语料库中的候选n-gram的数量,以计算整个测试语料库的修改的精确度分数pn

BLEU: a Method for Automatic Evaluation of Machine Translation, 2002.

因为翻译必须与参考完全不可能完全匹配,在实践中不可能获得满分,人类翻译甚至都无法做到这一点。用于计算BLEU分数的参考文献的数量和质量参差不齐意味着比较数据集之间的分数是一个不容易处理的问题。

BLEU度量范围从01。极少数翻译可以获得1分,除非它们与参考翻译相同。出于这个原因,即使是一个人类翻译也不一定能在大约500个句子(40个一般新闻报道)的测试语料库中得分为1。一个人类翻译者对四个参考文献得分为0.3468,对两个参考文献得分为0.2571

BLEU: a Method for Automatic Evaluation of Machine Translation, 2002.

除了翻译,我们还可以通过深度学习方法将BLEU评分用于其他语言生成问题,例如:

  • 语言生成。
  • 图像字幕生成。
  • 文字摘要。
  • 语音识别。
  • 以及更多。

21.3 计算BLEU分数

Python Natural Language Toolkit库或NLTK提供了BLEU分数的实现,您可以使用它来根据引用评估生成的文本。

21.3.1 句子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)

代码清单21.1:计算句子BLEU得分的例子

运行此示例将打印出一个完美的分数,因为候选者完全匹配其中一个引用。

1.0

代码清单21.2:计算句子BLEU得分的示例输出。

21.3.2 语料库BLEU分数

NLTK还提供了一个名为corpus_bleu()的函数,用于计算多个句子(如段落或文档)的BLEU分数。必须将引用指定为文档list,其中每个文档是引用list,每个备选引用是标记list,例如标记列表的list。必须将候选文档指定为list,其中每个文档是令牌list,例如令牌列表的list,这有点令人困惑;这是一个文档的两个引用的示例,困惑就看实例代码。

# 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)

代码清单21.3:计算语料库BLEU分数的示例

运行该示例将像以前一样打印出完美的分数。

1.0

代码清单21.4:计算语料库BLEU得分的示例输出

21.4 累积和单个BLEU分数

NLTK中的BLEU分数计算允许您在计算BLEU分数时指定不同n-gram的权重。这使您可以灵活地计算不同类型的BLEU分数,例如单个和累积的n-gram分数。让我们来看看。

21.4.1 单个n-gram分数

单独的n-gram分数是仅匹配特定顺序的grams的评估,例如单个单词(1-gram)或单词对(2-grambigram),权重被指定为元组,其中每个索引参照gram顺序。要仅计算1-gram匹配的BLEU分数,您可以为1-gram指定权重12-grams3-grams4-grams的权重为0,权重为(1,0,0,0)。例如:

ffrom 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)

清单21.5:计算单个1BLEU分数的示例。

运行此示例会打印0.5分。

0.75

代码清单21.6:计算单个1BLEU分数的示例输出。

我们可以针对14的单个n-gram重复此示例,如下所示

# n-gram individual BLEU
from nltk.translate.bleu_score import sentence_bleu
reference = [[ 'this' , 'is' , 'small' , '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)))

代码清单21.7:计算单个n-gram BLEU分数的示例

运行该示例会得到以下结果。

Individual 1-gram: 0.750000

Individual 2-gram: 0.333333

Individual 3-gram: 0.000000

Individual 4-gram: 0.000000

代码清单21.8:计算单个n-gram BLEU分数的示例输出

虽然我们可以计算单个BLEU分数,但这不是该方法的用途,并且分数没有很多意义,或者似乎可以解释。

21.4.2 累积n-gram分数

累积分数指的是从1n的所有阶数的单个n-gram分数的计算,并通过计算加权几何平均值对它们进行加权。默认情况下,sentence_bleu()和语料库corpus_bleu()得分计算累积的4-gramsBLEU分数,也称为BLEU-4。对于1-gram2-gram3-gram4-gram分数,BLEU-4的权重分别为1/425%)或0.25。例如:

# 4-gram cumulative BLEU
from nltk.translate.bleu_score import sentence_bleu
reference = [[ 'this' , 'is' , 'small' , 'test',’and’ ]]
candidate = [ 'this' , 'is' , 'a' , 'test' ]
score = sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25))
print(score)

代码清单21.9:计算累积4BLEU分数的示例

运行此示例将打印以下分数:

1.5319719891192393e-231

代码清单21.10:计算累积4BLEU分数的示例输出

返回值和作者书的结果不一样,原因出在:没有三元和4元词组,运行过程中sentence_bleu()内部出了问题。反馈错误如下:越界错误

/home/oliverbak/anaconda3/lib/python3.6/site-packages/nltk/translate/bleu_score.py:523: UserWarning:

The hypothesis contains 0 counts of 3-gram overlaps.

Therefore the BLEU score evaluates to 0, independently of

how many N-gram overlaps of lower order it contains.

Consider using lower n-gram order or use SmoothingFunction()

  warnings.warn(_msg)

/home/oliverbak/anaconda3/lib/python3.6/site-packages/nltk/translate/bleu_score.py:523: UserWarning:

The hypothesis contains 0 counts of 4-gram overlaps.

Therefore the BLEU score evaluates to 0, independently of

how many N-gram overlaps of lower order it contains.

Consider using lower n-gram order or use SmoothingFunction()

  warnings.warn(_msg)

累积和单个1-gram BLEU使用相同的权重,1-gram为(1,0,0,0)。2-gram1-gram权重各为50%,1-gram2-gram3-gram权重各为33%。让我们通过计算BLEU-1BLEU-2BLEU-3BLEU-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)))

代码清单21.11:计算累积n-gram BLEU分数的示例

运行该示例将打印以下分数。它们与独立的单个n-gram分数完全不同且更具表现力。

 Cumulative 1-gram: 0.750000

 Cumulative 2-gram: 0.500000

 Cumulative 3-gram: 0.632878

 Cumulative 4-gram: 0.707107

代码清单21.12:计算累积n-gram BLEU分数的示例输出

在描述文本生成系统的技能时,通常会报告累积的BLEU-1BLEU-4分数。

21.5 工作的例子

在本节中,我们试图通过一些例子来进一步直观说明BLEU分数。我们使用以下单个参考句子完成整个例子:

the quick brown fox jumped over the lazy dog

代码清单21.13:计算BLEU分数的工作示例的示例文本

首先,让我们看看一个完美的分数。

# 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)

代码清单21.14:两个匹配案例的示例运行该示例打印完美匹配。

1.0

代码清单21.15:两个匹配案例的示例输出BLEU得分

接下来,让我们改变一个词,quick改为fast

# prefect match
from nltk.translate.bleu_score import sentence_bleu
reference = [[ 'the' , 'quick' , 'brown' , 'fox' , 'jumped' , 'over' , 'the' , 'lazy' , 'dog' ]]
candidate = [ 'the' , 'fast' , 'brown' , 'fox' , 'jumped' , 'over' , 'the' , 'lazy' , 'dog' ]
score = sentence_bleu(reference, candidate)
print(score)

代码清单21.16:使一个单词不同的例子

结果是得分略有下降。

0.7506238537503395

清单21.17:使一个单词不同时的示例输出BLEU得分。

尝试改变两个词,quickfast“lazy”sleepy

# prefect match
from nltk.translate.bleu_score import sentence_bleu
reference = [[ 'the' , 'quick' , 'brown' , 'fox' , 'jumped' , 'over' , 'the' , 'lazy' , 'dog' ]]
candidate = [ 'the' , 'fast' , 'brown' , 'fox' , 'jumped' , 'over' , 'the' , 'sleepy' , 'dog' ]
score = sentence_bleu(reference, candidate)
print(score)

代码清单21.18:使两个单词不同的示例

运行这个例子,我们可以看到技能的线性下降。

0.4854917717073234

代码清单21.19:使两个单词不同时的示例输出BLEU得分。

如果候选的所有单词都不同怎么办?

# all words different
from nltk.translate.bleu_score import sentence_bleu
reference = [[ ' the ' , ' quick ' , ' brown ' , ' fox ' , ' jumped ' , ' over ' , ' the ' , ' lazy ' , ' dog ' ]]
candidate = [ ' a ' , ' b ' , ' c ' , ' d ' , ' e ' , ' f ' , ' g ' , ' h ' , ' i ' ]
score = sentence_bleu(reference, candidate)
print(score)

代码清单21.20:使所有单词不同的示例

我们得分可能更差。

0.0

清单21.21:使所有单词不同时的示例输出BLEU得分。

现在,让我们尝试一个比参考词少的候选词(例如删掉最后两个词),但这些词都是正确的。

# 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'  ]
score = sentence_bleu(reference, candidate)
print(score)

代码清单21.22:缩短一个案例的示例当两个单词

分数很像上面两个单词错了的分数。

0.7514772930752859

清单21.23:缩短一个案例时的示例输出BLEU得分。

如果我们让候选的两个单词长于参考文件怎么样?

# 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', 'from','space']
score = sentence_bleu(reference, candidate)
print(score)

代码清单21.24:使用一个更长案例的例子

再次,我们可以看到。我们的直觉成立,并且得分就像两个字错的几乎一样。

0.7860753021519787

清单21.25:翻译比原文长两个单词的BLEU得分。

最后,让我们比较一个太短的候选例子:长度只有两个单词。

# prefect match
from nltk.translate.bleu_score import sentence_bleu
reference = [[ 'the' , 'quick' , 'brown' , 'fox' , 'jumped' , 'over' , 'the' , 'lazy' , 'dog' ]]
candidate = [ 'the' , 'quick']
score = sentence_bleu(reference, candidate)
print(score)

代码清单21.26:使一个案例太短的例子

运行此示例首先会打印一条警告消息,指示无法执行评估的3克及以上部分(最多4克)。

这是公平的,因为我们只有2克与候选人一起工作。

UserWarning:

Corpus/Sentence contains 0 counts of 3-gram overlaps.

BLEU scores might be undesirable; use SmoothingFunction().

warnings.warn(_msg)

代码清单21.27:示例警告消息

接下来,我们的分数确实非常低。

4.5044474950870215e-156

清单21.28:当一个案例太短时,样本输出BLEU得分。

我鼓励你继续做自己的例子,数学很简单,我也鼓励你阅读论文并探索自己在电子表格中计算句子级别的分数。


0 条 查看最新 评论

没有评论
暂时无法发表评论