bleu全称为Bilingual Evaluation Understudy(双语评估替换),是2002年提出的用于评估机器翻译效果的一种方法,这种方法简单朴素、短平快、易于理解。因为其效果还算说得过去,因此被广泛迁移到自然语言处理的各种评估任务中。这种方法可以说是:山上无老虎,猴子称大王。时无英雄遂使竖子成名。蜀中无大将,廖化做先锋。

问题描述

首先,对bleu算法建立一个直观的印象。

有两类问题:

1、给定一个句子和一个候选句子集,求bleu值,此问题称为sentence_bleu

2、给定一堆句子和一堆候选句子集,求bleu值,此问题称为corpus_bleu

机器翻译得到的句子称为candidate,候选句子集称为references。

计算方式就是计算candidate和references的公共部分。公共部分越多,说明翻译结果越好。

给定一个句子和一个候选句子集计算bleu值

bleu考虑1,2,3,4共4个n-gram,可以给每个n-gram指定权重。

对于n-gram:

  • 对candidate和references分别分词(n-gram分词)
  • 统计candidate和references中每个word的出现频次
  • 对于candidate中的每个word,它的出现频次不能大于references中最大出现频次

    这一步是为了整治形如the the the the the这样的candidate,因为the在candidate中出现次数太多了,导致分值为1。为了限制这种不正常的candidate,使用正常的references加以约束。
  • candidate中每个word的出现频次之和除以总的word数,即为得分score
  • score乘以句子长度惩罚因子即为最终的bleu分数

    这一步是为了整治短句子,比如candidate只有一个词:the,并且the在references中出现过,这就导致得分为1。也就是说,有些人因为怕说错而保持沉默。

bleu的发展不是一蹴而就的,很多人为了修正bleu,不断发现bleu的漏洞并提出解决方案。从bleu的发展历程上,我们可以学到如何设计规则整治badcase。

最后,对于1-gram,2-gram,3-gram的组合,应该采用几何平均,也就是s1^w1*s2^2*s3^w3,而不是算术平均w1*s1+w2*s2+w3*s3

from collections import Counter

import numpy as np
from nltk.translate import bleu_score def bp(references, candidate):
# brevity penality,句子长度惩罚因子
ind = np.argmin([abs(len(i) - len(candidate)) for i in references])
if len(references[ind]) < len(candidate):
return 1
scale = 1 - (len(candidate) / len(references[ind]))
return np.e ** scale def parse_ngram(sentence, gram):
# 把一个句子分成n-gram
return [sentence[i:i + gram] for i in range(len(sentence) - gram + 1)] # 此处一定要注意+1,否则会少一个gram def sentence_bleu(references, candidate, weight):
bp_value = bp(references, candidate)
s = 1
for gram, wei in enumerate(weight):
gram = gram + 1
# 拆分n-gram
ref = [parse_ngram(i, gram) for i in references]
can = parse_ngram(candidate, gram)
# 统计n-gram出现次数
ref_counter = [Counter(i) for i in ref]
can_counter = Counter(can)
# 统计每个词在references中的出现次数
appear = sum(min(cnt, max(i.get(word, 0) for i in ref_counter)) for word, cnt in can_counter.items())
score = appear / len(can)
# 每个score的权值不一样
s *= score ** wei
s *= bp_value # 最后的分数需要乘以惩罚因子
return s references = [
"the dog jumps high",
"the cat runs fast",
"dog and cats are good friends"
]
candidate = "the d o g jump s hig"
weights = [0.25, 0.25, 0.25, 0.25]
print(sentence_bleu(references, candidate, weights))
print(bleu_score.sentence_bleu(references, candidate, weights))

一个corpus是由多个sentence组成的,计算corpus_bleu并非求sentence_bleu的均值,而是一种略微复杂的计算方式,可以说是没什么道理的狂想曲。

corpus_bleu

一个文档包含3个句子,句子的分值分别为a1/b1,a2/b2,a3/b3。

那么全部句子的分值为:(a1+a2+a3)/(b1+b2+b3)

惩罚因子也是一样:三个句子的长度分别为l1,l2,l3,对应的最接近的reference分别为k1,k2,k3。那么相当于bp(l1+l2+l3,k1+k2+k3)。

也就是说:对于corpus_bleu不是单纯地对sentence_bleu求均值,而是基于更统一的一种方法。

from collections import Counter

