Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preprocess & capability for custom dataset for mt #3269

Merged
merged 69 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
7d4b849
add preprocess for mt
FrostML Sep 14, 2022
6239eb6
refine the code
FrostML Sep 14, 2022
5da947f
dataset support
FrostML Sep 15, 2022
7b989c7
refine
FrostML Sep 15, 2022
ce87721
update
FrostML Sep 15, 2022
84ef304
refine code and add doc(wip)
FrostML Sep 15, 2022
9fee279
update
FrostML Sep 19, 2022
d3bf9dc
update doc
FrostML Sep 19, 2022
670d524
update doc
FrostML Sep 19, 2022
7cfe7b7
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Sep 19, 2022
c23eba3
copy right
FrostML Sep 20, 2022
d7aba2c
copy right
FrostML Sep 20, 2022
0bff658
cr
FrostML Sep 20, 2022
c28d9a9
update according to comments
FrostML Sep 23, 2022
bbc9ff4
add weight_decay
FrostML Sep 23, 2022
c457b34
add post-norm support and refine pad id support
FrostML Oct 24, 2022
700de43
merge from develop
FrostML Oct 24, 2022
f13c24b
compatibility for normalize_before
FrostML Oct 24, 2022
dfbc323
fix post-normalize
FrostML Oct 25, 2022
bf76715
update
FrostML Oct 25, 2022
8d8ca6d
Merge branch 'develop' into preprocess
FrostML Oct 25, 2022
4fad7cf
typo
FrostML Oct 27, 2022
999d487
delete useless
FrostML Oct 27, 2022
4261327
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Oct 27, 2022
6e0c88e
fix Criterion
FrostML Oct 27, 2022
0526aed
merge from develop and resolve conflict
FrostML Nov 1, 2022
ed651a0
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Nov 8, 2022
f3da891
update doc according to comments
FrostML Nov 8, 2022
179b0f8
Merge branch 'develop' into preprocess
FrostML Nov 10, 2022
0a228cb
Merge branch 'develop' into preprocess
FrostML Nov 15, 2022
dfb059b
Merge branch 'develop' into preprocess
FrostML Nov 23, 2022
ffa12d2
delete unk in export script
FrostML Nov 23, 2022
b49fe23
Merge branch 'develop' into preprocess
FrostML Nov 23, 2022
f7d87aa
Merge branch 'develop' into preprocess
FrostML Nov 23, 2022
45bddbf
update
FrostML Nov 23, 2022
118bba3
update
FrostML Nov 23, 2022
c5dd01f
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
9205c78
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
b091186
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
8b7dc96
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
4b7bbcf
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
df3ae4a
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
7b52031
Merge branch 'develop' into preprocess
FrostML Nov 24, 2022
09be1b5
update
FrostML Nov 25, 2022
f577360
Merge branch 'develop' into preprocess
FrostML Nov 25, 2022
52b9e8a
Merge branch 'develop' into preprocess
FrostML Nov 28, 2022
23afdbf
update according to comments
FrostML Nov 28, 2022
589610d
Merge branch 'preprocess' of https://github.com/FrostML/PaddleNLP int…
FrostML Nov 28, 2022
0e89386
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Nov 28, 2022
415e2a2
Merge branch 'develop' into preprocess
FrostML Nov 28, 2022
5908411
Merge branch 'develop' into preprocess
FrostML Nov 29, 2022
f4a1ee5
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Nov 29, 2022
7231b10
update codestyle
FrostML Nov 29, 2022
c98be33
Merge branch 'preprocess' of https://github.com/FrostML/PaddleNLP int…
FrostML Nov 29, 2022
4c563d5
update code style
FrostML Nov 29, 2022
40da1e8
Merge branch 'develop' into preprocess
FrostML Nov 30, 2022
7c46e86
Merge branch 'develop' into preprocess
FrostML Nov 30, 2022
c5125a2
merge from develop and resolve conflict
FrostML Nov 30, 2022
a809acf
Merge branch 'develop' into preprocess
FrostML Dec 1, 2022
fc9be90
add warning
FrostML Dec 1, 2022
8d6bf66
Merge branch 'preprocess' of https://github.com/FrostML/PaddleNLP int…
FrostML Dec 1, 2022
721f0a7
Merge branch 'develop' into preprocess
FrostML Dec 2, 2022
fcb9be6
Merge branch 'develop' into preprocess
FrostML Dec 2, 2022
4714f2b
Merge branch 'develop' into preprocess
FrostML Dec 2, 2022
aa46c22
Merge branch 'develop' into preprocess
FrostML Dec 5, 2022
639c422
Merge branch 'develop' into preprocess
FrostML Dec 6, 2022
67294e3
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Dec 6, 2022
057a20b
relative path
FrostML Dec 6, 2022
8f938d5
Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleNLP i…
FrostML Dec 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions examples/machine_translation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# 机器翻译

