本文介绍利用NiuTrans工具进行文白对译的步骤,默认用户已经安装NiuTrans,安装目录为NiuTrans/,以下相对路径基于此目录。
文白对译模型训练步骤分为语料预处理、对齐、翻译模型训练、语言模型训练、参数调整四个阶段。
一、语料预处理
我们拿到的原始数据格式比较杂乱,需要做预处理,最终形成规则的平行语料数据。
语 料预处理包括统一标点符号,删除无关符号,删除段前段后,句前句后空格,分词等阶段。最终形成两个平行文件,暂称为src.txt和tgt.txt两个文 件(若是文白翻译src为文,tgt为白,反之src为白,tgt为文),这两个文件每行一句话(自然句),每句话已经分好词,词语以空格分割开,两个文 件中每行一一对应,因此行数相等。
考虑到文白词语的异意性,句句对齐后的句子分词策略为:白话文为平常的中文分词,文言文为一元分词。
二、对齐
NiuTrans采用第三方工具giza++进行词汇对齐,由于giza++只能进行单向对齐,为了保证对齐结果的准确性,NiuTrans合并了两个方向上的对齐结果,生成了最终的对齐结果供后续步骤使用。
具 体来说,若src==》tgt对齐,giza++会生成一个名为src2tgt.A3.final的结果文件;若tgt==》src对齐,giza++会 生成一个名为tgt2src.A3.final的结果文件;NiuTrans对这两个方向的A3文件进行合并,生成最终的对齐文件 alignment.txt。
以上的对齐步骤可参看脚本代码scripts/NiuTrans-running-GIZA++.pl。
需要特别注意的是,giza++ 进行单向对齐时,如果源/目标句对的长度(分词后词汇个数)相差太大,giza++会对长的一方进行剪切,而在另一个方向目标/源对齐时,并不一定进行剪 切。这样就造成了在两个对齐结果文件src2tgt.A3.final和tgt2src.A3.final的不一致。这个不一致将会造成NiuTrans 在合并生成alignment.txt时发生错误直接退出。这种错误只跟源/目标的长度比有关,与具体哪种语言无关。因此本文题目虽然是《文白对译步骤》,其实是所有语言通用的步骤。因此,直接运行脚本scripts/NiuTrans-running-GIZA++.pl,在第八步之后会出现错误退出,导致训练失败。
解决方法为:在第七步之后,第八步之前,遍历src2tgt.A3.final和tgt2src.A3.final两个文件,将不一致的句对从两个A3文件中剔除,同时也从两个平行语料文件中剔除。
遍历算法很简单,首先看下A3文件的格式:
# Sentence pair (20) source length 11 target length 13 alignment score : 1.14927e-38 智果 便 向 太史 请求 脱离 智族 姓氏 , 另 立 为辅 氏 NULL ({ 2 }) 智 ({ }) 果 ({ 1 5 6 7 8 }) 别 ({ 10 }) 族 ({ }) 于 ({ 3 }) 太 ({ 4 }) 史 ({ }) , ({ 9 }) 为 ({ }) 辅 ({ 11 12 }) 氏 ({ 13 })
上面摘录的是一个句子对的对齐结果。第一行为句对描述,第二行为目标句子内容,第三行为对齐结果。【Sentence pair (20)】表示为第20个句对,source length 11表示源句长度为11,target length 13表示目标句子长度为13。遍历两个A3文件中的每个pair(三行),检查若第一个文件中的source length与第二个文件中的target length相等,且第一个文件中的target length和第二个文件中的source length 相等,说明该句对是一致的,否则不一致,把该句对从两个A3文件和平行文件中剔除。
处理代码粘贴如下:
# -*- coding: utf-8 -*- ''' Created on 2014年8月25日 @author: wuseguang ''' import sys import re print "脚本名:", sys.argv[0] if(len(sys.argv)!=5): print "参数不对" sys.exit() src=sys.argv[1] tgt=sys.argv[2] srcs=sys.argv[3] tgts=sys.argv[4] errPairs=[] with open(src,'r') as srcFile,open(tgt,'r')as tgtFile,open(src+'.check','w')as srcw,open(tgt+'.check','w')as tgtw: index=-1 pair=0 flag=True while True: index+=1 print index srcLine=srcFile.readline() tgtLine=tgtFile.readline() if not srcLine or not tgtLine: break if index%3!=0 or not srcLine.startswith("# Sentence pair"): if flag: srcw.write(srcLine) tgtw.write(tgtLine) continue srcData=re.split('D+',srcLine) tgtData=re.split('D+',tgtLine) #print srcData #print tgtData if srcData[2]!=tgtData[3] or srcData[3]!=tgtData[2]: errPairs.append(index/3+1) flag=False continue pair+=1 flag=True oldtitle="# Sentence pair ("+srcData[1]+")" newtitle='# Sentence pair ('+str(pair)+')' print oldtitle print newtitle srcw.write(srcLine.replace(oldtitle,newtitle)) tgtw.write(tgtLine.replace(oldtitle,newtitle)) with open(srcs,'r') as srcsfile,open(tgts,'r') as tgtsfile,open(srcs+'.check','w')as srcw,open(tgts+'.check','w')as tgtw: pair=0 errSet=set(errPairs) #print srcs+' ' #print tgts+' ' while True: pair+=1 #print 'pair:',pair srcline=srcsfile.readline() tgtline=tgtsfile.readline() if not srcline or not tgtline: break if pair in errSet: continue srcw.write(srcline) tgtw.write(tgtline) print errPairs print len(errPairs)
请将该文件放置于scripts目录下。后续脚本会自助调用。
然后,注释掉scripts/NiuTrans-running-GIZA++.pl脚本中的第八步的代码。在调用scripts/NiuTrans-running-GIZA++.pl脚本之后,调用check.py进行一致性检查,最后调用NiuTrans的合并对齐命令../bin/NiuTrans.SymAlignment。
三、翻译模型训练
在上一步对齐的结果alignment.txt上,进行翻译模型的训练,训练命令为:
perl NiuTrans-phrase-train-model.pl -tmdir $workDir /model .phrase/ -s $srcFile -t $tgtFile -a $aligFile |
-s指向源平行语料文件,-t指向目标平行语料文件,-a指向alignment.txt文件。
四、语言模型训练
语言模型检查目标语言的合法度,因此训练语料只需用目标语言语料即可,格式跟平行语料格式一样,即每行一句,没句以空格分词。训练命令如下:
perl NiuTrans-training-ngram-LM.pl -corpus $lmodelFile -ngram 3 -vocab $workDir /lm/lm .vocab -lmbin $workDir /lm/lm .trie.data |
lmodelFile即为训练语料文件,惯例以lm.txt命名。
五、参数调整
参数调整阶段,对上面训练的两个模型(翻译模型和语言模型)进行权重的调整,其实质是把这两个模型作为两个feature,然后套如特征模型。
训练命令如下:
perl NiuTrans-phrase-generate-mert-config.pl -tmdir $workDir /model .phrase/ -lmdir $workDir /lm/ -ngram 3 -o $workDir /NiuTrans .phrase.user.config |
六、总流程小结
为了操作方便,以上的所有流程我写成到了一个总脚本中,名为train.sh放置在script目录下。内容如下:
1 #!/bin/sh 2 scriptDir=$(realpath $PWD) 3 workDir=$(realpath $1) 4 srcFile=${workDir}/preprocessing/$2 5 tgtFile=${workDir}/preprocessing/$3 6 lmodelFile=${workDir}/preprocessing/$4 7 aligFile=$workDir/wordalignment/alignment.txt 8 src2tgtA3File=$workDir/wordalignment/src2tgt.A3.final 9 tgt2srcA3File=$workDir/wordalignment/tgt2src.A3.final 10 echo "script_dir is ${scriptDir}" 11 echo "work_dir is $workDir" 12 echo "src_file is ${srcFile}" 13 echo "tgt_file is ${tgtFile}" 14 echo "alignFile is $aligFile" 15 #exit 16 mkdir $workDir/wordalignment -p 17 mkdir $workDir/lm -p 18 mkdir $workDir/model.phrase -p 19 #exit 20 cd $scriptDir 21 perl NiuTrans-running-GIZA++.pl -src $srcFile -tgt $tgtFile -out $aligFile -tmpdir $workDir/wordalignment/ 22 cd $scriptDir 23 python $scriptDir/check.py $src2tgtA3File $tgt2srcA3File $srcFile $tgtFile 24 src2tgtA3File=${src2tgtA3File}.check 25 tgt2srcA3File=${tgt2srcA3File}.check 26 srcFile=${srcFile}.check 27 tgtFile=${tgtFile}.check 28 cd $scriptDir 29 ../bin/NiuTrans.SymAlignment $tgt2srcA3File $src2tgtA3File $aligFile 30 cd $scriptDir 31 perl NiuTrans-phrase-train-model.pl -tmdir $workDir/model.phrase/ -s $srcFile -t $tgtFile -a $aligFile 32 cd $scriptDir 33 perl NiuTrans-training-ngram-LM.pl -corpus $lmodelFile -ngram 3 -vocab $workDir/lm/lm.vocab -lmbin $workDir/lm/lm.trie.data 34 cd $scriptDir 35 perl NiuTrans-phrase-generate-mert-config.pl -tmdir $workDir/model.phrase/ -lmdir $workDir/lm/ -ngram 3 -o $workDir/NiuTrans.phrase.user.config
脚本的内容已经非常清晰,不再详述。
运行此脚本的前提为:1、NiuTrans-running-GIZA++.pl脚本中第八步已经被注释;2、check.py已经放置在scripts文件夹下。
运行示例:(wenyan.txt baihua.txt位于../work/preprocessing/目录)
. /train .sh .. /work/ wenyan.txt baihua.txt lm2.txt |
七、测试
在模型训练完毕后,即可进行测试。首先需要具备测试文件test.txt,测试文件格式与平行文件格式一样,与训练语料保持无交集即可。测试命令如下:
perl NiuTrans-phrase-decoder-model.pl - test $workDir /test/test .txt -c $workDir /NiuTrans .phrase.user.config -output $workDir /test/Xbest .out |
-test指明测试文件位置,-c指明上一步训练的模型配置文件位置,-output 指明翻译结果文件位置。
注意,若需要指明多个翻译结果,需要修改脚本NiuTrans-phrase-decoder-model.pl第56行的-nbest参数,默认为1。