利用Fairseq训练新的机器翻译模型
利用Fairseq训练一个新的机器翻译模型,官方机器翻译(German-English)示例:Fairseq-Training a New Model。
数据预处理
进入fairseq/examples/translation
目录下,执行sh prepare-iwslt14.sh
。prepare-iwslt14.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
...
URL="https://wit3.fbk.eu/archive/2014-01/texts/de/en/de-en.tgz"
GZ=de-en.tgz
...
wget "$URL"
主要下载3个数据,分别是英德双语语料,以及mosesdecoder
和subword_nmt
两个工具库。
-
mosesdecoder
是机器翻译中常用的工具,里面包含了很多有用的脚本。 -
subword_nmt
根据训练数据建立subword词表,以及对训练集、测试集、验证集切分成subword的形式。
数据清洗
SCRIPTS=mosesdecoder/scripts
TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl
LC=$SCRIPTS/tokenizer/lowercase.perl
CLEAN=$SCRIPTS/training/clean-corpus-n.perl
...
src=de
tgt=en
lang=de-en
prep=iwslt14.tokenized.de-en
tmp=$prep/tmp
...
echo "pre-processing train data..."
for l in $src $tgt; do
f=train.tags.$lang.$l
tok=train.tags.$lang.tok.$l
cat $orig/$lang/$f | \
grep -v '<url>' | \
grep -v '<talkid>' | \
grep -v '<keywords>' | \
sed -e 's/<title>//g' | \
sed -e 's/<\/title>//g' | \
sed -e 's/<description>//g' | \
sed -e 's/<\/description>//g' | \
perl $TOKENIZER -threads 8 -l $l > $tmp/$tok
echo ""
done
perl $CLEAN -ratio 1.5 $tmp/train.tags.$lang.tok $src $tgt $tmp/train.tags.$lang.clean 1 175
for l in $src $tgt; do
perl $LC < $tmp/train.tags.$lang.clean.$l > $tmp/train.tags.$lang.$l
done
在该任务中,使用sed
清除HTML标签。mosesdecoder
的scripts/tokenizer/tokenizer.perl
对句子进行分词。scripts/training/clean-corpus-n.perl
清理训练集中过长的句子,以及一些src和tgt的长度比过大的句子。scripts/tokenizer/lowercase.perl
将所有文本转化为小写。
切分训练集、验证集和测试集
echo "creating train, valid, test..."
for l in $src $tgt; do
awk '{if (NR%23 == 0) print $0; }' $tmp/train.tags.de-en.$l > $tmp/valid.$l
awk '{if (NR%23 != 0) print $0; }' $tmp/train.tags.de-en.$l > $tmp/train.$l
cat $tmp/IWSLT14.TED.dev2010.de-en.$l \
$tmp/IWSLT14.TEDX.dev2012.de-en.$l \
$tmp/IWSLT14.TED.tst2010.de-en.$l \
$tmp/IWSLT14.TED.tst2011.de-en.$l \
$tmp/IWSLT14.TED.tst2012.de-en.$l \
> $tmp/test.$l
done
从训练语料中学习BPE,并对数据集进行BPE切分
TRAIN=$tmp/train.en-de
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 > $prep/$f
done
done
-
learn_bpe.py
的功能是从原始的训练集中学习一个subword的词表。 -
apply_bpe.py
的功能是将刚才学到的词表对训练数据进行subword化。 -
BPE
在NLP领域的应用:https://zhuanlan.zhihu.com/p/86965595
数据规范化
值得说明的是,上述步骤在不同的任务上,数据处理步骤可能有所差异。在该步骤中,将上述用shell脚本初步处理的数据进行规范化,规范化之后的数据作为模型的最终输入。
安装了Fairseq之后,Fairseq就会把fairseq-preprocess
等注册到控制台,如setup.py中所示:
entry_points={
'console_scripts': [
'fairseq-eval-lm = fairseq_cli.eval_lm:cli_main',
'fairseq-generate = fairseq_cli.generate:cli_main',
'fairseq-interactive = fairseq_cli.interactive:cli_main',
'fairseq-preprocess = fairseq_cli.preprocess:cli_main',
'fairseq-score = fairseq_cli.score:cli_main',
'fairseq-train = fairseq_cli.train:cli_main',
'fairseq-validate = fairseq_cli.validate:cli_main',
],
}
按照官方教程,可以执行:
TEXT=examples/translation/iwslt14.tokenized.de-en
fairseq-preprocess --source-lang de --target-lang en \
--trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
--destdir data-bin/iwslt14.tokenized.de-en
但是在实际使用过程中,发现有时候调用的Python版本不对,特别是使用了conda
环境时,因此不如直接执行对应的Python脚本。此外,可以指定dataset-impl raw
以生成文本形式的训练语料,便于理解和检查问题:
TEXT=examples/translation/iwslt14.tokenized.de-en
python fairseq-cli/preprocess.py --source-lang de --target-lang en \
--trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
--destdir data-bin/iwslt14.tokenized.de-en \
--dataset-impl raw
在该步骤中,主要是将训练语料放置到目标位置destdir
,建立token-索引值
词典,并且对训练语料进行二进制化。
除此之外,
-
Fairseq需要将
args.***pref
和source-lang
,target-lang
组合起来查找语料,因此source-lang
,target-lang
需要和之前的语言简写保持一致。Fairseq寻找的语料位置:{args.***pref.xxx}-{lang}
,其中,***
为train
/valid
/test
,xxx
为source
/target
。另外,source-lang
,target-lang
指定字典命名,输出的字典名为:dict.{xxx-lang}
,其中,xxx
为source
/target
。 -
destdir
用于指定输出的训练语料位置。
训练
创建存放模型的文件夹
mkdir -p checkpoints/fconv
启动训练
同样地,直接执行对应的Python脚本:
CUDA_VISIBLE_DEVICES=0 python fairseq-cli/train.py data-bin/iwslt14.tokenized.de-en \
--lr 0.25 --clip-norm 0.1 --dropout 0.2 --max-tokens 4000 \
--dataset-impl raw \
--arch fconv_iwslt_de_en --save-dir checkpoints/fconv
默认情况下,Fairseq使用机器上的所有GPU,在这个例子中,通过指定CUDA_VISIBLE_DEVICES=0
使用机器上编号为0的GPU。由于上一个步骤中,指定数据集形式为raw
,因此在这一步骤中,训练集的形式应明确指定为raw
。另外,通过指定max-tokens
,Fairseq自行决定batch_size
。
除此之外,在上述的示例中,
-
第一个无名参数
data-bin/iwslt14.tokenized.de-en
用于指定训练语料的父目录。 -
lr
指定学习率,clip-norm
指定梯度的最大范数,参见:torch.nn.utils.clip_grad_norm_,dropout
指定dropout的丢弃率。 -
arch
指定训练的具体模型,可在fairseq/models
寻找到定义的模型结构。model
定义抽象模型,arch
定义具体的模型结构,比如多少词嵌入维度,多少个隐藏层等。
生成
在该步骤中,不使用官方教程上面的generate
,因为其无法指定输入文件,改用interactive
,并使用--input
指定输入的测试文本。
python fairseq-cli/interactive.py data-bin/iwslt14.tokenized.de-en \
--input evalution.txt \
--path checkpoints/fconv/checkpoint_best.pt \
--batch-size 128 --beam 5
-
第一个无名参数
data-bin/iwslt14.tokenized.de-en
用于指定语料的父目录。 -
input
指定用于预测的语料路径。 -
path
指定训练好的模型路径。 -
beam
指定束搜索(beam search)的束大小。参见:https://www.jianshu.com/p/c2420ff9744a