Fairseq 机器翻译全流程一文速通 (NMT, WMT, translation)

最新编辑于:2024年8月30日

一、摘要

fairseq 是个常用的机器翻译项目。它的优化很好,但代码晦涩难懂,限制了我们的使用。

本文旨在梳理如下流程:1)准备 WMT23 的数据 (其余生成任务皆可类比),2)训练模型,3)用 sacrebleu、COMET-22 评测模型。

不想要 wmt 的数据,想要自己的数据也完全 ok。反正最终的目标就是为训练、验证、测试准备分别两个文本文件(验证、测试集也可以不准备),每个文本文件的每一行就是一条句子。准备好后,跳到第三章预处理,按照需求修改文件名字和路径就行。

二、数据下载

1. 训练数据

我们使用 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.zhtrain.en 。这两个文件有相同的行数。每一行为一条训练数据。两个文件的每一行一一对应。

或许有人好奇,上述的命令到底下载了那些语料库。我们可以看到,第二行wget命令下载了一个 yml 文件。用文本编辑器打开这个文件,有如下的内容:

- id: wmt23-jaen
  langs: jpn-eng
  #dev:
  #test:
  train: &para_jpn_eng
    - Statmt-news_commentary-16-eng-jpn
    - KECL-paracrawl-3-eng-jpn
    - Statmt-wikititles-3-jpn-eng
    - Facebook-wikimatrix-1-eng-jpn
    - Statmt-ted-wmt20-eng-jpn
    - StanfordNLP-jesc_train-1-eng-jpn
    - Phontron-kftt_train-1-eng-jpn
  mono_train:
    - *mono_eng
    - &mono_jpn
        - Statmt-news_crawl-2021-jpn
        - Statmt-news_commentary-17-jpn
        - Statmt-commoncrawl-wmt22-jpn
        - Leipzig-web-2020_1m-jpn_JP
        - Leipzig-comweb-2018_1m-jpn
        - Leipzig-web_public-2019_1m-jpn_JP
        - Leipzig-news-2020_100k-jpn
        - Leipzig-newscrawl-2019_1m-jpn
        - Leipzig-wikipedia-2021_1m-jpn

对于日语-英语,上述列举的那么多文件都会被下载下来,然后拼接在一起。如果我们只想下载部分文件,进行小规模的实验,不妨按照需求删除一些文件。

好的,训练集到此为止了。在进一步清洗训练数据之前,我们先准备下测试数据和验证数据。

2. 测试集和验证集

根据 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。

如果我们选择用 sacrebleu 来最终评估模型的性能的话,这一步的测试集可以不用下载,不过反手顺手,就下载了吧。

三、预处理

在进行预处理之前,这里提供了 A,B 两种选择。推荐别思考,选第二个。

A、BPE from subword-nmt

使用 fairseq 默认的预处理,具体来说,它包括如下几个步骤:

  1. 用 moses (或者 sacremoses)进行 normalization 。具体做了啥,我也很难说清楚。
  2. 用 moses 进行 tokenization。主要就是把词语与标点符号用空格分开。
  3. 用 subword-nmt 在训练数据上学习 bpe tokenizer。
  4. 用 bpe tokenizer 对训练数据和测试数据进行 tokenization。可以理解为把单词进一步拆碎,成为一个或多个token。
  5. 过滤训练数据中过短或过长的。

(该选择请阅读接下来的1, 3小节。)

B、SentencePiece Tokenizer

Sentencepiece 是现在更常用的 tokenizer。它的好处之一,就是说在学习 tokenizer 之前,不需要用 moses 进行 normalization,tokenization:

--input: one-sentence-per-line raw corpus file. No need to run tokenizer, normalizer or preprocessor. By default, SentencePiece normalizes the input with Unicode NFKC. You can pass a comma-separated list of files.

而且它可以直接指定 tokenizer 最终会有多少个 token。

(该选择请阅读接下来的1, 2小节。)

