◆版權聲明:本文出自胖喵~的博客,轉載必須註明出處。html
轉載請註明出處:http://www.cnblogs.com/by-dream/p/7679284.html python
近年來,在天然語言研究領域中,評測問題愈來愈受到普遍的重視,能夠說,評測是整個天然語言領域最核心和關鍵的部分。而機器翻譯評價對於機器翻譯的研究和發展具備重要意義:機器翻譯系統的開發者能夠經過評測得知系統存在的問題而不斷改進,用戶也能夠根據評測報告選擇知足本身需求的產品,而對於機器翻譯的研究人員來講,評測可以給他們的技術發展方向提供最可靠的依據。算法
——摘自北京郵電大學信息工程系張劍博士在微軟亞洲研究院訪問期間完成的一篇論文中的一段話。 json
早在90年代初,美國國家天然基金委員會和歐盟就資助的國際語言工程標準(ISLE)計劃就專門設立了EWG(Evaluation Working Group)機器翻譯評測工做組。1992年至1994年之間,美國國防部高級研究計劃署(DARPA)專門組織一批專家從翻譯譯文的忠實度、流利度和信息量三個角度對當時的法英、日英、西英的機器翻譯系統進行了大規模的評測。目前比較流行的自動評測方法是是IBM提出的BLEU算法,BLEU(bilingual evaluation understudy),簡單來講,BLEU算法的思想就是機器翻譯的譯文越接近人工翻譯的結果,它的翻譯質量就越高。因此評測算法就是如何定義機器翻譯譯文與參考譯文之間的類似度。app
咱們看下BLEU算法具體的細節吧:ide
BLEU 採用一種N-gram的匹配規則,原理比較簡單,就是比較譯文和參考譯文之間n組詞的類似的一個佔比。oop
例如:fetch
原文:今每天氣不錯ui
機器譯文:It is a nice day todaythis
人工譯文:Today is a nice day
若是用1-gram匹配的話:
能夠看到機器譯文一共6個詞,有5個詞語都命中的了參考譯文,那麼它1-gram的匹配度爲 5/6
咱們再以3-gram舉例:
能夠看到機器譯文一共能夠分爲四個3-gram的詞組,其中有兩個能夠命中參考譯文,那麼它3-gram的匹配度爲 2/4
依次類推,咱們能夠很容易實現一個程序來遍歷計算N-gram的一個匹配度。通常來講1-gram的結果表明了文中有多少個詞被單獨翻譯出來了,所以它反映的是這篇譯文的忠實度;而當咱們計算2-gram以上時,更多時候結果反映的是譯文的流暢度,值越高文章的可讀性就越好。
上面所說的方法比較好理解,也比較好實現,可是沒有考慮到召回率,舉一個很是簡單的例子說明:
原文:貓站在地上
機器譯文:the the the the
人工譯文:The cat is standing on the ground
在計算1-gram的時候,the 都出如今譯文中,所以匹配度爲4/4 ,可是很明顯 the 在人工譯文中最多出現的次數只有2次,所以BLEU算法修正了這個值的算法,首先會計算該n-gram在譯文中可能出現的最大次數:
Count是N-gram在機器翻譯譯文中的出現次數,Max_Ref_Count是該N-gram在一個參考譯文中最大的出現次數,最終統計結果取二者中的較小值。而後在把這個匹配結果除以機器翻譯譯文的N-gram個數。所以對於上面的例子來講,修正後的1-gram的統計結果就是2/4。
咱們將整個要處理的將機器翻譯的句子表示爲Ci,標準答案表示爲 Si=si1,...sim(m表示有m個參考答案)
n-grams表示n個單詞長度的詞組集合,令Wk第k個n-gram
好比這樣的一句話,」I come from china」,第1個2-gram爲:I come; 第2個2-gram爲:come from; 第3個2-gram爲:from china;
Hk(Ci) 表示Wk翻譯選譯文Ci中出現的次數
Hk(Sij) 表示Wk在標準答案Sij中出現的次數
綜上所述各階N-gram的精度均可以按照下面這個公式計算:
maxi∈mhk(sij)表示某n-gram在多條標準答案中出現最多的次數
∑i∑kmin(hk(ci),maxj∈mhk(sij))表示取n-gram在翻譯譯文和標準答案中出現的最小次數
上面的算法已經足夠能夠有效的翻譯評估了,然而N-gram的匹配度可能會隨着句子長度的變短而變好,所以會存在這樣一個問題:一個翻譯引擎只翻譯出了句子中部分句子且翻譯的比較準確,那麼它的匹配度依然會很高。爲了不這種評分的偏向性,BLEU在最後的評分結果中引入了長度懲罰因子(Brevity Penalty)。
BP的計算公式如上。lc表明表示機器翻譯譯文的長度,ls表示參考答案的有效長度,當存在多個參考譯文時,選取和翻譯譯文最接近的長度。當翻譯譯文長度大於參考譯文的長度時,懲罰係數爲1,意味着不懲罰,只有機器翻譯譯文長度小於參考答案纔會計算懲罰因子。
因爲各N-gram統計量的精度隨着階數的升高而呈指數形式遞減,因此爲了平衡各階統計量的做用,對其採用幾何平均形式求平均值而後加權,再乘以長度懲罰因子,獲得最後的評價公式:
BLEU的原型系統採用的是均勻加權,即Wn=1/N 。N的上限取值爲4,即最多隻統計4-gram的精度。
譯文(Candidate)
Going to play basketball this afternoon ?
參考答案(Reference)
Going to play basketball in the afternoon ?
譯文gram長度:7 參考答案gram長度:8
先看1-gram,除了this這個單詞沒有命中,其餘都命中了,所以:
P1 = 6/7 = 0.85714...
其餘gram以此類推:
P2 = 4/6 = 0.6666..
P3 = 2/5 = 0.4
P4 = 1/4 = 0.25
再計算logPn,這裏用python自帶的:
∑logPn和爲-2.8622 ;再乘以Wn,也就是除以4爲 0.7156
BP = e^(1-8/7) 約等於 0.867
BLEU = 0.867 * e^((P1 + P2 + P3 + P4)/4) = 0.867*0.4889 = 0.4238
原本打算本身實現一個python的代碼,結果發現已經有國外小哥作了,拿下來稍微修改了點內容,這裏供你們參考
#-*- coding:utf-8 -*- import sys import codecs import os import math import operator import json # 若是是一份答案的話,務必在答案的後面加上.txt python Bleu.py Candidate ref.txt # 若是是多份答案的話,把多份答案放到一個文件夾中 python Bleu.py Candidate 文件夾 def fetch_data(cand, ref): """ Store each reference and candidate sentences as a list """ references = [] if '.txt' in ref: reference_file = codecs.open(ref, 'r', 'utf-8') references.append(reference_file.readlines()) else: for root, dirs, files in os.walk(ref): for f in files: reference_file = codecs.open(os.path.join(root, f), 'r', 'utf-8') references.append(reference_file.readlines()) candidate_file = codecs.open(cand, 'r', 'utf-8') candidate = candidate_file.readlines() return candidate, references def count_ngram(candidate, references, n): clipped_count = 0 count = 0 r = 0 c = 0 for si in range(len(candidate)): # Calculate precision for each sentence #print si ref_counts = [] ref_lengths = [] #print references # Build dictionary of ngram counts for reference in references: #print 'reference' + reference ref_sentence = reference[si] ngram_d = {} words = ref_sentence.strip().split() ref_lengths.append(len(words)) limits = len(words) - n + 1 # loop through the sentance consider the ngram length for i in range(limits): ngram = ' '.join(words[i:i+n]).lower() if ngram in ngram_d.keys(): ngram_d[ngram] += 1 else: ngram_d[ngram] = 1 ref_counts.append(ngram_d) # candidate cand_sentence = candidate[si] cand_dict = {} words = cand_sentence.strip().split() limits = len(words) - n + 1 for i in range(0, limits): ngram = ' '.join(words[i:i + n]).lower() if ngram in cand_dict: cand_dict[ngram] += 1 else: cand_dict[ngram] = 1 clipped_count += clip_count(cand_dict, ref_counts) count += limits r += best_length_match(ref_lengths, len(words)) c += len(words) if clipped_count == 0: pr = 0 else: pr = float(clipped_count) / count bp = brevity_penalty(c, r) return pr, bp def clip_count(cand_d, ref_ds): """Count the clip count for each ngram considering all references""" count = 0 for m in cand_d.keys(): m_w = cand_d[m] m_max = 0 for ref in ref_ds: if m in ref: m_max = max(m_max, ref[m]) m_w = min(m_w, m_max) count += m_w return count def best_length_match(ref_l, cand_l): """Find the closest length of reference to that of candidate""" least_diff = abs(cand_l-ref_l[0]) best = ref_l[0] for ref in ref_l: if abs(cand_l-ref) < least_diff: least_diff = abs(cand_l-ref) best = ref return best def brevity_penalty(c, r): if c > r: bp = 1 else: bp = math.exp(1-(float(r)/c)) return bp def geometric_mean(precisions): return (reduce(operator.mul, precisions)) ** (1.0 / len(precisions)) def BLEU(candidate, references): precisions = [] for i in range(4): pr, bp = count_ngram(candidate, references, i+1) precisions.append(pr) print 'P'+str(i+1), ' = ',round(pr, 2) print 'BP = ',round(bp, 2) bleu = geometric_mean(precisions) * bp return bleu if __name__ == "__main__": candidate, references = fetch_data(sys.argv[1], sys.argv[2]) bleu = BLEU(candidate, references) print 'BLEU = ',round(bleu, 4) out = open('bleu_out.txt', 'w') out.write(str(bleu)) out.close()
由於不少人(非計算機專業)的同窗留言問我BLEU相關的計算,爲了更加方便,我提供了一個在線BLEU計算的頁面,供你們使用。
地址:http://39.105.173.45:8080/debug#/tools/bleu
只需按照提示,上傳譯文,點擊計算便可。
另外針對中文計算BLEU的「分詞」功能,我也提供了出來。
地址:http://39.105.173.45:8080/debug#/tools/segword
昨天熬夜寫到凌晨2點多,終於搞定。
各位以爲還有用的話,歡迎多多推廣。