BLEU计算

1. BLEU计算

评价机器翻译结果通常使⽤BLEU来评价,对于模型预测序列中任意的⼦序列,BLEU考察这个⼦序列是否出现在标签序列中。

具体来说,设词数为 n n 的⼦序列的精度为 p n p_n ,他是预测序列_与_标签序列_匹配词数为 n n 的子序列的数量_与_预测序列中词数为 n n 的_子序列的数量之比

举个例子,假设标签序列为A, B, C, D, E, F,预测序列为A, B, B, C, D,那么 p 1 = 4 / 5 p_1=4/5 , p 2 = 3 / 4 p_2=3/4 , p 3 = 1 / 3 , p 4 = 0 p_3=1/3, p_4=0 。 另 l e n l a b e l len_{label} l e n p r e d len_{pred} 分别为标签序列和预测序列的词数,那么BLEU的定义为:
B L E U = exp ( min ( 0, 1 l e n label  len pred  ) ) n = 1 k p n 1/ 2 n BLEU=\exp \left( \min \left( \text{0,}1-\frac{len_{\text{label }}}{\text{len}_{\text{pred }}} \right) \right) \prod_{n=1}^k{p_{n}^{\text{1/}2^n}}

其中 k k 是我们希望匹配的子序列的最大词数(或者说是n-gram中的n),可以发现当预测序列和标签序列完全一致时BLEU值为1。

因为匹配较⻓⼦序列⽐匹配较短⼦序列更难,BLEU对匹配较⻓⼦序列的精度赋予了更⼤权重。例如,当 p n p_n 固定为0.5时, 随着n增大, 0. 5 1 / 2 0.7 , 0. 5 1 / 4 0.84 , 0. 5 1 / 8 0.92 , 0. 5 1 / 16 0.96 0.5^{1/2}\approx0.7,0.5^{1/4}\approx0.84,0.5^{1/8}\approx0.92,0.5^{1/16}\approx0.96

另外,模型预测较短序列往往会得到较⾼ p n p_n 值。因此,上式中连乘项前⾯的系数是为了惩罚较短的输出⽽设置的。例如,当 k = 2 k=2 时,假设标签序列为A, B, C, D, E, F,而预测序列为A, B。虽然 p 1 = p 2 = 1 p_1 = p_2 = 1 ,但是惩罚系数 exp ( 1 6 / 2 ) 0.14 \exp(1-6/2)\approx0.14 , 因此BLEU结果接近0.14

2. 注意

BLEU计算公式很多地方会给出如下形式:
B L E U = B P exp ( n = 1 N w n log p n ) BLEU=BP\exp \left( \sum_{n=1}^N{w_n}\log p_n \right) 其中 BP = { 1    if  c > r e ( 1 r / c )    if  c r \text{BP}=\left\{ \begin{matrix} 1& \,\,\text{if }c>r\\ e^{\left( 1-r/c \right)}& \,\,\text{if }c\le r\\ \end{matrix} \right. c c 是预测序列的长度, r r 是标签序列的长度

该公式和上面的公式其实是一样的,此处的 B P BP 就相当于上面的 e x p ( min ( 0, 1 l e n label     len pred    ) ) exp\left( \min \left( \text{0,}1-\frac{len_{\text{label }}}{\,\,\text{len}_{\text{pred}}\,\,} \right) \right) ,剩下部分推导如下:
exp ( n = 1 N w n ln p n ) = n = 1 N exp ( ω n ln p n ) = n = 1 N exp ( ln p n w n ) = n = 1 N p n w n \exp \left( \sum_{n=1}^N{w_n}\ln p_n \right) =\prod_{n=1}^N{\exp \left( \omega _n\ln p_n \right)}=\prod_{n=1}^N{\exp \left( \ln p_{n}^{w_n} \right)}=\prod_{n=1}^N{p_{n}^{w_n}} 其中 ω n = 1 2 n \omega _n=\frac{1}{2^n}

BLEU的取值范围是[0,1],0最差,1最好;在第1节公式中 k k 一般取4左右,一般不大于4

3.BLEU编程实现

def bleu(pred_tokens, label_tokens, k):
	len_pred, len_label = len(pred_tokens), len(label_tokens)
	score = math.exp(min(0, 1-len_label/len_pred))
	for n in range(1, k+1):
		num_matches, label_subs = 0, collections.defaultdict(int)
		for i in range(len_label-n+1):
			label_subs[" ".join(label_tokens[i:i+n])] += 1
		for i in range(len_pred-n+1):
			if label_subs[" ".join(pred_tokens[i:i+n])] > 0:
				num_matches += 1
				label_subs[" ".join(pred_tokens[i:i+n])] -= 1
		score *= math.pow(num_matches/(len_pred-n+1), math.pow(0.5, n))
	return score

if __name__ == '__main__':
	import math
	import collections
	score = bleu(['The', 'cat', 'sat', 'on', 'the', 'mat'], ['The', 'cat', 'is', 'on', 'the', 'mat'], 3)
	print(score)
输出:
0.6756000774035172
[Finished in 0.1s]
注意k值不能取太大
发布了39 篇原创文章 · 获赞 2 · 访问量 3698

猜你喜欢

转载自blog.csdn.net/orangerfun/article/details/105293821