1. 文件准备

在开始之前,请将之前下载的文件组织成这样:

|-orig
  --train.tgt
  --train.src
  |-test
    --test.tgt
    --test.src
    --dev.tgt
    --dev.src

其中 orig 表示你存放数据的文件夹,或许是叫做 wmt23-jaen,其他的也可以。这里的srctgt是指源语言和目标语言的两个字母的代码,比如jaen,请按照需求修改。

2. SentencePiece

下面的 bash 文件包括了学习 tokenizer,并且处理训练和测试集的流程。

你所要做的,1)确保已经 clone 了 fairseq,然后用 SCRIPTS 指向文件夹。2)检查(用#分割的)前两块的一些变量,设置成自己的。

接着,一键运行吧,就是这么简单!

注意character_coverage 这个是可以修改的。对于中文和日文,可以设置为 0.9995 。对于拉丁语言,改成 1.0 比较好。

(参考了 fairseq 提供的这个例子)

############################################################
pip install sentencepiece

SCRIPTS=fairseq/scripts
SPM_TRAIN=$SCRIPTS/spm_train.py
SPM_ENCODE=$SCRIPTS/spm_encode.py


BPESIZE=32000
TRAIN_MINLEN=1  # remove sentences with <1 BPE token
TRAIN_MAXLEN=250  # remove sentences with >250 BPE tokens

TRAIN_SENTENCE_MAXNUM=20000000
############################################################

CHAR_COVER=1.0

src=ja
tgt=en
orig=wmt23-jaen

CORPORA=(
    "train"
)

OUTDIR=wmt23-${src}${tgt}-sentencepiece
prep=$OUTDIR

mkdir -p $prep

############################################################
TRAIN_FILES=$(for f in "${CORPORA[@]}"; do echo $orig/$f.${src}; echo $orig/$f.${tgt}; done | tr "\n" ",")
echo "learning joint BPE over ${TRAIN_FILES}..."
python "$SPM_TRAIN" \
    --input=$TRAIN_FILES \
    --model_prefix=$prep/sentencepiece.bpe \
    --vocab_size=$BPESIZE \
    --character_coverage=$CHAR_COVER \
    --model_type=bpe \
    --input_sentence_size=$TRAIN_SENTENCE_MAXNUM \
    --shuffle_input_sentence=true

############################################################
# echo "encoding train/valid/test with learned BPE..."

echo "encoding train with learned BPE..."
python "$SPM_ENCODE" \
    --model "$prep/sentencepiece.bpe.model" \
    --output_format=piece \
    --inputs $orig/train.${src} $orig/train.${tgt} \
    --outputs $prep/train.${src} $prep/train.${tgt} \
    --min-len $TRAIN_MINLEN --max-len $TRAIN_MAXLEN

echo "encoding valid with learned BPE..."
python "$SPM_ENCODE" \
    --model "$prep/sentencepiece.bpe.model" \
    --output_format=piece \
    --inputs $orig/test/dev.${src} $orig/test/dev.${tgt} \
    --outputs $prep/valid.${src} $prep/valid.${tgt}

echo "encoding test with learned BPE..."
python "$SPM_ENCODE" \
    --model "$prep/sentencepiece.bpe.model" \
    --output_format=piece \
    --inputs $orig/test/test.${src} $orig/test/test.${tgt} \
    --outputs $prep/test.${src} $prep/test.${tgt}

3. BPE from subword-nmt

喂,用了 SentencePiece,就没必要看这节了!

这一步包括清洗训练数据,学习 bpe tokenizer,然后 tokenize 数据。请放心,我们将用一个 bash 文件搞定。

准备工作完成,请将下述的代码保存在一个 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
pip install subword-nmt

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}..."
subword-nmt learn-bpe -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}..."
        subword-nmt apply-bpe -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 指定训练数据的前缀。你或许会注意到,我们可以在 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

############################################################

4. 二进制文件

