MBART: Multilingual Denoising Pre-training for Neural Machine Translation
模型下載
mBART 是一個基于序列到序列的去噪自編碼器,使用 BART 目標在多種語言的大規模單語語料庫上進行預訓練。mBART 是首批通過去噪完整文本在多種語言上預訓練序列到序列模型的方法之一,而以往的方法則僅集中在編碼器、解碼器或重構文本的部分內容。
首先需要在github上下載mbart的預訓練模型,我們要完成的任務是微調:README
下載mbart.CC25模型,下載解壓后的目錄如下:
├── mbart.cc25.v2└── dict.txt└── model.pt└── sentence.bpe.model
dict是模型的詞典文件,model是mbart的預訓練模型,sentence.bpe.model是sentencepiece訓練分詞的模型。
我們在en->vi
雙語對上對預訓練模型進行微調:
數據為IWSLT15的雙語對,下載好數據之后對訓練集、驗證集和測試集重新命名,整理后的目錄如下
├── en_vi └── test.en_XX└── test.vi_VN└── train.en_XX└── train.vi_VN└── valid.en_XX└── valid.vi_VN
環境準備
這里需要特定的fairseq版本來完成以下的這些命令,因此推薦新創建一個conda環境來隔離版本,這里我們命名為mbart_ft.
fairseq=0.10.2
python=3.8
numpy=1.19.5
數據分詞
首先使用sentencepiece模型對數據進行分詞,由于原文中描述沒有額外的 true-casing、normalizing punctuation / characters.因此我們直接分詞即可。
#!/bin/bashSPM=/path/to/sentencepiece/build/src/spm_encode
MODEL=/mbart/mbart.cc25.v2/sentence.bpe.model
DATA=/mbart/en_vi
DEST=/mbart/en_vi/spm
TRAIN=train
VALID=valid
TEST=test
SRC=en_XX
TGT=vi_VN ${SPM} --model=${MODEL} < ${DATA}/${TRAIN}.${SRC} > ${DEST}/${TRAIN}.spm.${SRC} &
${SPM} --model=${MODEL} < ${DATA}/${TRAIN}.${TGT} > ${DEST}/${TRAIN}.spm.${TGT} &
${SPM} --model=${MODEL} < ${DATA}/${VALID}.${SRC} > ${DEST}/${VALID}.spm.${SRC} &
${SPM} --model=${MODEL} < ${DATA}/${VALID}.${TGT} > ${DEST}/${VALID}.spm.${TGT} &
${SPM} --model=${MODEL} < ${DATA}/${TEST}.${SRC} > ${DEST}/${TEST}.spm.${SRC} &
${SPM} --model=${MODEL} < ${DATA}/${TEST}.${TGT} > ${DEST}/${TEST}.spm.${TGT} &wait
echo "SentencePiece encoding completed!"
我們創建spm目錄,分詞后的目錄文件為:
├── en_vi├── spm └── test.spm.en_XX└── test.spm.vi_VN└── train.spm.en_XX└── train.spm.vi_VN└── valid.spm.en_XX└── valid.spm.vi_VN
數據預處理
使用fairseq將數據處理為滿足訓練的、輸入模型的格式。包含兩種語言的詞典文件、二進制格式和分詞轉換為id的文件。
#!/bin/bashBASEDIR=/mbart/en_vi
DATA=${BASEDIR}/spm
DEST=${BASEDIR}/ids
DICT=/mbart/mbart.cc25.v2/dict.txt
SRC=en_XX
TGT=en_viTRAIN=train
VALID=valid
TEST=testfairseq-preprocess \
--source-lang ${SRC} \
--target-lang ${TGT} \
--trainpref ${DATA}/${TRAIN}.spm \
--validpref ${DATA}/${VALID}.spm \
--testpref ${DATA}/${TEST}.spm \
--destdir ${DEST}/ \
--thresholdtgt 0 \
--thresholdsrc 0 \
--srcdict ${DICT} \
--tgtdict ${DICT} \
--workers 70
預處理后的模型數據準備的目錄為:
├── en_vi├── ids └── dict.en_XX.txt└── dict.vi_VN.txt└── preprocess.log└── test.en_XX-vi_VN.en_XX.bin└── test.en_XX-vi_VN.en_XX.idx└── test.en_XX-vi_VN.vi_VN.bin└── test.en_XX-vi_VN.vi_VN.idx└── train.en_XX-vi_VN.en_XX.bin└── train.en_XX-vi_VN.en_XX.idx└── train.en_XX-vi_VN.vi_VN.bin└── train.en_XX-vi_VN.vi_VN.idx└── valid.en_XX-vi_VN.en_XX.bin└── valid.en_XX-vi_VN.en_XX.idx└── valid.en_XX-vi_VN.vi_VN.bin└── valid.en_XX-vi_VN.vi_VN.idx
訓練集和驗證集在訓練過程中被用到,而測試集只在評價生成中被用到。
模型的訓練
需要注意的是,與官方文檔設置的參數相比,有一處需要修改。
--max-update 40000
:模型參數更新次數。
----total-num-update 40000
這是設置學習率調度器的更新次數,即學習率更新40k次訓練停止。
在mbart的原文中:
We use a maximum of 40K training updates for all low and medium resource pairs and 100K for high resource pairs.
我們的en-vi雙語數據屬于低資源語言對,因此參數更新次數40K次,即應該設置--max-update 40000
#!/bin/bash
source /path/to/conda/etc/profile.d/conda.sh
conda activate mbart_ftBASEDIR=/mbart/en_vi
PRETRAIN=/mbart/mbart.cc25.v2/model.pt # 已下載的預訓練模型路徑
DATA=${BASEDIR}/ids # 預處理后的二進制數據路徑SRC=en_XX
TGT=vi_VNlangs=ar_AR,cs_CZ,de_DE,en_XX,es_XX,et_EE,fi_FI,fr_XX,gu_IN,hi_IN,it_IT,ja_XX,kk_KZ,ko_KR,lt_LT,lv_LV,my_MM,ne_NP,nl_XX,ro_RO,ru_RU,si_LK,tr_TR,vi_VN,zh_CNfairseq-train ${DATA} \--encoder-normalize-before --decoder-normalize-before \--arch mbart_large --layernorm-embedding \--task translation_from_pretrained_bart \--source-lang ${SRC} --target-lang ${TGT} \--criterion label_smoothed_cross_entropy --label-smoothing 0.2 \--optimizer adam --adam-eps 1e-06 --adam-betas '(0.9, 0.98)' \--lr-scheduler polynomial_decay --lr 3e-05 --warmup-updates 2500 --max-update 40000 \--dropout 0.3 --attention-dropout 0.1 --weight-decay 0.0 \--max-tokens 1024 --update-freq 2 \--save-interval 1 --save-interval-updates 5000 --keep-interval-updates 10 --no-epoch-checkpoints \--seed 222 --log-format simple --log-interval 2 \--restore-file $PRETRAIN \--reset-optimizer --reset-meters --reset-dataloader --reset-lr-scheduler \--langs $langs \--save-dir ${BASEDIR} \--ddp-backend no_c10d
在一塊RTX 4090顯卡上,運行3個小時后,訓練結束。我們設置了每5000次更新保存一次檢查點,微調模型保存的文件位置為 --save-dir ${BASEDIR}
微調后的目錄文件為:
├── en_vi├── ids ├── spm└── dict.en_XX.txt└── checkpoint_2_5000.pt└── checkpoint_4_10000.pt└── checkpoint_6_15000.pt└── checkpoint_8_20000.pt└── checkpoint_10_25000.pt└── checkpoint_12_30000.pt└── checkpoint_14_35000.pt└── checkpoint_16_40000.pt└── checkpoint_best.pt└── checkpoint_last.pt└── ...train...test...valid
模型的解碼
我們使用checkpoint_best.pt
對其進行解碼以及測BLEU分數。
這里我將分詞模型復制到了en_vi文件夾中,并且添加--cpu
使得解碼在cpu上運行。解碼生成的文件為/mbart/en_vi/ids/en_vi
2025.1.13修訂:
需要注意的是,相比于官方文檔,這里刪除了--bpe "sentencepiece"
、--sentencepiece-model $model_dir/sentence.bpe.model
以及--sacrebleu
若保留--sacrebleu
,由于版本間不匹配會報錯
若保留--bpe "sentencepiece"
,則除了模型推理行“H”,其他源句子、目標句子和行“D”均沒有空格出現。說明解碼過程中并不需要此參數。
--remove-bpe 'sentencepiece'
:用于去除分詞過程中產生的spm標記。
#!/bin/bash
source /path/to/conda/etc/profile.d/conda.sh
conda activate mbart_ft
model_dir=/mbart/en_vi/ids langs=ar_AR,cs_CZ,de_DE,en_XX,es_XX,et_EE,fi_FI,fr_XX,gu_IN,hi_IN,it_IT,ja_XX,kk_KZ,ko_KR,lt_LT,lv_LV,my_MM,ne_NP,nl_XX,ro_RO,ru_RU,si_LK,tr_TR,vi_VN,zh_CN
TOKENIZER=${model_dir}/sentence.bpe.modelfairseq-generate ${model_dir} \--path $model_dir/../checkpoint_best.pt \--task translation_from_pretrained_bart \--gen-subset test \--cpu \-t vi_VN -s en_XX \--remove-bpe 'sentencepiece' \--batch-size 32 \--langs $langs > ${model_dir}/en_vi
查看生成文件en_vi的片段:
S-74 I lost all hope .[en_XX]
T-74 T?i hoàn toàn tuy?t v?ng .
H-74 -0.4808153808116913 T?i ?? m?t h?t hy v?ng .
D-74 -0.4808153808116913 T?i ?? m?t h?t hy v?ng .
P-74 -0.3194 -0.9490 -0.5736 -0.8777 -0.7397 -0.0389 -0.4746 -0.2814 -0.2920 -0.2618
S-372 Today I am 22 .[en_XX]
T-372 H?m nay t?i 22 tu?i .
H-372 -0.3478223383426666 H?m nay t?i 22 tu?i .
D-372 -0.3478223383426666 H?m nay t?i 22 tu?i .
P-372 -0.5605 -0.0631 -0.4549 -0.2989 -0.4617 -0.4079 -0.3166 -0.3061 -0.2606
S-336 Thank you very much .[en_XX]
T-336 Cám ?n r?t nhi?u .
H-336 -0.46486935019493103 Cám ?n các b?n r?t nhi?u .
D-336 -0.46486935019493103 Cám ?n các b?n r?t nhi?u .
P-336 -1.8484 -0.0979 -0.1278 -0.9053 -0.2160 -0.4894 -0.1446 -0.4404 -0.2856 -0.3061 -0.2521
S-1267 Thank you very much .[en_XX]
T-1267 C?m ?n r?t nhi?u .
H-1267 -0.46486935019493103 Cám ?n các b?n r?t nhi?u .
D-1267 -0.46486935019493103 Cám ?n các b?n r?t nhi?u .
P-1267 -1.8484 -0.0979 -0.1278 -0.9053 -0.2160 -0.4894 -0.1446 -0.4404 -0.2856 -0.3061 -0.2521
S-21 But many die .[en_XX]
T-21 Nh?ng r?t nhi?u ng??i ?? ch?t .
H-21 -0.5680863261222839 Nh?ng nhi?u ng??i ch?t .
D-21 -0.5680863261222839 Nh?ng nhi?u ng??i ch?t .
P-21 -0.3266 -1.4395 -0.1804 -1.2362 -0.5122 -0.2999 -0.2973 -0.2526
S:是源句子,在en->vi
雙語對上,源語言是英語。
T:是人工翻譯句子,即測試集中的句子;
H:是模型輸出的解碼句子,第一個數字為其得分;
D:第一個數字為得分和H一致,但相比于H去掉了所有的空格,和S、T格式相同;
P:翻譯過程中每個單詞的預測概率。
運行解碼腳本后,在ids目錄中會生成 en_vi 文件。
├── en_vi├── ids └── sentence.bpe.model└── en_vi└── train...test...valid...dict...├── spm├── train...valid...test...
模型的評價
cat en_vi | grep -P "^H" |sort -V |cut -f 3- | sed 's/\[vi_VN\]//g' > en_vi.hyp
cat en_vi | grep -P "^T" |sort -V |cut -f 2- | sed 's/\[vi_VN\]//g' > en_vi.ref
sacrebleu en_vi.ref -i en_vi.hyp -m bleu
這里將 H 開頭的行提取,并去掉前兩個字段,僅保留模型輸出的解碼句子,將他們合成 en_vi.hyp文件;
將 T 開頭的行提取,并去掉第一個字段,保留test文件中的目標句子,將他們合成 en_vi.ref 文件。
這兩行代碼運行后,目錄ids中應該多出兩個文件。
├── en_vi├── ids └── en_vi└── en_vi.hyp└── en_vi.ref└── train...test...valid...dict...├── spm├── train...valid...test...
這兩個文件的行數應該一致,使用sacrebleu來測bleu的分數,指定 -tok 分詞方式是 “spm” 即sentencepiece。
我們測試的模型評價結果為:
{"name": "BLEU","score": 34.7,"signature": "nrefs:1|case:mixed|eff:no|tok:13a|smooth:exp|version:2.4.3","verbose_score": "66.2/42.0/27.8/18.7 (BP = 1.000 ratio = 1.007 hyp_len = 33986 ref_len = 33738)","nrefs": "1","case": "mixed","eff": "no","tok": "13a","smooth": "exp","version": "2.4.3"
}
附錄
2025.1.13修訂:
原版本未刪除--bpe "sentencepiece"
、--sentencepiece-model $model_dir/sentence.bpe.model
參數,fairseq推理后生成的en_vi文件為:
S-74 Ilostallhope.[en_XX]
T-74 T?ihoàntoàntuy?tv?ng.
H-74 -0.4808153808116913 T?i ?? m?t h?t hy v?ng .
D-74 -0.4808153808116913 T?i??m?th?thyv?ng.
P-74 -0.3194 -0.9490 -0.5736 -0.8777 -0.7397 -0.0389 -0.4746 -0.2814 -0.2920 -0.2618
S-372 TodayIam22.[en_XX]
T-372 H?mnayt?i22tu?i.
H-372 -0.3478223383426666 H?m nay t?i 22 tu?i .
D-372 -0.3478223383426666 H?mnayt?i22tu?i.
P-372 -0.5605 -0.0631 -0.4549 -0.2989 -0.4617 -0.4079 -0.3166 -0.3061 -0.2606
S-336 Thankyouverymuch.[en_XX]
T-336 Cám?nr?tnhi?u.
H-336 -0.46486935019493103 Cám ?n các b?n r?t nhi?u .
D-336 -0.46486935019493103 Cám?ncácb?nr?tnhi?u.
P-336 -1.8484 -0.0979 -0.1278 -0.9053 -0.2160 -0.4894 -0.1446 -0.4404 -0.2856 -0.3061 -0.2521
S-1267 Thankyouverymuch.[en_XX]
T-1267 C?m?nr?tnhi?u.
H-1267 -0.46486935019493103 Cám ?n các b?n r?t nhi?u .
D-1267 -0.46486935019493103 Cám?ncácb?nr?tnhi?u.
P-1267 -1.8484 -0.0979 -0.1278 -0.9053 -0.2160 -0.4894 -0.1446 -0.4404 -0.2856 -0.3061 -0.2521
S-21 Butmanydie.[en_XX]
T-21 Nh?ngr?tnhi?ung??i??ch?t.
H-21 -0.5680863261222839 Nh?ng nhi?u ng??i ch?t .
D-21 -0.5680863261222839 Nh?ngnhi?ung??ich?t.
P-21 -0.3266 -1.4395 -0.1804 -1.2362 -0.5122 -0.2999 -0.2973 -0.2526
可以看到,測試集源句子S以及目標句子T的空格被誤刪除。由此提取的模型生成文件en_vi.hyp和翻譯參考文件en_vi.ref同樣誤刪空格。且模型輸出句子H是正常的,這就說明是在解碼過程中出現的問題。
2025.1.15修訂:
在使用sacrebleu測bleu分數時,-tok 參數指定分詞器。
默認為“13a”,即不添加此參數時的默認,我們測出評分為34.7.
指定為"spm" 等同于 “flores101”,使用基于Flores-101和Flores-200數據集構建的SentencePiece模型。
{"name": "BLEU","score": 35.4,"signature": "nrefs:1|case:mixed|eff:no|tok:flores101|smooth:exp|version:2.4.3","verbose_score": "66.3/42.8/28.8/19.5 (BP = 0.997 ratio = 0.997 hyp_len = 34971 ref_len = 35063)","nrefs": "1","case": "mixed","eff": "no","tok": "flores101","smooth": "exp","version": "2.4.3"
}
指定為“none”,將不應用任何分詞,測出評分為:
{"name": "BLEU","score": 34.6,"signature": "nrefs:1|case:mixed|eff:no|tok:none|smooth:exp|version:2.4.3","verbose_score": "66.2/42.0/27.8/18.6 (BP = 1.000 ratio = 1.008 hyp_len = 33948 ref_len = 33682)","nrefs": "1","case": "mixed","eff": "no","tok": "none","smooth": "exp","version": "2.4.3"
}