import numpy as np
from nltk.translate import bleu_score def bp(references_len, candidate_len):
if references_len < candidate_len:
return 1
scale = 1 - (candidate_len / references_len)
return np.e ** scale def parse_ngram(sentence, gram):
return [sentence[i:i + gram] for i in range(len(sentence) - gram + 1)] def corpus_bleu(references_list, candidate_list, weights):
candidate_len = sum(len(i) for i in candidate_list)
reference_len = 0
for candidate, references in zip(candidate_list, references_list):
ind = np.argmin([abs(len(i) - len(candidate)) for i in references])
reference_len += len(references[ind])
s = 1
for index, wei in enumerate(weights):
up = 0 # 分子
down = 0 # 分母
gram = index + 1
for candidate, references in zip(candidate_list, references_list):
# 拆分n-gram
ref = [parse_ngram(i, gram) for i in references]
can = parse_ngram(candidate, gram)
# 统计n-gram出现次数
ref_counter = [Counter(i) for i in ref]
can_counter = Counter(can)
# 统计每个词在references中的出现次数
appear = sum(min(cnt, max(i.get(word, 0) for i in ref_counter)) for word, cnt in can_counter.items())
up += appear
down += len(can)
s *= (up / down) ** wei
return bp(reference_len, candidate_len) * s references = [
[
"the dog jumps high",
"the cat runs fast",
"dog and cats are good friends"],
[
"ba ga ya",
"lu ha a df",
]
]
candidate = ["the d o g jump s hig", 'it is too bad']
weights = [0.25, 0.25, 0.25, 0.25]
print(corpus_bleu(references, candidate, weights))
print(bleu_score.corpus_bleu(references, candidate, weights))

如果你用的NLTK版本是3.2,发布时间是2016年3月份,那么计算corpus_bleu时有一处bug。NLTK在2016年10月份已经修复了此处bug。对于句子分值的求和,NLTK代码中是使用Fraction,Fraction会自动对分子和分母进行化简,导致求和的时候计算错误。

简化代码

在计算sentence_bleu和corpus_bleu过程中,许多步骤都是相似的、可以合并的。精简后的代码如下:

from collections import Counter

import numpy as np
from nltk.translate import bleu_score def bp(references_len, candidate_len):
return np.e ** (1 - (candidate_len / references_len)) if references_len > candidate_len else 1 def nearest_len(references, candidate):
return len(references[np.argmin([abs(len(i) - len(candidate)) for i in references])]) def parse_ngram(sentence, gram):
return [sentence[i:i + gram] for i in range(len(sentence) - gram + 1)] def appear_count(references, candidate, gram):
ref = [parse_ngram(i, gram) for i in references]
can = parse_ngram(candidate, gram)
# 统计n-gram出现次数
ref_counter = [Counter(i) for i in ref]
can_counter = Counter(can)
# 统计每个词在references中的出现次数
appear = sum(min(cnt, max(i.get(word, 0) for i in ref_counter)) for word, cnt in can_counter.items())
return appear, len(can) def corpus_bleu(references_list, candidate_list, weights):
candidate_len = sum(len(i) for i in candidate_list)
reference_len = sum(nearest_len(references, candidate) for candidate, references in zip(candidate_list, references_list))
bp_value = bp(reference_len, candidate_len)
s = 1
for index, wei in enumerate(weights):
up = 0 # 分子
down = 0 # 分母
gram = index + 1
for candidate, references in zip(candidate_list, references_list):
appear, total = appear_count(references, candidate, gram)
up += appear
down += total
s *= (up / down) ** wei
return bp_value * s def sentence_bleu(references, candidate, weight):
bp_value = bp(nearest_len(references, candidate), len(candidate))
s = 1
for gram, wei in enumerate(weight):
gram = gram + 1
appear, total = appear_count(references, candidate, gram)
score = appear / total
# 每个score的权值不一样
s *= score ** wei
# 最后的分数需要乘以惩罚因子
return s * bp_value if __name__ == '__main__':
references = [
[
"the dog jumps high",
"the cat runs fast",
"dog and cats are good friends"],
[
"ba ga ya",
"lu ha a df",
]
]
candidate = ["the d o g jump s hig", 'it is too bad']
weights = [0.25, 0.25, 0.25, 0.25]
print(corpus_bleu(references, candidate, weights))
print(bleu_score.corpus_bleu(references, candidate, weights))
print(sentence_bleu(references[0], candidate[0], weights))
print(bleu_score.sentence_bleu(references[0], candidate[0], weights))

参考资料

https://cloud.tencent.com/developer/article/1042161

https://en.wikipedia.org/wiki/BLEU

https://blog.csdn.net/qq_31584157/article/details/77709454

https://www.jianshu.com/p/15c22fadcba5