很好,您可以检查下输出目录下的文件。如果成功的话,训练、测试、验证数据各有两个文件。这时候打开文件,会发现这些文件仍旧是可读的,只不过会有 或者 @@ 这种符号。它表示一个单词被切成了一个个 token。

接下来,我们需要将这些文本数据转换成二进制文件,用于 fairseq。这有利于更快的训练。同样的,根据你之前选择的 tokenizer,这里有两种不同的处理方式。不过,我仍然推荐按顺序阅读下,更好地理解代码。

a. BPE from subword-nmt

TEXT=examples/translation/wmt23-jaen

fairseq-preprocess --source-lang ja --target-lang en \
--joined-dictionary \
--trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
--destdir data-bin/wmt23-zhen --workers 10

显然,TEXT 指的是您先前的输出目录,而现在,它成了输入目录。destdir 则是二进制文件要保存的地方。

其中 --joined-dictionary 是选填的,也就是说输入语言和输出语言要不要共用一个字典。

既然您阅读到了这里,我必须要说明下这个命令在做什么。

  1. 初始化一个字典。它的实现在 fairseq/data/dictionary.py 。初始化的时候,会自动加上四个特殊符号:句子结束、开始、padding、unk。
  2. 扫描传入文件的每一行,按照空格将 token 分开。然后统计每个 token 出现的次数。
  3. 将所有 token 按照出现次数降序排列,然后根据设置的字典大小,将排在前面的 token 加入字典。如果不设置,就是所有 token。
  4. 接着扫描所有文件的每一行,按照空格将 token 分开,然后把每个 token 变成字典中的序号。
  5. 然后经过 binarize 啥的,大抵就是减少存储空间。

在上面的二进制命令中,用 nwordssrcnwordstgt 可以指定字典的大小。如果是joined-dictionary,指定一个就行了。

如果要后续复用字典处理其他文件,可以这么指定 --tgtdict data-bin/iwslt17.de_fr.en.bpe16k/dict.en.txt。因为是 src 和 tgt 使用同一个字典,所以只要制定 tgt 要用到的字典就行。


好的,很符合直觉,也很奇怪

bpe 学习完之后就自带一个字典。而且用 bpe tokenize 之后的文件,里面是不会出现不在 bpe 字典里的词。也就是说,这一步学习到的字典,肯定是和 bpe 自带的一样。

不过我跑了下,居然是不一样的,你说奇怪不奇怪。

--- 更新于 2024年8月30日

我猜测,在上一节 学习 bpe 以及 把数据 tokenize 化,目标是把文本切分开了一个个词。

然后这一步就用之前切分好的词,来维护一个新的字典(是不是有病啊,怀疑显得程度)

b. SentencePiece

可以发现,在 SentencePiece 处理完文件后,你能够得到两个额外的东西,一个是 model,一个是 vocab。

处理后的文件用空格分开的所有 token,都能在 vocab 里找到对应(有例外)。所以,接下来我们可以根据这个 vocab,把处理完的文件的变成数字的序列。

如果不在 fairseq-preprocess 里传入字典,那么它会学习一个新的字典 —— 这个字典取决于这个命令处理的数据。这样不好。所以我们观测下 fairseq-preprocess 输出的字典格式,然后把 sentencepiece 得到的字典直接伪装成最终要的字典。

(这里参考了issue)。

首先要把这个 vocab 转换成 fairseq 能用的格式:

# strip the first three special tokens and append fake counts for each vocabulary
tail -n +4 sentencepiece.bpe.vocab | cut -f1 | sed 's/$/ 100/g' > fairseq.vocab

接着开始处理:

TEXT=wmt23-zhen-sentencepiece
DICT=wmt23-zhen-sentencepiece/fairseq.vocab

fairseq-preprocess --source-lang zh --target-lang en \
--joined-dictionary --tgtdict $DICT \
--trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
--destdir wmt23-zhen-fairseq-sentencepiece --workers 10

