摘要
fairseq是个常用的机器翻译项目。它的优化很好,但代码晦涩难懂,限制了我们的使用。
翻译数据的准备,是训练的第一步。但 fairseq 关于翻译数据的准备流程散布在零星的 bash 脚本中。本文旨在梳理如下流程:1)准备 WMT23 的数据,2)训练模型,3)用 sacrebleu 评测模型。
训练数据
我们使用 mtdata 这个库来准备我们需要的数据。这个库是 WMT 官方钦定的。
接下来下载数据:
pip install mtdata==0.4.0
wget https://www.statmt.org/wmt23/mtdata/mtdata.recipes.wmt23-constrained.yml
for ri in wmt23-{enzh,zhen,ende,deen,enhe,heen,enja,jaen,enru,ruen,encs,csuk,enuk,uken}; do
mtdata get-recipe -ri $ri -o $ri
done
上面的代码拷贝自 WMT23 网站。
对于每个语言对,比如 zhen(中文到英文),在 wmt23-zhen
下将会有两个文件,应该叫 train.zh
和 train.en
。这两个文件有相同的行数。每一行为一条训练数据。两个文件的每一行一一对应。
好的,训练数据就先到这,在进一步清洗数据之前,我们先准备下测试数据和验证数据。
测试集和验证集
(首先声明,本文准备的数据是训练和评估机器翻译模型,而不是参与 WMT 榜单。)
根据 WMT 官方推荐,应该用历年的测试数据来当做验证集。
To evaluate your system during development, we suggest using test sets from past WMT years...
由于今年的测试数据是没有标签的。所以,我们选择用一部分往年的测试数据当验证集,用另一部分的往年的测试数据当测试集。
那么,同样用 mtdata 来下载数据:
cd wmt23-zhen
# 验证集
mtdata get -l zho-eng --out test/ --merge --dev Statmt-newstest_zhen-20{18,19,20}-zho-eng
# 测试集
mtdata get -l zho-eng --out test/ --merge --test Statmt-newstest_zhen-2021-zho-eng
可以看到,我们使用18-20年的数据当做验证集,21年的数据当做测试集。如果好奇有多少年的数据,可以使用这个命令查看:mtdata list -l zho-eng | cut -f1
。
验证集和测试集都被保存在了 test/
文件夹中,但各自的名字不同。两者都有两个文件。其中,验证集的名字是 dev.zho
以及 dev.eng
,后缀是语言的三字母缩写。测试集以此类推。
为了后续的处理,请重命名文件,将后缀改成语言的二字母缩写。比如将 zho 修改为 zh。
预处理
这一步包括清洗训练数据,学习 bpe tokenizer,tokenize 数据。请放心,我们将用一个 bash 文件搞定。
在开始之前,请确保你的文件结构是这样的:
--orig
--train.tgt
--train.src
--test
--test.tgt
--test.src
--dev.tgt
--dev.src
其中 orig
表示你存放数据的文件夹,或许是叫做 wmt23-zhen
,其他的也可以。
准备工作完成,请将下述的代码保存在一个 sh 文件中。然后,再回来看后续内容:
#!/bin/bash
# Adapted from https://github.com/facebookresearch/MIXER/blob/master/prepareData.sh
echo 'Cloning Moses github repository (for tokenization scripts)...'
git clone https://github.com/moses-smt/mosesdecoder.git
echo 'Cloning Subword NMT repository (for BPE pre-processing)...'
git clone https://github.com/rsennrich/subword-nmt.git
SCRIPTS=mosesdecoder/scripts
TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl
CLEAN=$SCRIPTS/training/clean-corpus-n.perl
NORM_PUNC=$SCRIPTS/tokenizer/normalize-punctuation.perl
REM_NON_PRINT_CHAR=$SCRIPTS/tokenizer/remove-non-printing-char.perl
BPEROOT=subword-nmt/subword_nmt
BPE_TOKENS=40000
if [ ! -d "$SCRIPTS" ]; then
echo "Please set SCRIPTS variable correctly to point to Moses scripts."
exit
fi
############################################################
src=zh
tgt=en
lang=zh-en
OUTDIR=wmt23-zhen
prep=$OUTDIR
tmp=$prep/tmp
TRAIN=$tmp/train.zh-en
CORPORA=(
"train"
)
orig=/data/yuanhang/mtdata/wmt23-zhen
mkdir -p $tmp $prep
############################################################
echo "pre-processing train data..."
for l in $src $tgt; do
rm $tmp/train.$l
for f in "${CORPORA[@]}"; do
cat $orig/$f.$l | \
perl $NORM_PUNC $l | \
perl $REM_NON_PRINT_CHAR | \
perl $TOKENIZER -threads 8 -a -l $l >> $tmp/train.$l
done
done
############################################################
echo "pre-processing test and dev data..."
for l in $src $tgt; do
cat $orig/test/dev.$l | \
sed -e "s/\’/\'/g" | \
perl $TOKENIZER -threads 8 -a -l $l > $tmp/valid.$l
echo ""
cat $orig/test/test.$l | \
sed -e "s/\’/\'/g" | \
perl $TOKENIZER -threads 8 -a -l $l > $tmp/test.$l
echo ""
done
############################################################
BPE_CODE=$prep/code
rm -f $TRAIN
for l in $src $tgt; do
cat $tmp/train.$l >> $TRAIN
done
echo "learn_bpe.py on ${TRAIN}..."
python $BPEROOT/learn_bpe.py -s $BPE_TOKENS < $TRAIN > $BPE_CODE
for L in $src $tgt; do
for f in train.$L valid.$L test.$L; do
echo "apply_bpe.py to ${f}..."
python $BPEROOT/apply_bpe.py -c $BPE_CODE < $tmp/$f > $tmp/bpe.$f
done
done
perl $CLEAN -ratio 1.5 $tmp/bpe.train $src $tgt $prep/train 1 250
for L in $src $tgt; do
cp $tmp/bpe.test.$L $prep/test.$L
cp $tmp/bpe.valid.$L $prep/valid.$L
done
您所需要修改的地方很少,仅为下面这些地方,用来制定下语言 src, tgt, lang
、输出的文件夹 OUTDIR
,要处理的文件所在路径 orig
,和 CORPORA
指定训练数据的前缀。你或许会注意到,我们可以指定多个训练数据。他们会在处理中被合并。
############################################################
src=zh
tgt=en
lang=zh-en
OUTDIR=final-wmt23-zhen
prep=$OUTDIR
tmp=$prep/tmp
TRAIN=$tmp/train.zh-en
CORPORA=(
"train"
)
orig=/data/yuanhang/mtdata/wmt23-zhen
mkdir -p $tmp $prep
############################################################