Skip to content

Commit

Permalink
Refine api docs and readme and fix baseline bug (#912)
Browse files Browse the repository at this point in the history
* fix unified transformer dtype problem

* fix win dtype bug

* Fix plato-2 and plato-mini dtype bug

* Fix plato-2 tokenization

* Refine some doc

* Add general k support for topk sampling

* fix seed

* minor fix

* Fix unitransformer readme

* topk kernel optimization

* add unimo model and fix generate api

* add 3 datasets for unimo-text

* fix tokenizer bug

* paddlenlp+lightseq sample

* Refine api docs and readme and fix baseline bug

* add shell srcipt

* remove lightseq samples

* minor fix

* minor fix

Co-authored-by: Jiaqi Liu <[email protected]>
Co-authored-by: liu zhengxi <[email protected]>
  • Loading branch information
3 people authored Aug 22, 2021
1 parent 9602532 commit fe8c2d6
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 110 deletions.
5 changes: 5 additions & 0 deletions docs/data_prepare/dataset_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ PaddleNLP提供了以下数据集的快速读取API,实际使用时请根据
| [HYP](https://pan.webis.de/semeval19/semeval19-web/) | 英文政治新闻情感分类语料 | `paddlenlp.datasets.load_dataset('hyp')` |

## 文本匹配
| 数据集名称 | 简介 | 调用方法 |
| ---- | --------- | ------ |
| [CAIL2019-SCM](https://github.com/china-ai-law-challenge/CAIL2019/tree/master/scm) | 相似法律案例匹配 | `paddlenlp.datasets.load_dataset('cail2019_scm')` |

## 序列标注
Expand Down Expand Up @@ -87,6 +89,9 @@ PaddleNLP提供了以下数据集的快速读取API,实际使用时请根据
| ---- | --------- | ------ |
| [Poetry](https://github.com/chinese-poetry/chinese-poetry) | 中文诗歌古典文集数据| `paddlenlp.datasets.load_dataset('poetry')`|
| [Couplet](https://github.com/v-zich/couplet-clean-dataset) | 中文对联数据集| `paddlenlp.datasets.load_dataset('couplet')`|
| [DuReaderQG](https://github.com/PaddlePaddle/Research/tree/master/NLP/DuReader-Robust-BASELINE) | 基于DuReader的问题生成数据集| `paddlenlp.datasets.load_dataset('dureader_qg')`|
| [AdvertiseGen](https://github.com/ZhihongShao/Planning-based-Hierarchical-Variational-Model) | 中文文案生成数据集| `paddlenlp.datasets.load_dataset('advertisegen')`|
| [LCSTS_new](https://aclanthology.org/D15-1229.pdf) | 中文摘要生成数据集| `paddlenlp.datasets.load_dataset('lcsts_new')`|

## 语料库

Expand Down
92 changes: 44 additions & 48 deletions examples/text_generation/unimo-text/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,50 @@
# 千言:面向事实一致性的生成评测比赛baseline
# 千言:面向事实一致性的生成评测比赛基线

## 比赛简介

自然语言生成旨在让机器能够像人一样使用自然语言进行表达和交互,它是人工智能领域重要的前沿课题,近年来受到学术界和工业界广泛关注。

随着神经网络生成模型特别是预训练语言模型的迅速发展,机器生成文本的可读性和流畅性不断提升。然而,自动生成的文本中依然经常出现不符合原文或背景的错误事实描述,这种生成的事实一致性问题是自然语言生成进行落地应用的主要障碍之一,并逐渐受到研究学者的关注。鉴于当前国内外关于事实一致性的生成评测比赛十分匮乏,为了促进自然语言生成的技术发展和实际应用,我们计划组织面向事实一致性的生成评测比赛。

在此比赛中,我们将提供三个对事实一致性有较高要求的生成任务,包括文案生成、摘要生成和问题生成。同时,在系统评价中,我们将结合文本流畅性和事实一致性两项指标综合评估参赛生成系统的水平。通过这样的任务设定和评价方式,此评测将有助于研究者和开发者更多关注自然语言生成的事实一致性难题,并为大家提供学术交流平台,从而进一步提升自然语言生成的研究水平,推动相关技术的应用发展。
[此比赛](https://aistudio.baidu.com/aistudio/competition/detail/105),我们将提供三个对事实一致性有较高要求的生成任务,包括文案生成、摘要生成和问题生成。同时,在系统评价中,我们将结合文本流畅性和事实一致性两项指标综合评估参赛生成系统的水平。通过这样的任务设定和评价方式,此评测将有助于研究者和开发者更多关注自然语言生成的事实一致性难题,并为大家提供学术交流平台,从而进一步提升自然语言生成的研究水平,推动相关技术的应用发展。

本比赛得到中国中文信息学会自然语言生成专业委员会(筹)支持,将在2021年11月7日首届中国自然语言生成大会(CCNLG-2021)召开评测研讨会,并在大会上对获奖团队颁奖。

## 模型简介
本次比赛提供的基线系统,基于百度提出的ERNIE-UNIMO统一模态预训练框架。在本次比赛的三个文本生成任务中,我们基于本基线使用的模型是UNIMO-text,是基于[ERNIE-UNIMO](https://arxiv.org/pdf/2012.15409.pdf)框架在文本数据上预训练得到模型。

## 快速开始

### 数据准备

比赛使用三个任务数据集测试参赛系统的生成能力,包括文案生成、摘要生成和问题生成
比赛使用三个任务数据集测试参赛系统的生成能力,包括文案生成(AdvertiseGen)、摘要生成(LCSTS_new)和问题生成(DuReaderQG)

- 文案生成根据结构化的商品信息生成合适的广告文案;
- 摘要生成是为输入文档生成简洁且包含关键信息的简洁文本;
- 问题生成则是根据给定段落以及答案生成适合的问题。

为了方便用户快速使用基线,PaddleNLP Dataset API内置了数据集,一键即可完成数据集加载,示例代码如下:

```python
from paddlenlp.datasets import load_dataset
train_ds, dev_ds = load_dataset('dureader_qg', splits=('train', 'dev'))
```

### 代码结构说明

以下是本项目主要代码结构及说明:

```text
.
├── run_gen.py # 模型finetune主程序入口
├── gen_utils.py # 定义参数及一些工具函数
├── scripts # 三个任务的基线训练脚本
└── README.md # 文档说明
```

### 模型训练

运行如下命令即可在样例训练集上进行finetune,并在样例验证集上进行验证
运行如下命令即可在样例训练集上进行finetune,并在样例验证集上进行验证。也可以使用./scripts目录下面的训练脚本分别启动三个任务的训练。

```shell
# GPU启动,参数`--gpus`指定训练所用的GPU卡号,可以是单卡,也可以多卡
Expand All @@ -47,9 +67,11 @@ python -m paddle.distributed.launch --gpus "0" --log_dir ./log run_gen.py \
--device=gpu
```

其中参数释义如下
关键参数释义如下
- `gpus` 指示了训练所用的GPU卡号。
- `dataset_name` 数据集名称,dureader_qg、advertisegen和lcsts_new分别对应问题生成、文案生成和摘要生成三个任务。
- `dataset_name` 数据集名称,`dureader_qg``advertisegen``lcsts_new`分别对应问题生成、文案生成和摘要生成三个任务。
- `train_file` 本地训练数据地址,数据格式必须与`dataset_name`所指数据集格式相同。
- `predict_file` 本地测试数据地址,数据格式必须与`dataset_name`所指数据集格式相同。
- `model_name_or_path` 指示了finetune使用的具体预训练模型,可以是PaddleNLP提供的预训练模型,或者是本地的预训练模型。如果使用本地的预训练模型,可以配置本地模型的目录地址,例如: ./checkpoints/model_xx/,目录中需包含paddle预训练模型model_state.pdparams。如果使用PaddleNLP提供的预训练模型,可以选择下面其中之一。

| PaddleNLP提供的预训练模型 |
Expand All @@ -68,6 +90,8 @@ python -m paddle.distributed.launch --gpus "0" --log_dir ./log run_gen.py \
- `warmup_propotion` 表示学习率逐渐升高到基础学习率(即上面配置的learning_rate)所需要的迭代数占总步数的比例,最早的使用可以参考[这篇论文](https://arxiv.org/pdf/1706.02677.pdf)
- `max_seq_len` 模型输入序列的最大长度。
- `max_target_len` 模型训练时标签的最大长度。
- `min_dec_len` 模型生成序列的最小长度。
- `max_dec_len` 模型生成序列的最大长度。
- `do_train` 是否进行训练。
- `do_predict` 是否进行预测,在验证集上会自动评估。
- `device` 表示使用的设备,从gpu和cpu中选择。
Expand All @@ -81,7 +105,6 @@ python -m paddle.distributed.launch --gpus "0" --log_dir ./log run_gen.py \
├── model_8000
│ ├── model_config.json
│ ├── model_state.pdparams
│ ├── spm.model
│ ├── tokenizer_config.json
│ └── vocab.txt
└── ...
Expand All @@ -91,54 +114,27 @@ python -m paddle.distributed.launch --gpus "0" --log_dir ./log run_gen.py \

### 模型预测

运行如下命令即可在样例测试集上进行测试
运行下方脚本可以使用训练好的模型进行预测。

```shell
export CUDA_VISIBLE_DEVICES=0
# GPU启动,预测仅支持单卡
python infer.py \
--model_name_or_path=./checkpoints/model_80000 \
--test_data_path=./datasets/test.txt \
--output_path=./predict.txt \
--logging_steps=500 \
--seed=2021 \
--batch_size=4 \
--min_dec_len=1 \
--max_dec_len=64 \
--num_samples=20 \
--decode_strategy=sampling \
--top_k=5 \
python run_gen.py \
--dataset_name=dureader_qg \
--model_name_or_path=your_model_path \
--logging_steps=100 \
--batch_size=16 \
--max_seq_len=512 \
--max_target_len=30 \
--do_predict \
--max_dec_len=20 \
--min_dec_len=3 \
--device=gpu
```

其中参数释义如下:
- `model_name_or_path` 指示了finetune使用的具体预训练模型,可以是PaddleNLP提供的预训练模型,或者是本地的预训练模型。如果使用本地的预训练模型,可以配置本地模型的目录地址,例如: ./checkpoints/model_xx/,目录中需包含paddle预训练模型model_state.pdparams。如果使用PaddleNLP提供的预训练模型,可以选择下面其中之一。

| PaddleNLP提供的预训练模型 |
|---------------------------------|
| unified_transformer-12L-cn |
| unified_transformer-12L-cn-luge |

- `test_data_path` 表示预测集文件路径。
- `output_path` 表示预测结果的保存路径。
- `logging_steps` 表示日志打印间隔。
- `seed` 表示随机数生成器的种子。
- `batch_size` 表示每次迭代**每张卡**上的样本数目。
- `min_dec_len` 表示预测生成的句子的最小长度。
- `max_dec_len` 表示预测生成的句子的最大长度。
- `num_samples` 表示每条样本生成的句子的数量。对于每条样本,模型会生成`num_samples`个句子,根据每个句子的概率得分进行排序,得分最高的句子作为最终的生成结果。
- `decode_strategy` 表示预测解码时采取的策略,可选"sampling"、"greedy_search"和"beam_search"之一。
- `top_k` 表示采用"sampling"解码策略时,token的概率按从大到小排序,生成的token只从前`top_k`个中进行采样。
- `device` 表示训练使用的设备。

参数详情和参数的默认值请参考`args.py`

程序运行结束后会将预测结果保存在`output_path`中。将预测结果准备成比赛官网要求的格式,提交评估即可得评估结果。

采用不同的模型在样例测试集上有如下结果
Finetuned baseline的模型在各任务验证集上有如下结果(指标为BLEU-4)

| model_name_or_path | F1 | BLEU1 / BLEU2 | DISTINCT1 / DISTINCT2 |
| model_name | LCSTS_new | DuReaderQG | AdvertiseGen |
| :-----------------------------: | :---: | :-----------: | :-------------------: |
| unified_transformer-12L-cn | 10.62 | 0.070 / 0.022 | 0.065 / 0.304 |
| unified_transformer-12L-cn-luge | 33.11 | 0.245 / 0.157 | 0.074 / 0.238 |
| ./checkpoints/model_80000 | 32.38 | 0.239 / 0.150 | 0.070 / 0.219 |
| finetuned unimo-text-1.0 | 18.82 | 39.78 | 10.03 |
2 changes: 1 addition & 1 deletion examples/text_generation/unimo-text/gen_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def convert_example(example,
add_start_token_for_decoding=True,
return_position_ids=True)

if 'target' in example:
if 'target' in example and example['target']:
tokenized_example['target'] = example['target']
return tokenized_example

Expand Down
24 changes: 17 additions & 7 deletions examples/text_generation/unimo-text/run_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
import paddle.nn as nn
import paddle.nn.functional as F
from paddlenlp.transformers import LinearDecayWithWarmup
from paddle.optimizer import AdamW, SGD
from paddlenlp.ops.optimizer import AdamwOptimizer
from paddle.optimizer import AdamW

from paddlenlp.datasets import load_dataset
from paddlenlp.transformers import UNIMOLMHeadModel, UNIMOTokenizer, BasicTokenizer
Expand All @@ -24,6 +23,8 @@ def parse_args():
parser = argparse.ArgumentParser(__doc__)
parser.add_argument('--dataset_name', type=str, default='dureader_qg', help='The name of the dataset to load.')
parser.add_argument('--model_name_or_path', type=str, default='unimo-text-1.0', help='The path or shortcut name of the pre-trained model.')
parser.add_argument("--train_file", type=str, required=False, default=None, help="Train data path.")
parser.add_argument("--predict_file", type=str, required=False, default=None, help="Predict data path.")
parser.add_argument('--save_dir', type=str, default='./checkpoints', help='The directory where the checkpoints will be saved.')
parser.add_argument('--logging_steps', type=int, default=100, help='Log every X updates steps.')
parser.add_argument('--save_steps', type=int, default=1000, help='Save checkpoint every X updates steps.')
Expand Down Expand Up @@ -103,7 +104,10 @@ def run(args):
if world_size > 1:
model = paddle.DataParallel(model)

train_ds, dev_ds = load_dataset(args.dataset_name, splits=['train', 'dev'])
train_ds = load_dataset(
args.dataset_name, splits='train', data_files=args.train_file)
dev_ds = load_dataset(
args.dataset_name, splits='dev', data_files=args.predict_file)

train_ds, train_data_loader = create_data_loader(train_ds, tokenizer, args,
'train')
Expand Down Expand Up @@ -165,13 +169,18 @@ def run(args):
save_ckpt(model, tokenizer, args.save_dir, step)
print('Saved step {} model.\n'.format(step))
if args.do_predict:
evaluation(model, dev_data_loader, args, tokenizer)
model_eval = model._layers if isinstance(
model, paddle.DataParallel) else model
evaluation(model_eval, dev_data_loader, args,
tokenizer)

batch_start_time = time.time()

print('\nTraining completed.')
elif args.do_predict:
evaluation(model, dev_data_loader, args, tokenizer)
model_eval = model._layers if isinstance(model,
paddle.DataParallel) else model
evaluation(model_eval, dev_data_loader, args, tokenizer)


@paddle.no_grad()
Expand All @@ -198,8 +207,9 @@ def evaluation(model, data_loader, args, tokenizer):
eos_token_id=tokenizer.mask_token_id)

total_time += (time.time() - start_time)
if step % 100 == 0:
print('step %d - %.3fs/step' % (step, total_time / 100))
if step % args.logging_steps == 0:
print('step %d - %.3fs/step' %
(step, total_time / args.logging_steps))
total_time = 0.0

results = select_sum(ids, scores, tokenizer, args.max_dec_len,
Expand Down
25 changes: 25 additions & 0 deletions examples/text_generation/unimo-text/scripts/lcsts_train.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# GPU启动,参数`--gpus`指定训练所用的GPU卡号,可以是单卡,也可以多卡
unset CUDA_VISIBLE_DEVICES

log_dir=./lcsts-log
rm -rf ${log_dir}
mkdir -p ${log_dir}

python -m paddle.distributed.launch --gpus "0,1,2,3" --log_dir ${log_dir} run_gen.py \
--dataset_name=lcsts_new \
--model_name_or_path=unimo-text-1.0 \
--save_dir=${log_dir}/checkpoints \
--logging_steps=100 \
--save_steps=10000 \
--epochs=6 \
--batch_size=64 \
--learning_rate=5e-5 \
--warmup_propotion=0.02 \
--weight_decay=0.01 \
--max_seq_len=320 \
--max_target_len=30 \
--max_dec_len=20 \
--min_dec_len=3 \
--do_train \
--do_predict \
--device=gpu >> ${log_dir}/lanch.log 2>&1
25 changes: 25 additions & 0 deletions examples/text_generation/unimo-text/scripts/qg_train.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# GPU启动,参数`--gpus`指定训练所用的GPU卡号,可以是单卡,也可以多卡
unset CUDA_VISIBLE_DEVICES

log_dir=./qg-log
rm -rf ${log_dir}
mkdir -p ${log_dir}

python -m paddle.distributed.launch --gpus "0,1,2,3" --log_dir ${log_dir} run_gen.py \
--dataset_name=dureader_qg \
--model_name_or_path=unimo-text-1.0 \
--save_dir=${log_dir}/checkpoints \
--logging_steps=10 \
--save_steps=1000 \
--epochs=6 \
--batch_size=8 \
--learning_rate=5e-5 \
--warmup_propotion=0.02 \
--weight_decay=0.01 \
--max_seq_len=320 \
--max_target_len=30 \
--max_dec_len=20 \
--min_dec_len=3 \
--do_train \
--do_predict \
--device=gpu >> ${log_dir}/lanch.log 2>&1
25 changes: 25 additions & 0 deletions examples/text_generation/unimo-text/scripts/table_train.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# GPU启动,参数`--gpus`指定训练所用的GPU卡号,可以是单卡,也可以多卡
unset CUDA_VISIBLE_DEVICES

log_dir=./table-log
rm -rf ${log_dir}
mkdir -p ${log_dir}

python -m paddle.distributed.launch --gpus "0,1,2,3" --log_dir ${log_dir} run_gen.py \
--dataset_name=advertisegen \
--model_name_or_path=unimo-text-1.0 \
--save_dir=${log_dir}/checkpoints \
--logging_steps=100 \
--save_steps=1000 \
--epochs=6 \
--batch_size=8 \
--learning_rate=5e-5 \
--warmup_propotion=0.02 \
--weight_decay=0.01 \
--max_seq_len=500 \
--max_target_len=200 \
--max_dec_len=200 \
--min_dec_len=10 \
--do_train \
--do_predict \
--device=gpu >> ${log_dir}/lanch.log 2>&1
2 changes: 1 addition & 1 deletion paddlenlp/datasets/advertisegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _read(self, filename, *args):

yield {
'source': json_data["content"],
'target': json_data["summary"],
'target': json_data.get("summary", ''),
'id': data_id
}
data_id += 1
2 changes: 1 addition & 1 deletion paddlenlp/datasets/dureader_qg.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _read(self, filename, *args):

yield {
'source': json_data["context"],
'target': json_data["question"],
'target': json_data.get("question", ''),
'title': title,
'id': json_data['id']
}
2 changes: 1 addition & 1 deletion paddlenlp/datasets/lcsts_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ def _read(self, filename, *args):

yield {
'source': json_data["content"],
'target': json_data["summary"],
'target': json_data.get("summary", ''),
'id': json_data['id']
}
Loading

0 comments on commit fe8c2d6

Please sign in to comment.