大致上和 BPE from subword-nmt 做的没什么区别,只是跳过了获得字典这一步。

四、训练

接下来,我们用四个显卡,小训 50 个 epoch,并且只保存在验证集上效果最好的那个 epoch 的 checkpoint。

如果没有准备验证集,只是想先训训看看的话,请传入 --disable-validation

CUDA_VISIBLE_DEVICES=0,1,2,3 fairseq-train data-bin/wmt17_en_de/ \
--arch transformer_iwslt_de_en --share-decoder-input-output-embed \
--optimizer adam --adam-betas '(0.9, 0.98)' \
--clip-norm 0.0 --lr 5e-4 --lr-scheduler inverse_sqrt \
--warmup-updates 512 --dropout 0.3 --weight-decay 0.0001 \
--criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
--max-tokens 32768 \
--update-freq 2 \
--max-source-positions 256 \
--max-target-positions 256 \
--max-epoch 10 \
--save-interval-updates 100 \
--keep-interval-updates 10 \
--no-epoch-checkpoints \
--amp \
--eval-bleu \
--eval-bleu-args '{"beam": 5, "max_len_a": 1.2, "max_len_b": 10}' \
--eval-bleu-detok moses \
--eval-bleu-remove-bpe \
--eval-bleu-print-samples \
--best-checkpoint-metric bleu \
--no-epoch-checkpoints \
--maximize-best-checkpoint-metric \
--find-unused-parameters \
--log-interval 1 \
--wandb-project translation_deen \
--skip-invalid-size-inputs-valid-test \
--save-dir checkpoints/test_transformer/

上面我尽可能列出了常用的命令,大家可以猜测意思修改。

其中 --save-interval-updates 100 keep-interval-updates 10 --no-epoch-checkpoints 意味着,每训练 100 步测试下模型,并保存这个模型,但是只保存最多 10 个,并且我选择不保存每个epoch结束后的模型。


下面是我自用的命令,备份用,大家跳过吧:

CUDA_VISIBLE_DEVICES=2,3 fairseq-train /data/yuanhang/wmt23/wmt23-deen-fairseq \
--arch transformer_wmt_en_de --share-decoder-input-output-embed \
--optimizer adam --adam-betas '(0.9, 0.98)' \
--clip-norm 0.0 --lr 5e-4 --lr-scheduler inverse_sqrt \
--warmup-updates 512 --dropout 0.3 --weight-decay 0.0001 \
--criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
--moe-loss-coeff 0.01 \
--max-tokens 16384 \
--update-freq 32 \
--max-source-positions 256 \
--max-target-positions 256 \
--max-epoch 10 \
--save-interval-updates 100 \
--keep-interval-updates 10 \
--no-epoch-checkpoints \
--amp \
--eval-bleu \
--eval-bleu-args '{"beam": 5, "max_len_a": 1.2, "max_len_b": 10}' \
--eval-bleu-detok moses \
--eval-bleu-remove-bpe \
--eval-bleu-print-samples \
--best-checkpoint-metric bleu \
--maximize-best-checkpoint-metric \
--find-unused-parameters \
--log-interval 1 \
--wandb-project translation_deen \
--save-dir checkpoints/wmt23-deen-base/ |& tee logs/wmt23-deen-base.log

五、模型平均(选做)

机器翻译界有个trick:训练过程中保存多个 checkpoints,训练结束后把它们的权重平均一下,得到新的模型。

这个功能 fairseq 提供了,很简单:

CHECKPOINT_DIR=

python scripts/average_checkpoints.py \
    --num-update-checkpoints 10 \
    --inputs $CHECKPOINT_DIR \
    --output $CHECKPOINT_DIR/checkpoint.avg10.pt

六、测试

测试有两种方法,一种是 fairseq 自带的,一种是用 sacrebleu。我个人测试下,差别不是很大。