机器翻译(Machine Translation)是利用计算机将一种自然语言(源语言)转换为另一种自然语言(目标语言)的过程,输入为源语言句子,输出为相应的目标语言的句子。

## 快速开始

### 环境依赖

使用当前机器翻译示例,需要额外安装配置以下环境:

* attrdict
* pyyaml
* subword_nmt
* sacreBLEU
* fastBPE (可选,若不使用 preprocessor.py 的 bpe 分词功能可以不需要)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是不是通过requirements.txt来指定更合理?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不合适,作为可选依赖,不强制安装

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已新增 requirements.txt 将非可选依赖加入。THX

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我的意思并不是加入主干的requirements.txt, 而是examples/machine_translation/requirements.txt, 这样子用户可以直接pip install -r requirements.txt

Copy link
Contributor Author

@FrostML FrostML Nov 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,目前这样加上了


### 数据准备

数据准备部分分成两种模式,一种是使用 PaddleNLP 内置的已经处理好的 WMT14 EN-DE 翻译的数据集,另一种,提供了当前 Transformer demo 使用自定义数据集的方式。以下分别展开介绍。

#### 使用内置已经处理完成数据集

内置的处理好的数据集是基于公开的数据集:WMT 数据集。

WMT 翻译大赛是机器翻译领域最具权威的国际评测大赛,其中英德翻译任务提供了一个中等规模的数据集,这个数据集是较多论文中使用的数据集,也是 Transformer 论文中用到的一个数据集。我们也将 [WMT'14 EN-DE 数据集](http://www.statmt.org/wmt14/translation-task.html) 作为示例提供。

可以编写如下代码,即可自动载入处理好的上述的数据,对应的 WMT14 EN-DE 的数据集将会自动下载并且解压到 `~/.paddlenlp/datasets/WMT14ende/`。

``` python
datasets = load_dataset('wmt14ende', splits=('train', 'dev'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里还是之前paddlenlp中的load_dataset和Dataset吗,统一更新使用HF的load_dataset和Dataset吧。下面新增的那个LanguagePairDataset也是能支持这个吧,但是好像没看到LanguagePairDataset在这个PR里的使用。

另外针对LanguagePairDataset,同 @sijunhe 的comment #3698 (comment) ,也如之前沟通如果放在package里作为通用API的话不建议load language_pair直接返回一个wmt En-De BPE的数据集出来,或者是否可以通过类似glue的方式以name指定来实现返回特定的parallel corpora,这样算是对HF那些具体翻译数据集的更高一层接口,请 @sijunhe 再看看这个PR呢

Copy link
Contributor Author

@FrostML FrostML Nov 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当前 PR 已全部替换为 LanguagePairDataset,这个可以在 reader 看到。

这个和 glue 不一样,glue 指明了不同的 Dataset 能够返回出数据,这里指定任意的翻译方向还是得提供处理好的数据。返回 en-de 只是作为默认的返回结果,保证不会报错,可以置为空,抛出异常处理,只是这样会影响使用体验

替换内置数据集 Dataset 载入方式还是等 HF WMT Dataset 接入之后再替换文档吧,这里的 language pair 预期是用于自定义数据并且和前面的预处理连接,内置的 en-de 翻译 Dataset 还是用之前的。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个LanguagePairDataset API加上 logger.warning 说明 subject to change 吧

```

如果使用内置的处理好的数据,那到这里即可完成数据准备一步,可以直接移步 [Transformer 翻译模型](transformer/README.md) 将详细介绍如何使用内置的数据集训一个英德翻译的 Transformer 模型。

#### 使用自定义翻译数据集

本示例同时提供了自定义数据集的方法。可参考以下执行数据处理方式:

``` bash
# 数据下载、处理,包括 bpe 的训练
bash preprocessor/prepare-iwslt14.sh

# 数据预处理
DATA_DIR=preprocessor/iwslt14.tokenized.de-en/
python preprocessor/preprocessor.py \
--src_lang de \
--trg_lang en \
--train_pref $DATA_DIR/train \
--dev_pref $DATA_DIR/dev \
--test_pref $DATA_DIR/test \
--dest_dir data/iwslt14.tokenized.de-en/
```

`preprocessor/preprocessor.py` 支持了在机器翻译中常见的数据预处理方式,具体的参数说明如下:

* `--src_lang`(`-s`): 指明数据处理对应的源语言类型,比如 `de` 表示德语,`en` 表示英语,`fr` 表示法语等等。
* `--trg_lang`(`-t`): 指明数据处理对应的目标语言的类型,比如 `de` 表示德语,`en` 表示英语,`fr` 表示法语等等。
* `--train_pref`: 指明前序步骤中,下载的训练数据的路径,以及对应的文件名前缀,比如 `preprocessor/iwslt14.tokenized.de-en/train` 结合 `--src_lang de` 和 `--trg_lang en`,表示在 `preprocessor/iwslt14.tokenized.de-en/` 路径下,源语言是 `preprocessor/iwslt14.tokenized.de-en/train.de`,目标语言是 `preprocessor/iwslt14.tokenized.de-en/train.en`。
* `--dev_pref`: 指明前序步骤中,下载的验证数据的路径,以及对应的文件名前缀。在验证集语料中,如果有的 token 在训练集中从未出现过,那么将会被 `<unk>` 替换。
* `--test_pref`: 指明前序步骤中,下载的测试数据的路径,以及对应的文件名前缀。在测试集语料中,如果有的 token 在训练集中从未出现过,那么将会被 `<unk>` 替换。
* `--dest_dir`: 完成数据处理之后,保存处理完成数据以及词表的路径。
* `--threshold_src`: 在源语言中,出现频次小于 `--threshold_src` 指定的频次的 token 将会被替换成 `<unk>`。默认为 0,表示不会根据 token 出现的频次忽略 token 本身。
* `--threshold_trg`: 在目标语言中,出现频次小于 `--threshold_trg` 指定的频次的 token 将会被替换成 `<unk>`。默认为 0,表示不会根据 token 出现的频次忽略 token 本身。
* `--src_vocab`: 源语言词表,默认为 None,表示需要预处理步骤根据训练集语料重新生成一份词表。如果源语言与目标语言共用同一份词表,那么将使用 `--src_vocab` 指定的词表。
* `--trg_vocab`: 目标语言词表,默认为 None,表示需要预处理步骤根据训练集语料重新生成一份词表。如果源语言与目标语言共用同一份词表,那么将使用 `--src_vocab` 指定的词表。
* `--nwords_src`: 源语言词表最大的大小,不包括 special token。默认为 None,表示不限制。若源语言和目标语言共用同一份词表,那么将使用 `--nwords_src` 指定的大小。
* `--nwords_trg`: 目标语言词表最大的大小,不包括 special token。默认为 None,表示不限制。若源语言和目标语言共用同一份词表,那么将使用 `--nwords_src` 指定的大小。
* `--align_file`: 是否将平行语料文件整合成一个文件。
* `--joined_dictionary`: 源语言和目标语言是否使用同一份词表。若不共用同一份词表,无需指定。
* `--only_source`: 是否仅处理源语言。
* `--dict_only`: 是否仅处理词表。若指定,则仅完成词表处理。
* `--bos_token`: 指明翻译所用的 `bos_token`,表示一个句子开始。
* `--eos_token`: 指明翻译所用的 `eos_token`,表示一个句子的结束。
* `--pad_token`: 指明 `pad_token`,用于将一个 batch 内不同长度的句子 pad 到合适长度。
* `--unk_token`: 指明 `unk_token`,用于当一个 token 在词表中未曾出现的情况,将使用 `--unk_token` 指明的字符替换。
* `--apply_bpe`: 是否需要对数据作 bpe 分词。若指定则会在 preprocessor.py 脚本开始执行 bpe 分词。如果是使用提供的 shell 脚本完成的数据下载,则无需设置,在 shell 脚本中会作 bpe 分词处理。
* `--bpe_code`: 若指明 `--apply_bpe` 使用 bpe 分词,则需同时提供训练好的 bpe code 文件。

除了 iwslt14 德英翻译数据集外,我们也提供了其他的 shell 脚本完成数据下载处理,其中包括了 WMT14 英德翻译数据和 WMT14 英法翻译数据。

``` bash
# WMT14 英德翻译的数据下载、处理
bash prepare-wmt14en2de.sh
# WMT14 英法翻译的数据下载、处理
bash prepare-wmt14en2fr.sh
```

完成数据处理之后,同样也可以采用上文提到的预处理方式获取词表,完成预处理。以下再以 WMT14 EN-DE 翻译数据集预处理为例:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide to use WMT14 en-de as an example, why do we need to mention IWSLT14 de-en above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This should be fixed.


``` bash
# 数据下载、处理,包括 bpe 的训练
DATA_DIR=examples/translation/wmt14_en_de

# 数据预处理
python preprocessor/preprocessor.py \
--source_lang en \
--target_lang de \
--train_pref $DATA_DIR/train \
--dev_pref $DATA_DIR/dev \
--test_pref $DATA_DIR/test \
--dest_dir data/wmt17_en_de \
--thresholdtgt 0 \
--thresholdsrc 0 \
--joined_dictionary
```

如果有或者需要使用其他的平行语料,可以自行完成下载和简单的处理。

在下载部分,即在 shell 脚本中,处理需要用到 [mosesdecoder](https://github.com/moses-smt/mosesdecoder) 和 [subword-nmt](https://github.com/rsennrich/subword-nmt) 这两个工具。包括:

* 使用 `mosesdecoder/scripts/tokenizer/tokenizer.perl` 完成对词做一个初步的切分;
* 基于 `mosesdecoder/scripts/training/clean-corpus-n.perl` 完成数据的清洗;
* 使用 `subword-nmt/subword_nmt/learn_bpe.py` 完成 bpe 的学习;

此外,基于学到的 bpe code 进行分词的操作目前提供了两种选项,其一是,可以在以上的 shell 脚本中处理完成,使用以下的工具:

* 使用 `subword-nmt/subword_nmt/apply_bpe.py` 完成分词工作。

其二,也可以直接在后面的 `preprocessor/preprocessor.py` 脚本中,指明 `--apply_bpe` 完成分词操作。

而在预处理 `preprocessor/preprocessor.py` 脚本中,则提供词表构建,数据集文件整理,甚至于 bpe 分词的功能(bpe 分词过程可选)。最后获取的处理完成的 train,dev,test 数据可以直接用于后面 Transformer 模型的训练、评估和推理中。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preprocessor/preprocessor.py 支持了在机器翻译中常见的数据预处理方式

建议把preprocessor.py的功能放在上面对其介绍的地方,避免看了一堆到最后了才知道preprocessor.py能干啥

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


### 如何训一个翻译模型

前文介绍了如何快速开始完成翻译训练所需平行语料的准备,关于进一步的,模型训练、评估和推理部分,可以根据需要,参考对应的模型的文档:

* [Transformer 翻译模型](transformer/README.md)

## Acknowledge

我们借鉴了 facebookresearch 的 [fairseq](https://github.com/facebookresearch/fairseq) 在翻译数据的预处理上优秀的设计,在此对 fairseq 作者以及其开源社区表示感谢。
135 changes: 135 additions & 0 deletions examples/machine_translation/preprocessor/prepare-iwslt14.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env bash
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里所有的license都应该是2022

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DONE. THX.

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 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.
# Adapted from https://github.com/facebookresearch/MIXER/blob/master/prepareData.sh

cd preprocessor/

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

SCRIPTS=mosesdecoder/scripts
TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl
LC=$SCRIPTS/tokenizer/lowercase.perl
CLEAN=$SCRIPTS/training/clean-corpus-n.perl
BPEROOT=subword-nmt/subword_nmt
BPE_TOKENS=10000

URL="http://dl.fbaipublicfiles.com/fairseq/data/iwslt14/de-en.tgz"
GZ=de-en.tgz

if [ ! -d "$SCRIPTS" ]; then
echo "Please set SCRIPTS variable correctly to point to Moses scripts."
exit
fi

src=de
tgt=en
lang=de-en
prep=iwslt14.tokenized.de-en
tmp=$prep/tmp
origin=origin

mkdir -p $origin $tmp $prep

echo "Downloading data from ${URL}..."
cd $origin
wget "$URL"

if [ -f $GZ ]; then
echo "Data successfully downloaded."
else
echo "Data not successfully downloaded."
exit
fi

tar zxvf $GZ
cd ..

echo "pre-processing train data..."
for l in $src $tgt; do
f=train.tags.$lang.$l
tok=train.tags.$lang.tok.$l

cat $origin/$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

echo "pre-processing dev/test data..."
for l in $src $tgt; do
for o in `ls $origin/$lang/IWSLT14.TED*.$l.xml`; do
fname=${o##*/}
f=$tmp/${fname%.*}
echo $o $f
grep '<seg id' $o | \
sed -e 's/<seg id="[0-9]*">\s*//g' | \
sed -e 's/\s*<\/seg>\s*//g' | \
sed -e "s/\’/\'/g" | \
perl $TOKENIZER -threads 8 -l $l | \
perl $LC > $f
echo ""
done
done


echo "creating train, dev, test..."
for l in $src $tgt; do
awk '{if (NR%23 == 0) print $0; }' $tmp/train.tags.de-en.$l > $tmp/dev.$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

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 dev.$L test.$L; do
echo "apply_bpe.py to ${f}..."
python $BPEROOT/apply_bpe.py -c $BPE_CODE < $tmp/$f > $prep/$f
done
done

cd -
Loading