chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# Beyond English-Centric Multilingual Machine Translation
|
||||
|
||||
## Introduction
|
||||
In this work, we create a true Many-to-Many multilingual translation model that can translate directly between any pair of 100 languages. Our focus on non-English-Centric models brings gains of more than 10 BLEU when directly translating between non-English directions while performing competitively with the best single systems of WMT.
|
||||
|
||||
If you are new to using fairseq, read the following walkthrough. Otherwise, skip to the sections below.
|
||||
|
||||
0. **Generation Data**
|
||||
|
||||
To download the generation data, follow the below commands. Note that all datasets need to be detokenized *before* applying SPM in the data preprocessing step. If you use these evaluation datasets, please cite their associated papers.
|
||||
```bash
|
||||
# WMT - use sacrebleu, example here:
|
||||
sacrebleu -t wmt14 -l fr-en --echo src > wmt.test.fr-en.fr
|
||||
sacrebleu -t wmt14 -l fr-en --echo ref > wmt.test.fr-en.en
|
||||
|
||||
# WAT
|
||||
wget http://lotus.kuee.kyoto-u.ac.jp/WAT/my-en-data/wat2020.my-en.zip
|
||||
unzip wat2020.my-en.zip
|
||||
|
||||
# FLORES
|
||||
# download from: https://github.com/facebookresearch/flores
|
||||
|
||||
# TED - need to detokenize with Moses!
|
||||
# from: https://github.com/neulab/word-embeddings-for-nmt
|
||||
wget http://phontron.com/data/ted_talks.tar.gz
|
||||
|
||||
# Autshumato
|
||||
# request to download: https://repo.sadilar.org/handle/20.500.12185/397
|
||||
|
||||
# Tatoeba Challenge
|
||||
# available here: https://github.com/Helsinki-NLP/Tatoeba-Challenge
|
||||
```
|
||||
|
||||
1. **Training Data**
|
||||
|
||||
To produce the training data, we use a combination of [CCMatrix](https://arxiv.org/abs/1911.04944) and [CCAligned](https://arxiv.org/abs/1911.06154). Check out the instructions [here](https://github.com/facebookresearch/LASER/tree/master/tasks/CCMatrix) to download the raw data.
|
||||
|
||||
2. **Preprocess Data**
|
||||
|
||||
After downloading raw data, you will need to postprocess the data, then apply SPM, then binarize. Note that it is very important you run the postprocessing script, because this removes any instance of the evaluation data in the mined training data.
|
||||
|
||||
```bash
|
||||
# preprocess data
|
||||
|
||||
# remove sentences with more than 50% punctuation
|
||||
python /path/to/fairseq/examples/m2m_100/process_data/remove_too_much_punc.py
|
||||
|
||||
# deduplicate training data
|
||||
paste /path/to/datadir/train.$src /path/to/datadir/train.$tgt | awk '!x[$0]++' > /path/to/datadir/train.dedup
|
||||
echo "keeping $(wc -l /path/to/datadir/train.dedup) bitext out of $(wc -l /path/to/datadir/train.$src)"
|
||||
cut -f1 /path/to/datadir/train.dedup > /path/to/datadir/train.$src
|
||||
cut -f2 /path/to/datadir/train.dedup > /path/to/datadir/train.$tgt
|
||||
|
||||
# remove all instances of evaluation data from the training data
|
||||
python /path/to/fairseq/examples/m2m_100/process_data/dedup_data.py
|
||||
|
||||
# frequency cleaning
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/histograms.tar.gz
|
||||
tar -xvzf histograms.tar.gz
|
||||
python /path/to/fairseq/examples/m2m_100/process_data/clean_histogram.py --src $src --tgt $tgt --src-file /path/to/source/file --tgt-file /path/to/output/file --src-output-file source_output.$src --tgt-output-file target_output.$tgt --histograms /path/to/histograms
|
||||
|
||||
# apply SPM
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/spm.128k.model
|
||||
python /path/to/fairseq/scripts/spm_encode.py \
|
||||
--model spm.128k.model \
|
||||
--output_format=piece \
|
||||
--inputs=/path/to/input/file/here \
|
||||
--outputs=/path/to/output/file/here
|
||||
|
||||
# length ratio cleaning
|
||||
perl mosesdecoder/scripts/training/clean-corpus-n.perl --ratio 3 /path/to/training/data/train.spm.$src-$tgt $src $tgt /path/to/output/directory/train.spm.$src-$tgt 1 250
|
||||
|
||||
# binarize data
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/data_dict.128k.txt
|
||||
fairseq-preprocess \
|
||||
--source-lang $src --target-lang $tgt \
|
||||
--testpref spm.$src.$tgt \
|
||||
--thresholdsrc 0 --thresholdtgt 0 \
|
||||
--destdir data_bin \
|
||||
--srcdict data_dict.128k.txt --tgtdict data_dict.128k.txt
|
||||
```
|
||||
|
||||
3. **Training Scripts**
|
||||
|
||||
To reproduce the training of our models, we train with fairseq-py's multilingual translation [task](https://github.com/pytorch/fairseq/tree/master/examples/multilingual). If you are interested in model parallel training, also check out [fairscale](https://github.com/facebookresearch/fairscale).
|
||||
|
||||
4. **Generation**
|
||||
|
||||
To generate from our models, follow the the commands in the generation section below.
|
||||
|
||||
|
||||
If you use any of the resources listed here, please cite:
|
||||
```bibtex
|
||||
@article{fan2020beyond,
|
||||
title={Beyond English-Centric Multilingual Machine Translation},
|
||||
author={Fan, Angela and Bhosale, Shruti and Schwenk, Holger and Ma, Zhiyi and El-Kishky, Ahmed and Goyal, Siddharth and Baines, Mandeep and Celebi, Onur and Wenzek, Guillaume and Chaudhary, Vishrav and Goyal, Naman and Birch, Tom and Liptchinsky, Vitaliy and Edunov, Sergey and Grave, Edouard and Auli, Michael and Joulin, Armand},
|
||||
journal={arXiv preprint},
|
||||
year={2020}
|
||||
}
|
||||
|
||||
@article{schwenk2019ccmatrix,
|
||||
title={Ccmatrix: Mining billions of high-quality parallel sentences on the web},
|
||||
author={Schwenk, Holger and Wenzek, Guillaume and Edunov, Sergey and Grave, Edouard and Joulin, Armand},
|
||||
journal={arXiv preprint arXiv:1911.04944},
|
||||
year={2019}
|
||||
}
|
||||
|
||||
@article{el2019massive,
|
||||
title={A Massive Collection of Cross-Lingual Web-Document Pairs},
|
||||
author={El-Kishky, Ahmed and Chaudhary, Vishrav and Guzman, Francisco and Koehn, Philipp},
|
||||
journal={arXiv preprint arXiv:1911.06154},
|
||||
year={2019}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Trained Models
|
||||
|
||||
### 418M and 1.2B Model
|
||||
We include the last checkpoint for both of these models.
|
||||
|
||||
```bash
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/model_dict.128k.txt
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/language_pairs_small_models.txt
|
||||
|
||||
# 418M parameter model
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/418M_last_checkpoint.pt
|
||||
|
||||
# 1.2B parameter model
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/1.2B_last_checkpoint.pt
|
||||
|
||||
# Generation:
|
||||
fairseq-generate $binarized_data_path --batch-size 32 --path $path_to_model -s en -t fr --remove-bpe 'sentencepiece' --beam 5 --task translation_multi_simple_epoch --lang-pairs language_pairs_small_models --decoder-langtok --encoder-langtok src --gen-subset test > gen_out
|
||||
```
|
||||
|
||||
### 12B Model
|
||||
12B parameter model trained on many-to-many training data for 100 languages. We include the last checkpoint, average of last 5 checkpoints, average of last 10 checkpoints. There isn't a universally best choice out of these three, but all three versions are pretty close in accuracy. You can either sweep over the 3 checkpoints on a dev test and use the best performing checkpoint for final testing. Or the last checkpoint can be a good default choice.
|
||||
|
||||
**Model Download Links**
|
||||
Configuration | 2 32GB GPUs | 4 16GB GPUs | 6 12GB GPUs | 8 8GB GPUs
|
||||
:--|:--|:--|:--|:--
|
||||
Last Checkpoint | [12b_last_chk_2_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_last_chk_2_gpus.pt) | [12b_last_chk_4_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_last_chk_4_gpus.pt) | [12b_last_chk_6_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_last_chk_6_gpus.pt) | [12b_last_chk_8_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_last_chk_8_gpus.pt)
|
||||
Average of last 5 checkpoints | [12b_avg5_chk_2_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg5_chk_2_gpus.pt) | [12b_avg5_chk_4_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg5_chk_4_gpus.pt) | [12b_avg5_chk_6_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg5_chk_6_gpus.pt) | [12b_avg5_chk_8_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg5_chk_8_gpus.pt)
|
||||
Average of last 10 checkpoints | [12b_avg10_chk_2_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg10_chk_2_gpus.pt) | [12b_avg10_chk_4_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg10_chk_4_gpus.pt) | [12b_avg10_chk_6_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg10_chk_6_gpus.pt) | [12b_avg10_chk_8_gpus.pt](https://dl.fbaipublicfiles.com/m2m_100/12b_avg10_chk_8_gpus.pt)
|
||||
|
||||
**Generation Arguments**
|
||||
Configuration | 2 32GB GPUs | 4 16GB GPUs | 6 12GB GPUs | 8 8GB GPUs
|
||||
:--|:--|:--|:--|:--
|
||||
`--pipeline-encoder-balance` | `[26]` | `[1,15,10]` | `[1,9,9,7]` | `[1,6,6,6,7]`
|
||||
`--pipeline-encoder-devices` | `[0]` | `[0,1,0]` | `[0,1,2,0]` | `[0,4,5,1,0]`
|
||||
`--pipeline-decoder-balance` | `[3,22,1]` | `[3,11,11,1]` | `[3,7,7,8,1]` | `[1,6,6,6,6,1]`
|
||||
`--pipeline-decoder-devices` | `[0,1,0]` | `[0,2,3,0]` | `[0,3,4,5,0]` | `[0,2,6,7,3,0]`
|
||||
|
||||
|
||||
## SentencePiece Model
|
||||
|
||||
```bash
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/spm.128k.model
|
||||
```
|
||||
|
||||
## Generation with M2M-100
|
||||
|
||||
### Encode using our SentencePiece Model
|
||||
|
||||
Note: Install SentencePiece from [here](https://github.com/google/sentencepiece)
|
||||
|
||||
```bash
|
||||
fairseq=/path/to/fairseq
|
||||
cd $fairseq
|
||||
sacrebleu --echo src -l de-fr -t wmt19 | head -n 20 > raw_input.de-fr.de
|
||||
sacrebleu --echo ref -l de-fr -t wmt19 | head -n 20 > raw_input.de-fr.fr
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/spm.128k.model
|
||||
for lang in de fr ; do
|
||||
python scripts/spm_encode.py \
|
||||
--model spm.128k.model \
|
||||
--output_format=piece \
|
||||
--inputs=raw_input.de-fr.${lang} \
|
||||
--outputs=spm.de-fr.${lang}
|
||||
done
|
||||
```
|
||||
|
||||
### Binarization
|
||||
|
||||
```bash
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/data_dict.128k.txt
|
||||
fairseq-preprocess \
|
||||
--source-lang de --target-lang fr \
|
||||
--testpref spm.de-fr \
|
||||
--thresholdsrc 0 --thresholdtgt 0 \
|
||||
--destdir data_bin \
|
||||
--srcdict data_dict.128k.txt --tgtdict data_dict.128k.txt
|
||||
```
|
||||
|
||||
### Generation for the 12B model
|
||||
|
||||
Note that generation can currently be run using 2 32GB / 4 16GB / 6 12GB / 8 8GB GPUs, and the corresponding model checkpoints and pipeline arguments can be found in the [12B Model Section](#12b-model).
|
||||
Generation on CPUs will be added in the future.
|
||||
|
||||
```bash
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/model_dict.128k.txt
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/language_pairs.txt
|
||||
wget https://dl.fbaipublicfiles.com/m2m_100/12b_last_chk_4_gpus.pt
|
||||
fairseq-generate \
|
||||
data_bin \
|
||||
--batch-size 1 \
|
||||
--path 12b_last_chk_4_gpus.pt \
|
||||
--fixed-dictionary model_dict.128k.txt \
|
||||
-s de -t fr \
|
||||
--remove-bpe 'sentencepiece' \
|
||||
--beam 5 \
|
||||
--task translation_multi_simple_epoch \
|
||||
--lang-pairs language_pairs.txt \
|
||||
--decoder-langtok --encoder-langtok src \
|
||||
--gen-subset test \
|
||||
--fp16 \
|
||||
--dataset-impl mmap \
|
||||
--distributed-world-size 1 --distributed-no-spawn \
|
||||
--pipeline-model-parallel \
|
||||
--pipeline-chunks 1 \
|
||||
--pipeline-encoder-balance '[1,15,10]' \
|
||||
--pipeline-encoder-devices '[0,1,0]' \
|
||||
--pipeline-decoder-balance '[3,11,11,1]' \
|
||||
--pipeline-decoder-devices '[0,2,3,0]' > gen_out
|
||||
```
|
||||
## Evaluation with M2M-100
|
||||
|
||||
### Tokenization
|
||||
|
||||
Note: Refer to tokenizers/README.md for more details on tokenization.
|
||||
|
||||
```bash
|
||||
cd ${fairseq}/examples/m2m_100
|
||||
cat ${fairseq}/gen_out | grep -P "^H" | sort -V | cut -f 3- | sh tok.sh fr > hyp
|
||||
cat ${fairseq}/raw_input.de-fr.fr | sh tok.sh fr > ref
|
||||
```
|
||||
|
||||
### BLEU
|
||||
|
||||
```bash
|
||||
sacrebleu -tok 'none' ref < hyp
|
||||
```
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
|
||||
CWD=`pwd`
|
||||
INSTALL_PATH=$CWD/tokenizers/thirdparty
|
||||
|
||||
MOSES=$INSTALL_PATH/mosesdecoder
|
||||
if [ ! -d $MOSES ]; then
|
||||
echo 'Cloning Moses github repository (for tokenization scripts)...'
|
||||
git clone https://github.com/moses-smt/mosesdecoder.git $MOSES
|
||||
cd $MOSES
|
||||
# To deal with differences in handling ' vs "
|
||||
git checkout 03578921cc1a03402
|
||||
cd -
|
||||
fi
|
||||
|
||||
WMT16_SCRIPTS=$INSTALL_PATH/wmt16-scripts
|
||||
if [ ! -d $WMT16_SCRIPTS ]; then
|
||||
echo 'Cloning Romanian tokenization scripts'
|
||||
git clone https://github.com/rsennrich/wmt16-scripts.git $WMT16_SCRIPTS
|
||||
fi
|
||||
|
||||
KYTEA=$INSTALL_PATH/kytea
|
||||
if [ ! -f $KYTEA/bin/kytea ]; then
|
||||
git clone https://github.com/neubig/kytea.git $KYTEA
|
||||
cd $KYTEA
|
||||
autoreconf -i
|
||||
./configure --prefix=`pwd`
|
||||
make
|
||||
make install
|
||||
cd ..
|
||||
fi
|
||||
|
||||
export MECAB=$INSTALL_PATH/mecab-0.996-ko-0.9.2
|
||||
if [ ! -f $MECAB/bin/mecab ]; then
|
||||
cd $INSTALL_PATH
|
||||
curl -LO https://bitbucket.org/eunjeon/mecab-ko/downloads/mecab-0.996-ko-0.9.2.tar.gz
|
||||
tar zxfv mecab-0.996-ko-0.9.2.tar.gz
|
||||
cd mecab-0.996-ko-0.9.2/
|
||||
./configure --prefix=`pwd`
|
||||
make
|
||||
make install
|
||||
|
||||
cd ..
|
||||
curl -LO https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-2.1.1-20180720.tar.gz
|
||||
tar zxfv mecab-ko-dic-2.1.1-20180720.tar.gz
|
||||
cd mecab-ko-dic-2.1.1-20180720/
|
||||
./autogen.sh
|
||||
./configure --prefix=`pwd` --with-dicdir=$MECAB/lib/mecab/dic/mecab-ko-dic --with-mecab-config=$MECAB/bin/mecab-config
|
||||
make
|
||||
sh -c 'echo "dicdir=$MECAB/lib/mecab/dic/mecab-ko-dic" > $MECAB/etc/mecabrc'
|
||||
make install
|
||||
cd $CWD
|
||||
fi
|
||||
|
||||
INDIC_RESOURCES_PATH=$INSTALL_PATH/indic_nlp_resources
|
||||
if [ ! -d $INDIC_RESOURCES_PATH ]; then
|
||||
echo 'Cloning indic_nlp_resources'
|
||||
git clone https://github.com/anoopkunchukuttan/indic_nlp_resources.git $INDIC_RESOURCES_PATH
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -f $INSTALL_PATH/seg_my.py ]; then
|
||||
cd $INSTALL_PATH
|
||||
wget http://lotus.kuee.kyoto-u.ac.jp/WAT/my-en-data/wat2020.my-en.zip
|
||||
unzip wat2020.my-en.zip
|
||||
# switch to python3
|
||||
cat wat2020.my-en/myseg.py |sed 's/^sys.std/###sys.std/g' | sed 's/### sys/sys/g' | sed 's/unichr/chr/g' > seg_my.py
|
||||
cd $CWD
|
||||
fi
|
||||
|
||||
|
||||
pip install pythainlp sacrebleu indic-nlp-library
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--src', type=str, help='Source language')
|
||||
parser.add_argument('--tgt', type=str, help='Target language')
|
||||
parser.add_argument('--src-file', type=str, help='Input source file')
|
||||
parser.add_argument('--tgt-file', type=str, help='Input target file')
|
||||
parser.add_argument('--src-output-file', type=str, help='Output source file')
|
||||
parser.add_argument('--tgt-output-file', type=str, help='Output target file')
|
||||
parser.add_argument('--threshold', type=float, default=0.5, help='Threshold')
|
||||
parser.add_argument('--threshold-character', type=str, default=']', help='Threshold character')
|
||||
parser.add_argument('--histograms', type=str, help='Path to histograms')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
def read_hist(f):
|
||||
ch = []
|
||||
for line in f:
|
||||
c = line[0]
|
||||
if c == args.threshold_character:
|
||||
break
|
||||
ch.append(c)
|
||||
return ch
|
||||
|
||||
|
||||
with(open("{}/{}".format(args.histograms, args.src), 'r', encoding='utf8')) as f:
|
||||
ch1 = read_hist(f)
|
||||
|
||||
with(open("{}/{}".format(args.histograms, args.tgt), 'r', encoding='utf8')) as f:
|
||||
ch2 = read_hist(f)
|
||||
|
||||
print("Accepted characters for {}: {}".format(args.src, ch1))
|
||||
print("Accepted characters for {}: {}".format(args.tgt, ch2))
|
||||
|
||||
with open(args.src_file, 'r', encoding='utf8') as fs1, open(args.tgt_file, 'r', encoding='utf8') as fs2, open(args.src_output_file, 'w', encoding='utf8') as fos1, open(args.tgt_output_file, 'w', encoding='utf8') as fos2:
|
||||
ls1 = fs1.readline()
|
||||
ls2 = fs2.readline()
|
||||
|
||||
while ls1 or ls2:
|
||||
cnt1 = len([c for c in ls1.strip() if c in ch1])
|
||||
cnt2 = len([c for c in ls2.strip() if c in ch2])
|
||||
|
||||
if cnt1 / len(ls1) > args.threshold and cnt2 / len(ls2) > args.threshold:
|
||||
fos1.write(ls1)
|
||||
fos2.write(ls2)
|
||||
else:
|
||||
print("{} {} {} \n{} {} {}".format(args.src, cnt1 / len(ls1), ls1.strip(), args.tgt, cnt2 / len(ls2), ls2.strip()))
|
||||
|
||||
ls1 = fs1.readline()
|
||||
ls2 = fs2.readline()
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
import os
|
||||
|
||||
DATADIR = "/path/to/train_data"
|
||||
DEDUP_FROM_DIR = "/path/to/eval/data"
|
||||
OUTPUT_DIR = "/path/to/output/data"
|
||||
|
||||
|
||||
def main(args):
|
||||
languages = set()
|
||||
for language_directory in os.listdir(DATADIR):
|
||||
if "_" in language_directory:
|
||||
src, tgt = language_directory.split("_")
|
||||
languages.add(LanguagePair(src=src, tgt=tgt))
|
||||
|
||||
data = existing_data()
|
||||
train_languages = sorted(languages)
|
||||
for language_pair in train_languages[args.start_index:args.start_index + args.size]:
|
||||
print(language_pair)
|
||||
dedup(language_pair, data)
|
||||
|
||||
|
||||
LanguagePair = namedtuple("LanguagePair", ["src", "tgt"])
|
||||
|
||||
|
||||
def existing_data():
|
||||
data = set()
|
||||
for file in os.listdir(DEDUP_FROM_DIR):
|
||||
with open(os.path.join(DEDUP_FROM_DIR, file)) as f:
|
||||
data |= set(f.readlines())
|
||||
return data
|
||||
|
||||
def dedup(language_pair, data, verbose=True, output=True):
|
||||
train_filenames = LanguagePair(
|
||||
src=f"{DATADIR}/{language_pair.src}_{language_pair.tgt}/train.{language_pair.src}",
|
||||
tgt=f"{DATADIR}/{language_pair.src}_{language_pair.tgt}/train.{language_pair.tgt}",
|
||||
)
|
||||
|
||||
output_filenames = LanguagePair(
|
||||
src=f"{OUTPUT_DIR}/train.dedup.{language_pair.src}-{language_pair.tgt}.{language_pair.src}",
|
||||
tgt=f"{OUTPUT_DIR}/train.dedup.{language_pair.src}-{language_pair.tgt}.{language_pair.tgt}"
|
||||
)
|
||||
|
||||
# If output exists, skip this pair. It has already been done.
|
||||
if (os.path.exists(output_filenames.src) and
|
||||
os.path.exists(output_filenames.tgt)):
|
||||
if verbose:
|
||||
print(f"{language_pair.src}-{language_pair.tgt} already done.")
|
||||
return
|
||||
|
||||
if verbose:
|
||||
print(f"{language_pair.src}-{language_pair.tgt} ready, will check dups.")
|
||||
|
||||
# If there is no output, no need to actually do the loop.
|
||||
if not output:
|
||||
return
|
||||
|
||||
if os.path.exists(train_filenames.src) and os.path.exists(train_filenames.tgt):
|
||||
with open(train_filenames.src) as f:
|
||||
train_source = f.readlines()
|
||||
|
||||
with open(train_filenames.tgt) as f:
|
||||
train_target = f.readlines()
|
||||
|
||||
# do dedup
|
||||
new_train_source = []
|
||||
new_train_target = []
|
||||
for i, train_line in enumerate(train_source):
|
||||
if train_line not in data and train_target[i] not in data:
|
||||
new_train_source.append(train_line)
|
||||
new_train_target.append(train_target[i])
|
||||
|
||||
assert len(train_source) == len(train_target)
|
||||
assert len(new_train_source) == len(new_train_target)
|
||||
assert len(new_train_source) <= len(train_source)
|
||||
|
||||
with open(output_filenames.src, "w") as o:
|
||||
for line in new_train_source:
|
||||
o.write(line)
|
||||
|
||||
with open(output_filenames.tgt, "w") as o:
|
||||
for line in new_train_target:
|
||||
o.write(line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-s", "--start-index", required=True, type=int)
|
||||
parser.add_argument("-n", "--size", required=True, type=int)
|
||||
main(parser.parse_args())
|
||||
@@ -0,0 +1,36 @@
|
||||
import gzip
|
||||
import argparse
|
||||
from string import punctuation
|
||||
|
||||
def len_no_punc(s, punc):
|
||||
return len([ch for ch in s if ch in punc])
|
||||
|
||||
def filter_overpunc(len_npunc, len_sen):
|
||||
return len_npunc < 0.5*len_sen
|
||||
|
||||
def main(args):
|
||||
punc = punctuation + "—|–"
|
||||
print('Processing file {}'.format(args.input))
|
||||
with gzip.open(args.input, 'rt', encoding=args.encoding) as tsv:
|
||||
with open(args.bitext + '.' + args.src_lang, 'wt', encoding=args.encoding) as fsrc:
|
||||
with open(args.bitext + '.' + args.tgt_lang, 'wt', encoding=args.encoding) as ftgt:
|
||||
line = tsv.readline()
|
||||
fields = line.split('\t')
|
||||
|
||||
src, tgt = fields[1], fields[2]
|
||||
|
||||
nchar_npunc_src = len_no_punc(src, punc)
|
||||
nchar_npunc_tgt = len_no_punc(tgt, punc)
|
||||
|
||||
if filter_overpunc(nchar_npunc_src, len(src)) and filter_overpunc(nchar_npunc_tgt, len(tgt)):
|
||||
fsrc.write(src.strip() + '\n')
|
||||
ftgt.write(tgt.strip() + '\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--input", required=True, type=str)
|
||||
parser.add_argument('--encoding', default='utf-8', help='character encoding for input/output')
|
||||
parser.add_argument('--bitext', type=str, required=True, help='language direction')
|
||||
parser.add_argument('--src-lang', type=str, required=True, help='Source language')
|
||||
parser.add_argument('--tgt-lang', type=str, required=True, help='Target language')
|
||||
main(parser.parse_args())
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2019-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
TOKENIZERS_SCRIPTS=tokenizers
|
||||
INSTALL_PATH=$TOKENIZERS_SCRIPTS/thirdparty
|
||||
|
||||
N_THREADS=8
|
||||
|
||||
lg=$1
|
||||
|
||||
MOSES=$INSTALL_PATH/mosesdecoder
|
||||
REPLACE_UNICODE_PUNCT=$MOSES/scripts/tokenizer/replace-unicode-punctuation.perl
|
||||
NORM_PUNC=$MOSES/scripts/tokenizer/normalize-punctuation.perl
|
||||
REM_NON_PRINT_CHAR=$MOSES/scripts/tokenizer/remove-non-printing-char.perl
|
||||
TOKENIZER=$MOSES/scripts/tokenizer/tokenizer.perl
|
||||
|
||||
# special tokenization for Romanian
|
||||
WMT16_SCRIPTS=$INSTALL_PATH/wmt16-scripts
|
||||
|
||||
NORMALIZE_ROMANIAN=$WMT16_SCRIPTS/preprocess/normalise-romanian.py
|
||||
REMOVE_DIACRITICS=$WMT16_SCRIPTS/preprocess/remove-diacritics.py
|
||||
|
||||
# Burmese
|
||||
MY_SEGMENT=$INSTALL_PATH/seg_my.py
|
||||
|
||||
# Arabic
|
||||
AR_TOKENIZER=$TOKENIZERS_SCRIPTS/tokenizer_ar.sh
|
||||
|
||||
# Korean
|
||||
KO_SEGMENT=$TOKENIZERS_SCRIPTS/seg_ko.sh
|
||||
|
||||
# Japanese
|
||||
JA_SEGMENT=$TOKENIZERS_SCRIPTS/seg_ja.sh
|
||||
|
||||
# Indic
|
||||
IN_TOKENIZER=$TOKENIZERS_SCRIPTS/tokenize_indic.py
|
||||
INDIC_RESOURCES_PATH=$INSTALL_PATH/indic_nlp_resources
|
||||
|
||||
# Thai
|
||||
THAI_TOKENIZER=$TOKENIZERS_SCRIPTS/tokenize_thai.py
|
||||
|
||||
# Chinese
|
||||
CHINESE_TOKENIZER=$TOKENIZERS_SCRIPTS/tokenize_zh.py
|
||||
|
||||
# Chinese
|
||||
if [ "$lg" = "zh" ]; then
|
||||
cat - | $REPLACE_UNICODE_PUNCT | $NORM_PUNC -l $lg | $REM_NON_PRINT_CHAR | python $CHINESE_TOKENIZER
|
||||
# Thai
|
||||
elif [ "$lg" = "th" ]; then
|
||||
cat - | python $THAI_TOKENIZER
|
||||
# Japanese
|
||||
elif [ "$lg" = "ja" ]; then
|
||||
cat - | $REPLACE_UNICODE_PUNCT | $NORM_PUNC -l $lg | $REM_NON_PRINT_CHAR | ${JA_SEGMENT}
|
||||
# Korean
|
||||
elif [ "$lg" = "ko" ]; then
|
||||
cat - | $REM_NON_PRINT_CHAR | ${KO_SEGMENT}
|
||||
# Romanian
|
||||
elif [ "$lg" = "ro" ]; then
|
||||
cat - | $REPLACE_UNICODE_PUNCT | $NORM_PUNC -l $lg | $REM_NON_PRINT_CHAR | $NORMALIZE_ROMANIAN | $REMOVE_DIACRITICS | $TOKENIZER -no-escape -threads $N_THREADS -l $lg
|
||||
# Burmese
|
||||
elif [ "$lg" = "my" ]; then
|
||||
cat - | python ${MY_SEGMENT}
|
||||
# Arabic
|
||||
elif [ "$lg" = "ar" ]; then
|
||||
cat - | ${AR_TOKENIZER}
|
||||
# Indic
|
||||
elif [ "$lg" = "ne" ]; then
|
||||
cat - | python ${IN_TOKENIZER} $lg
|
||||
elif [ "$lg" = "si" ]; then
|
||||
cat - | python ${IN_TOKENIZER} $lg
|
||||
elif [ "$lg" = "hi" ]; then
|
||||
cat - | python ${IN_TOKENIZER} $lg
|
||||
# other languages
|
||||
else
|
||||
cat - | $REPLACE_UNICODE_PUNCT | $NORM_PUNC -l $lg | $REM_NON_PRINT_CHAR | $TOKENIZER -no-escape -threads $N_THREADS -l $lg
|
||||
fi
|
||||
@@ -0,0 +1,18 @@
|
||||
# M2M-100 Tokenization
|
||||
|
||||
We apply different tokenization strategies for different languages following the existing literature. Here we provide tok.sh a tokenizer that can be used to reproduce our results.
|
||||
|
||||
To reproduce the results, follow these steps:
|
||||
|
||||
```
|
||||
tgt_lang=...
|
||||
reference_translation=...
|
||||
cat generation_output | grep -P "^H" | sort -V | cut -f 3- | sh tok.sh $tgt_lang > hyp
|
||||
cat $reference_translation |sh tok.sh $tgt_lang > ref
|
||||
sacrebleu -tok 'none' ref < hyp
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Tools needed for all the languages except Arabic can be installed by running install_dependencies.sh
|
||||
If you want to evaluate Arabic models, please follow the instructions provided here: http://alt.qcri.org/tools/arabic-normalizer/ to install
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
SCRIPT=`realpath $0`
|
||||
KYTEA=`dirname $SCRIPT`/thirdparty/kytea
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$KYTEA/lib:/usr/local/lib
|
||||
export PATH=$PATH:"$KYTEA/bin"
|
||||
|
||||
cat - | tr -d "[:blank:]" | kytea -notags
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
SCRIPT=`realpath $0`
|
||||
MECAB=`dirname $SCRIPT`/thirdparty/mecab-0.996-ko-0.9.2
|
||||
|
||||
export PATH=$PATH:"$MECAB/bin":"$MECAB/lib"
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$MECAB/lib"
|
||||
|
||||
cat - | mecab -O wakati
|
||||
@@ -0,0 +1,12 @@
|
||||
seg_my.py
|
||||
indic_nlp_library/
|
||||
indic_nlp_resources/
|
||||
kytea/
|
||||
mecab-0.996-ko-0.9.2.tar.gz
|
||||
mecab-0.996-ko-0.9.2/
|
||||
mosesdecoder/
|
||||
wat2020.my-en.zip
|
||||
wat2020.my-en/
|
||||
wmt16-scripts/
|
||||
mecab-ko-dic-2.1.1-20180720/
|
||||
mecab-ko-dic-2.1.1-20180720.tar.gz
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
# Use: echo {text} | python tokenize_indic.py {language}
|
||||
|
||||
import sys
|
||||
|
||||
from indicnlp.normalize.indic_normalize import IndicNormalizerFactory
|
||||
from indicnlp.tokenize.indic_tokenize import trivial_tokenize
|
||||
|
||||
|
||||
factory = IndicNormalizerFactory()
|
||||
normalizer = factory.get_normalizer(
|
||||
sys.argv[1], remove_nuktas=False, nasals_mode="do_nothing"
|
||||
)
|
||||
|
||||
for line in sys.stdin:
|
||||
normalized_line = normalizer.normalize(line.strip())
|
||||
tokenized_line = " ".join(trivial_tokenize(normalized_line, sys.argv[1]))
|
||||
print(tokenized_line)
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import sys
|
||||
|
||||
from pythainlp import word_tokenize
|
||||
|
||||
|
||||
for line in sys.stdin:
|
||||
print(" ".join(word_tokenize(line.strip())))
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
|
||||
import fileinput
|
||||
|
||||
import sacrebleu
|
||||
|
||||
|
||||
for line in fileinput.input():
|
||||
print(sacrebleu.tokenize_zh(line))
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
#
|
||||
# Please follow the instructions here http://alt.qcri.org/tools/arabic-normalizer/
|
||||
# to install tools needed for Arabic
|
||||
|
||||
echo "Please install Arabic tools: http://alt.qcri.org/tools/arabic-normalizer/"
|
||||
echo "Then update environment variables in tokenizer_ar.sh"
|
||||
exit 1
|
||||
|
||||
SVMTOOL=...
|
||||
GOMOSESGO=...
|
||||
QCRI_ARABIC_NORMALIZER=...
|
||||
|
||||
export PERL5LIB="$SVMTOOL/lib":"$GOMOSESGO/bin/MADA-3.2":$PERL5LIB
|
||||
|
||||
|
||||
tempfile=$(mktemp)
|
||||
cat - > $tempfile
|
||||
|
||||
cd $QCRI_ARABIC_NORMALIZER
|
||||
|
||||
bash qcri_normalizer_mada3.2_aramorph1.2.1.sh $tempfile
|
||||
cat $tempfile.mada_norm-aramorph.europarl_tok
|
||||
Reference in New Issue
Block a user