fairseq-generate data-bin/wmt17_en_de/ --path checkpoints/test_transformer/checkpoint_best.pt --batch-size 128 --beam 5 --remove-bpe=sentencepiece

如果用的是 subword-ntm,请将 --remove-bpe=sentencepiece change to --remove-bpe

当然,现在更流行的是 sacrebelu,我参考了这个链接这个链接,得到了下面两个命令,亲测可行:

**重要,更新于 2024年8月30日 **:所有的 sacrebleu 命令后面都应该加上 --tokenize $TGTLANG 来指定语言。不然的话 bleu 会低很多。如果 sacreblue 没有支持某个语言,或者是你想要自定义 tokenize 的方法,那么就请先把预测结果和 label 都 tokenize 下,然后再用 sacrebleu 某个参数,明确告诉它输入的是 tokenize 后的版本。

1. BPE from subword-nmt

DATASET=wmt22
LANGPAIR=ja-en
SRCLANG=ja
TGTLANG=en

BPECODE=
DATABIN=
MODEL=

sacrebleu -t $DATASET -l $LANGPAIR --echo src \
| sacremoses -q -l $SRCLANG tokenize -a \
| subword-nmt apply-bpe -c $BPECODE \
| fairseq-interactive $DATABIN --path $MODEL \
    -s $SRCLANG -t $TGTLANG \
    --beam 5 --remove-bpe --buffer-size 1024 --max-tokens 8000 \
| grep ^H- | cut -f 3- \
| sacremoses -q -l $TGTLANG detokenize \
| sacrebleu -t $DATASET -l $LANGPAIR -m bleu chrf --tokenize $TGTLANG

用指令 sacrebleu --list -l en-fr 可以查看当前语言对支持的数据集。

2. SentencePiece

DATASET=wmt22
LANGPAIR=ja-en
SRCLANG=ja
TGTLANG=en

SPM_ENCODE=fariseq/scripts/spm_encode.py
SPM_MODEL=xxx/sentencepiece.bpe.model
DATABIN=
MODEL=

sacrebleu -t $DATASET -l $LANGPAIR --echo src \
| python $SPM_ENCODE --model $SPM_MODEL \
| fairseq-interactive $DATABIN --path $MODEL \
    -s $SRCLANG -t $TGTLANG \
    --beam 5 --remove-bpe=sentencepiece --buffer-size 1024 --max-tokens 8000 \
| grep ^H- | cut -f 3- \
| sacrebleu -t $DATASET -l $LANGPAIR -m bleu chrf --tokenize $TGTLANG

其实和 subword-nmt 的区别不大。

3. 基于神经网络的测试: COMET-22

根据这篇文章:Results of WMT22 Metrics Shared Task: Stop Using BLEU – Neural Metrics Are Better and More Robust, COMET-22 是更符合人类偏好的测试方法。

要使用这个方法,我是参照 https://huggingface.co/Unbabel/wmt22-comet-da 里的描述。

流程很简单,首先要安装一个库:

pip install --upgrade pip  # ensures that pip is current 
pip install unbabel-comet

然后要准备三个文件,翻译的源文件,我的翻译输出,翻译参考答案。

我们先准备源文件和参考答案:

DATASET=wmt22
LANGPAIR=ja-en
SRCLANG=ja
TGTLANG=en

sacrebleu -t $DATASET -l $LANGPAIR --echo src > source-inputs.txt
sacrebleu -t $DATASET -l $LANGPAIR --echo ref > references.txt

然后进行翻译(和上一小节一样,为了偷懒,我就不写 subword-nmt的了):

SPM_ENCODE=fariseq/scripts/spm_encode.py
SPM_MODEL=xxx/sentencepiece.bpe.model
DATABIN=
MODEL=

sacrebleu -t $DATASET -l $LANGPAIR --echo src \
| python $SPM_ENCODE --model $SPM_MODEL \
| fairseq-interactive $DATABIN --path $MODEL \
    -s $SRCLANG -t $TGTLANG \
    --beam 5 --remove-bpe=sentencepiece --buffer-size 1024 --max-tokens 8000 \
