fairseq提供了一份wmt14英德数翻译据集的预处理脚本,简单结合其代码分析一下其处理步骤:
- 下载mosesdecoder。mosesdecoder的使用文档在这里
echo 'Cloning Moses github repository (for tokenization scripts)...'
git clone https://github.com/moses-smt/mosesdecoder.git
- 下载subword nmt。这个开源库是用于构造bpecodes及其字典的。
echo 'Cloning Subword NMT repository (for BPE pre-processing)...'
git clone https://github.com/rsennrich/subword-nmt.git
SCRIPTS=mosesdecoder/scripts # 定义SCRIPTS变量,指向mosesdecoder的脚本文件夹
TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl # 定义TOKENIZER变量,指向mosesdecoder的tokenizer.perl, 用来分词
CLEAN=$SCRIPTS/training/clean-corpus-n.perl # 定义CLEAN变量,指向mosesdecoder的clean-corpus-n.perl,clean的主要作用是保留指定长度的数据
NORM_PUNC=$SCRIPTS/tokenizer/normalize-punctuation.perl # 定义NORM_PUNC变量,指向normalize-punctuation.perl,用来将标点符号规范化
REM_NON_PRINT_CHAR=$SCRIPTS/tokenizer/remove-non-printing-char.perl # 定义REM_NON_PRINT_CHAR变量,指向remove-non-printing-char.perl,去除语料中的非打印字符
BPEROOT=subword-nmt/subword_nmt # 定义BPEROOT变量,指向subword_nmt根目录。
BPE_TOKENS=40000 # 指定BPE TOKENS的数量为40000
# 指定语料来源,其中包括了训练、验证、测试语料
URLS=(
"http://statmt.org/wmt13/training-parallel-europarl-v7.tgz"
"http://statmt.org/wmt13/training-parallel-commoncrawl.tgz"
"http://data.statmt.org/wmt17/translation-task/training-parallel-nc-v12.tgz"
"http://data.statmt.org/wmt17/translation-task/dev.tgz"
"http://statmt.org/wmt14/test-full.tgz"
)
# 指定文件名,和上面URLS对应
FILES=(
"training-parallel-europarl-v7.tgz"
"training-parallel-commoncrawl.tgz"
"training-parallel-nc-v12.tgz"
"dev.tgz"
"test-full.tgz" # 只要test-full是测试集,上面四个都是训练+验证集。
)
CORPORA=(
"training/europarl-v7.de-en"
"commoncrawl.de-en"
"training/news-commentary-v12.de-en"
)
# This will make the dataset compatible to the one used in "Convolutional Sequence to Sequence Learning"
# https://arxiv.org/abs/1705.03122
# 如果指定参数--icml17,就将语料2替换成wmt14的语料,而不是使用wmt17的语料,这是为了和ConvS2S论文保持一致
if [ "$1" == "--icml17" ]; then
URLS[2]="http://statmt.org/wmt14/training-parallel-nc-v9.tgz"
FILES[2]="training-parallel-nc-v9.tgz"
CORPORA[2]="training/news-commentary-v9.de-en"
OUTDIR=wmt14_en_de # 指定输出文件夹名
else
OUTDIR=wmt17_en_de
fi
src=en # 源语言为英文
tgt=de # 目标语言是德语
lang=en-de # 语言对为英德
prep=$OUTDIR # 文件夹前缀为$OUTDIR
tmp=$prep/tmp # 文件夹$OUTDIR内有一个tmp文件夹
orig=orig # orig=orig
dev=dev/newstest2013 # 开发集使用newstest2013
mkdir -p $orig $tmp $prep # 递归创建上面定义的文件夹,包括orig文件夹,$OUTDIR/tmp文件夹,$OUTDIR文件夹
cd $orig # 切换到orig文件夹中
for ((i=0;i<${#URLS[@]};++i)); do # 迭代每一个URLS
file=${FILES[i]}
if [ -f $file ]; then
echo "$file already exists, skipping download" # 如果文件之前已经下载下来了,就跳过
else
url=${URLS[i]}
wget "$url" # 否则下载
if [ -f $file ]; then
echo "$url successfully downloaded." # 下载完文件存在表示下载成功
else
echo "$url not successfully downloaded." # 查无此人,下载失败
exit -1
fi
if [ ${file: -4} == ".tgz" ]; then # 对于.tgz格式的文件,用zxvf命令解压
tar zxvf $file
elif [ ${file: -4} == ".tar" ]; then # 对于.tar格式的文件,用xvf命令解压
tar xvf $file
fi
fi
done
cd ..
- 重点来了
echo "pre-processing train data..." # 预处理训练语料
for l in $src $tgt; do
rm $tmp/train.tags.$lang.tok.$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.tags.$lang.tok.$l # 分词
done
done
echo "pre-processing test data..." # 预处理测试语料
for l in $src $tgt; do
if [ "$l" == "$src" ]; then
t="src"
else
t="ref"
fi
grep '<seg id' $orig/test-full/newstest2014-deen-$t.$l.sgm | \ #这一块操作没看懂
sed -e 's/<seg id="[0-9]*">\s*//g' | \
sed -e 's/\s*<\/seg>\s*//g' | \
sed -e "s/\’/\'/g" | \
perl $TOKENIZER -threads 8 -a -l $l > $tmp/test.$l # 分词
echo ""
done
echo "splitting train and valid..." # 划分训练集和验证集
for l in $src $tgt; do
awk '{if (NR%100 == 0) print $0; }' $tmp/train.tags.$lang.tok.$l > $tmp/valid.$l # 从训练集中,每100个句子抽1个句子作为验证集
awk '{if (NR%100 != 0) print $0; }' $tmp/train.tags.$lang.tok.$l > $tmp/train.$l
done
TRAIN=$tmp/train.de-en # 训练语料(包含src和tgt)
BPE_CODE=$prep/code # BPECODE文件
rm -f $TRAIN # train.de-en如果存在就删掉
for l in $src $tgt; do
cat $tmp/train.$l >> $TRAIN # 其实就是简单地将src语料和tgt语料按顺序放到一个文件中,方便后面联合学习bpe
done
echo "learn_bpe.py on ${TRAIN}..." # 学习BPE
python $BPEROOT/learn_bpe.py -s $BPE_TOKENS < $TRAIN > $BPE_CODE # 这里是将源语言和目标语言的语料联合起来学BPE的,因为我们用的是train.de-en
for L in $src $tgt; do
for f in train.$L valid.$L test.$L; do # 用学到的bpecode应用到三份语料中(训练语料,验证语料,测试语料)
echo "apply_bpe.py to ${f}..."
python $BPEROOT/apply_bpe.py -c $BPE_CODE < $tmp/$f > $tmp/bpe.$f # 输出到tmp中对应的文件,以bpe.作为前缀
done
done
perl $CLEAN -ratio 1.5 $tmp/bpe.train $src $tgt $prep/train 1 250 # 按照长度对训练语料和验证语料进行clean,只保留前250个token,并将结果输出到output文件夹中
perl $CLEAN -ratio 1.5 $tmp/bpe.valid $src $tgt $prep/valid 1 250
for L in $src $tgt; do
cp $tmp/bpe.test.$L $prep/test.$L # 对于test语料,不进行clean,直接放到output文件夹。
done
结束