理解bleu的更多相关文章

  1. 关于机器翻译评价指标BLEU(bilingual evaluation understudy)的直觉以及个人理解

    最近我在做Natural Language Generating的项目,接触到了BLEU这个指标,虽然知道它衡量的是机器翻译的效果,也在一些文献的experiment的部分看到过该指标,但我实际上经常 ...

  2. 机器翻译评测——BLEU算法详解

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/7679284.html 前言 近年来,在自然语言研究领域中, ...

  3. 机器翻译评价指标 — BLEU算法

    1,概述 机器翻译中常用的自动评价指标是 $BLEU$ 算法,除了在机器翻译中的应用,在其他的 $seq2seq$ 任务中也会使用,例如对话系统. 2 $BLEU$算法详解 假定人工给出的译文为$re ...

  4. 利用BLEU进行机器翻译检测(Python-NLTK-BLEU评分方法)

    双语评估替换分数(简称BLEU)是一种对生成语句进行评估的指标.完美匹配的得分为1.0,而完全不匹配则得分为0.0.这种评分标准是为了评估自动机器翻译系统的预测结果而开发的,具备了以下一些优点: 计算 ...

  5. Deep Learning基础--机器翻译BLEU与Perplexity详解

    前言 近年来,在自然语言研究领域中,评测问题越来越受到广泛的重视,可以说,评测是整个自然语言领域最核心和关键的部分.而机器翻译评价对于机器翻译的研究和发展具有重要意义:机器翻译系统的开发者可以通过评测 ...

  6. 阅读关于DuReader:百度大规模的中文机器阅读理解数据集

    很久之前就得到了百度机器阅读理解关于数据集的这篇文章,今天才进行总结!.... 论文地址:https://arxiv.org/abs/1711.05073 自然语言处理是人工智能皇冠上的明珠,而机器阅 ...

  7. 对于文本生成类4种评价指标的的计算BLEU METEOR ROUGE CIDEr

    github下载链接:https://github.com/Maluuba/nlg-eval 将下载的文件放到工程目录,而后使用如下代码计算结果 具体的写作格式如下: from nlgeval imp ...

  8. 机器阅读理解综述Neural Machine Reading Comprehension Methods and Trends(略读笔记)

    标题:Neural Machine Reading Comprehension: Methods and Trends 作者:Shanshan Liu, Xin Zhang, Sheng Zhang, ...

  9. 理解CSS视觉格式化

    前面的话   CSS视觉格式化这个词可能比较陌生,但说起盒模型可能就恍然大悟了.实际上,盒模型只是CSS视觉格式化的一部分.视觉格式化分为块级和行内两种处理方式.理解视觉格式化,可以确定得到的效果是应 ...

随机推荐

  1. 弹出层框架layer快速使用

    layer官方及演示文档:layer官方及演示文档 1.将layer整个放入工程内. 2.文件内引入layer.js, <script type="text/javascript&qu ...

  2. Java-JUC(十):线程按序交替执行

    问题: 有a.b.c三个线程,使得它们按照abc依次执行10次. 实现: package com.dx.juc.test; import java.util.concurrent.locks.Cond ...

  3. 如何对 GIT 分支进行规划?

    项目背景: 该项目是在2011年11月份使用Asp.net三层帮荷兰某个客户开发的机票预定系统 该客户主要是做中国与欧洲的旅行社业务,特别是最近两年由于中国的发展因此客户也越来越重视机票业务 于是他们 ...

  4. jquery easyui tree异步加载子节点

    easyui中的树可以从标记中建立,也可以通过指定一个URL属性读取数据建立.如果想建立一棵异步树,需要为每个节点指定一个id属性值,这样在加载数据时会自动向后台传递id参数. <ul id=& ...

  5. 看见上帝的 10 个公式……

    原文 Top Ten Greatest Equations Ever 本文内容 No.1 麦克斯韦方程组 No.2 欧拉方程 No.3 牛顿第二定律 No.4 毕达哥拉斯定理 No.5 薛定谔方程 N ...

  6. 如何在onCreate方法中获取视图的宽度和高度

    你可以通过视图的getWidth()和getHeight()来获取视图的宽度和高度. 但是,可能会让你失望的是,如果你直接在onCreate方法内调用这两个函数,你会的到0. 为什么呢? 这是因为,当 ...

  7. js 时间加减

    //js格式化时间 "yyyy-MM-dd hh:mm:ss" Date.prototype.Format = function (fmt) { var o = { "M ...

  8. python 不需要函数重载

    函数重载主要是为了解决两个问题. 可变参数类型. 可变参数个数. 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其 ...

  9. 通过实例看懂diff命令输出

    摘自:http://blog.sina.com.cn/s/blog_612144f30100nkpt.html ############################### 实例: 有这样两个文件: ...

  10. 一个JAVA的WEB服务器事例

    其实编写一个入门级别的JAVA的WEB服务器,很简单,用SOCKET类即可实现.相关内容可以参考:http://www.cnblogs.com/liqiu/p/3253022.html 一.首先创建一 ...