| grep ^H- | cut -f 3- > translation-outputs.txt

comet-score -s source-inputs.txt -t translation-outputs.txt -r references.txt --model Unbabel/wmt22-comet-da

最后一行其实就是打分啦,如果是第一次使用,会下载一个 2GB 的模型:

comet-score -s source-inputs.txt -t translation-outputs.txt -r references.txt --model Unbabel/wmt22-comet-da

Github 的 Interpreting Scores 这一节可以读一下,它推荐,在比较不同的模型时,使用下面这个命令,可以输出 statistical hypothesis test 的结果:

comet-compare -s source-inputs.txt -t hyp1.en hyp2.en hyp3.en -r references.txt

七、结语

祝大家吃好睡好,下次再见!

八、番外:utf-8 问题

在处理日语文件时,我遇到了有无法被 unicode 解码,导致 tokenize 失败的问题。要解决这个问题也很简单,只要在预处理的时候,处理掉那些奇怪的字符就好了。

举个例子,我假设我的训练文件名字是 old_train, 新文件是 train,那么处理可以这样:

with open("old_train.ja", errors='ignore') as input_ja, open("old_train.en") as input_en, open("train.ja", "w") as output_ja, open("train.en", "w") as output_en:
    index = 0
    try:
        for ja_row, en_row in zip(input_ja, input_en):
            output_ja.write(ja_row)
            output_en.write(en_row)
            index += 1
            
            if index % 1000000 == 0:
                print(f"!!!! {index}")

            
    except UnicodeDecodeError:
        print(f"yyh here {index}")

可以看到,在读取日语训练文件时,我指定了 errors='ignore',这会忽略导致错误的字符。

虽然我写了 except,但理论上不会被触发。

九、番外:安装 fairseq 的问题

1. 12 erros detected in ...


参考这个网站 https://github.com/NVIDIA/apex/issues/1735 。 如果安装失败,或许是 apex 当前版本的代码有错误(2023年11月12日01:47:21)。

我是 torch 2.0.1 时安装 apex 有这个问题。

只要安装 apex 之前的版本就行:

git checkout 2386a912164b0c5cfcd8be7a2b890fbac5607c82

2. cuda 相关


最近在新机器上安装 fairseq ,会遇到当前 cuda 版本和编译了 pytorch 的 cuda 版本不匹配的问题。但如果不是安装可编辑版本,而是直接用 pip 安装,就没问题。很奇怪。

我安装的全流程是这样的:

根据我的一篇文章安装 cuda,修改环境变量 --> 创建 conda 虚拟环境 --> 安装 torch --> 安装 apex --> 安装 fairseq。

然后就遇到问题了。我一通折腾,改了个流程:

删除 conda 的环境,删除 conda 缓存的包,删除 cuda 的环境变量 --> 创建 conda 虚拟环境 --> 安装 torch --> 安装 fairseq --> 修改 cuda 环境变量 --> 安装 apex。

然后就好了。很奇怪,不是吗。我不设置 cuda 的环境变量,居然能够安装 fairseq,在这时候安装 apex 反而会报错。我也不知道是哪一步生效的。

我又在一台机子上测试了下,在安装 fairseq 的时候,不要有 cuda 的环境变量,就可以安装成功。

值得一提,如果要为了 fairseq 安装 apex,可能需要加几个参数,当然不加也是可以的:

pip install -v --disable-pip-version-check --no-cache-dir --no-build-isolation --config-settings "--build-option=--cpp_ext" --config-settings "--build-option=--cuda_ext" --config-settings "--build-option=--deprecated_fused_adam" --config-settings "--build-option=--xentropy" --config-settings "--build-option=--fast_multihead_attn" ./
posted @ 2023-11-07 00:35  ysngki  阅读(1214)  评论(0编辑  收藏  举报