Skip to content

Commit c367f8f

Browse files
authored
Optimize clue grid search tools (#2313)
* optimize clue grid search * update grid search * update logger * new warmup file * fix grd search filename bug, optimize warmup, fix logger, fix logger extractor * fix log * update grid search tools * add seed in argument list * optimize * update grid search according to comments * avoid recomputing when running again. * add num_proc args * update readme * update readme and extract script * update grid search doc * add index for readme * fix index format
1 parent 058a43a commit c367f8f

19 files changed

+505
-539
lines changed

examples/benchmark/clue/README.md

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
# CLUE Benchmark
22

3+
**目录**
4+
* [CLUE 评测结果](#CLUE评测结果)
5+
* [一键复现模型效果](#一键复现模型效果)
6+
* [启动 CLUE 分类任务](#启动CLUE分类任务)
7+
* [使用 Trainer 启动 CLUE 分类任务](#使用Trainer启动CLUE分类任务)
8+
* [启动 CLUE 阅读理解任务](#启动CLUE阅读理解任务)
9+
* [批量启动 Grid Search](#批量启动GridSearch)
10+
* [环境依赖](#环境依赖)
11+
* [一键启动方法](#一键启动方法)
12+
* [Grid Search 脚本说明](#GridSearch脚本说明)
13+
* [参加 CLUE 竞赛](#参加CLUE竞赛)
14+
* [分类任务](#分类任务)
15+
* [阅读理解任务](#阅读理解任务)
16+
317
[CLUE](https://www.cluebenchmarks.com/) 自成立以来发布了多项 NLP 评测基准,包括分类榜单,阅读理解榜单和自然语言推断榜单等,在学术界、工业界产生了深远影响。是目前应用最广泛的中文语言测评指标之一。详细可参考 [CLUE论文](https://arxiv.org/abs/2004.05986)
418

519
本项目基于 PaddlePaddle 在 CLUE 数据集上对领先的开源预训练模型模型进行了充分评测,为开发者在预训练模型选择上提供参考,同时开发者基于本项目可以轻松一键复现模型效果,也可以参加 CLUE 竞赛取得好成绩。
620

21+
<a name="CLUE评测结果"></a>
22+
723
## CLUE 评测结果
824

925
使用多种**中文**预训练模型微调在 CLUE 的各验证集上有如下结果:
@@ -92,7 +108,7 @@
92108
</td>
93109
</tr>
94110
<tr>
95-
<td rowspan=6 align=center> 12L768H </td>
111+
<td rowspan=6 align=center> 12L768H </td>
96112
<td style="text-align:center">
97113
<span style="font-size:18px">ERNIE 3.0-Base-zh</span>
98114
</td>
@@ -511,7 +527,7 @@
511527
</td>
512528
</tr>
513529
<tr>
514-
<td rowspan=1 align=center> 6L384H </td>
530+
<td rowspan=1 align=center> 6L384H </td>
515531
<td style="text-align:center">
516532
<span style="font-size:18px">ERNIE 3.0-Mini-zh</span>
517533
</td>
@@ -550,7 +566,7 @@
550566
</td>
551567
</tr>
552568
<tr>
553-
<td rowspan=1 align=center> 4L384H </td>
569+
<td rowspan=1 align=center> 4L384H </td>
554570
<td style="text-align:center">
555571
<span style="font-size:18px">ERNIE 3.0-Micro-zh</span>
556572
</td>
@@ -589,7 +605,7 @@
589605
</td>
590606
</tr>
591607
<tr>
592-
<td rowspan=1 align=center> 4L312H </td>
608+
<td rowspan=1 align=center> 4L312H </td>
593609
<td style="text-align:center">
594610
<span style="font-size:18px">ERNIE 3.0-Nano-zh</span>
595611
</td>
@@ -675,11 +691,14 @@ AFQMC、TNEWS、IFLYTEK、CMNLI、OCNLI、CLUEWSC2020、CSL 、CHID 和 C<sup>3<
675691

676692
其中,`ERNIE 3.0-Base-zh``ERNIE 3.0-Medium-zh``ERNIE-Gram-zh``ERNIE 1.0``ERNIE 3.0-Mini-zh``ERNIE 3.0-Micro-zh``ERNIE 3.0-Nano-zh` 在 CLUEWSC2020 处的 dropout_prob 为 0.0,`ERNIE 3.0-Base-zh``HLF/RBT6, Chinese``Mengzi-BERT-Base``ERNIE-Gram-zh``ERNIE 1.0``TinyBERT6, Chinese``UER/Chinese-RoBERTa (L6-H768)``ERNIE 3.0-Mini-zh``ERNIE 3.0-Micro-zh``ERNIE 3.0-Nano-zh` 在 IFLYTEK 处的 dropout_prob 为 0.0。
677693

694+
<a name="一键复现模型效果"></a>
678695

679696
## 一键复现模型效果
680697

681698
这一节将会对分类、阅读理解任务分别展示如何一键复现本文的评测结果。
682699

700+
<a name="启动CLUE分类任务"></a>
701+
683702
### 启动 CLUE 分类任务
684703
以 CLUE 的 TNEWS 任务为例,启动 CLUE 任务进行 Fine-tuning 的方式如下:
685704

@@ -744,7 +763,10 @@ eval loss: 2.572999, acc: 0.112, eval done total : 25.67190170288086 s
744763
global step 400/20010, epoch: 0, batch: 399, rank_id: 0, loss: 2.631579, lr: 0.0000059970, speed: 2.6238 step/s
745764
eval loss: 2.476962, acc: 0.1697, eval done total : 25.794789791107178 s
746765
```
747-
#### 使用Trainer启动 CLUE 分类任务
766+
767+
<a name="使用Trainer启动CLUE分类任务"></a>
768+
769+
#### 使用 Trainer 启动 CLUE 分类任务
748770
PaddleNLP 提供了 Trainer API,本示例新增了`run_clue_classifier_trainer.py`脚本供用户使用。
749771

750772
```
@@ -790,6 +812,8 @@ python -u ./run_clue_classifier_trainer.py \
790812
- `load_best_model_at_end` 训练结束时,时候加载评估结果最好的 ckpt。
791813
- `save_total_limit` 保存的ckpt数量的最大限制
792814

815+
<a name="启动CLUE阅读理解任务"></a>
816+
793817
### 启动 CLUE 阅读理解任务
794818
以 CLUE 的 C<sup>3</sup> 任务为例,多卡启动 CLUE 任务进行 Fine-tuning 的方式如下:
795819

@@ -814,64 +838,81 @@ python -m paddle.distributed.launch --gpus "0,1,2,3" run_c3.py \
814838
```
815839
需要注意的是,如果显存无法容纳所传入的 `batch_size`,可以通过传入 `gradient_accumulation_steps` 参数来模拟该 `batch_size`
816840

841+
<a name="批量启动GridSearch"></a>
842+
817843
### 批量启动 Grid Search
818844

819-
#### 环境说明
845+
<a name="环境依赖"></a>
820846

821-
脚本中的默认参数是针对 32G 显卡的机器设置的,如果显卡是 16G 或者更小,可以在`grid_search_tools/run_mrc.sh`中增加每个任务的`${grd_accu_steps}``${grd_accu_steps}` 对应于 Python 脚本中的 `--gradient_accumulation_steps` 参数,该参数代表梯度累加的步数。即,实际训练的时候,会以 batch_size 为 `batch_size / ${grd_accu_steps}` 训练 `${grd_accu_steps}` 步来模拟原 batch_size 下的训练。因此也需要保证原 batch_size 能被 `${grd_accu_steps}` 整除。
847+
#### 环境依赖
822848

823-
#### 使用说明
824-
- `run_all_cls.sh` 分类任务批量启动脚本入口,需要 2 个参数:模型名称或目录、模式 id
825-
- 模式 0、1、2 均代表独立的 1 组 4 卡实验,需要在不同的 4 张卡上运行(见下方分类任务的方法二)
826-
- 模型 3 代表 同时起 12 组 8 卡实验(见下方分类任务的方法一)
827-
- 对于 32G 8 GPUs 卡机器,base 模型可以选模式 3,即 1 组 8 卡,可同时完成所有分类任务。而 large 模型需要分别起模式 0、1、2 三组实验
828-
- `run_all_mrc.sh` 阅读理解任务批量启动脚本入口,需要 2 个参数:模型名称或目录、模式id
829-
- 模式 0、1、2 分别代表起 4 卡 CHID,4 卡 C<sup>3</sup>,2 卡 CMRC2018 实验(见下方阅读理解任务的方法二)
830-
- 模式 3 代表 起 8 卡实验,任务会完成 CHID、C<sup>3</sup>、CMRC2018 任务(见下方阅读理解任务的方法一)
831-
- 对于 32G 8 GPUs 卡机器,base 模型可以选模式 3
832-
- `extract_acc.sh` 从日志抽取每个任务的最佳结果,需要 1 个参数:模型名称或目录。调用前需要确认训练均全部完成,并且该目录下有分类和阅读理解任务所有的日志。可以把打印出的 10 个任务的结果直接复制到表格中。例如:
849+
Grid Search 需要在 GPU 环境下进行,需要依赖 pynvml 库。pynvml 库提供了 GPU 管理的 Python 接口。
833850

834-
```
835-
AFQMC TNEWS IFLYTEK CMNLI OCNLI CLUEWSC2020 CSL CMRC2018 CHID C3
836-
75.3 557.45 60.18 81.16 77.19 79.28 81.93 65.83/87.29 80.00 70.36
851+
可启动以下命令进行安装 pynvml:
852+
853+
```shell
854+
pip install pynvml
837855
```
838856

857+
<a name="一键启动方法"></a>
839858

840-
```shell
841-
cd grid_search_tools
842-
# 7 个分类任务
843-
# 方法一:3 代表模式 3,1 组 8 卡实验(适用于 base 及更小的模型)
844-
bash run_all_cls.sh ernie-3.0-nano-zh 3 # 第二个参数 3 就代表模式 3
859+
#### 一键启动方法
845860

846-
# 方法二:0、1、2 分别代表模式 0、1、2,代表起了 3 组 4 卡实验(适用于较大的模型)
847-
bash run_all_cls.sh ernie-3.0-nano-zh 0
848-
bash run_all_cls.sh ernie-3.0-nano-zh 1
849-
bash run_all_cls.sh ernie-3.0-nano-zh 2
861+
运行下面一句命令即可启动 Grid Search 任务。前期需要注意数据集是否正常下载,否则训练任务不会正式启动。
850862

851-
# 3 个阅读理解任务
852-
# 对于32G 8 GPUs 卡机器,base 模型可以选模式 3
863+
```shell
864+
cd grid_search_tools
853865

854-
# 方法一:3 代表模式3,1组8卡实验
855-
bash run_all_mrc.sh ernie-3.0-nano-zh 3
866+
# 这里 ernie-3.0-nano-zh 是模型名,也可以传用户自定义的模型目录
867+
# 自定义的模型目录需要有 model_config.json, model_state.pdparams, tokenizer_config.json 和 vocab.txt 四个文件
868+
python grid_seach.py ernie-3.0-nano-zh
856869

857-
# 方法二:对于较大模型,分别起3组实验:
858-
bash run_all_mrc.sh ernie-3.0-nano-zh 0
859-
bash run_all_mrc.sh ernie-3.0-nano-zh 1
860-
bash run_all_mrc.sh ernie-3.0-nano-zh 2
861870
```
862871

863-
确认模型所有任务训练完成后,可以调用脚本 `extract_acc.sh` 一键抽取 Grid Search 结果
872+
确认模型所有任务训练完成后,可以调用脚本 `extract_result.sh` 一键抽取 Grid Search 结果,打印出每个任务的最佳结果和对应的超参数,例如:
864873

865874
```shell
866-
sh extract_acc.sh ernie-3.0-nano-zh
875+
bash extract_result.sh ernie-3.0-nano-zh # 注意模型名或者模型目录名后面不要带'/'
867876
```
877+
```text
878+
AFQMC TNEWS IFLYTEK CMNLI OCNLI CLUEWSC2020 CSL CMRC2018 CHID C3
879+
76.48 58.47 61.18 83.57 80.20 88.49 82.80 72.20/90.67 83.67 78.49
880+
====================================================================
881+
best hyper-parameters list:
882+
====================================================================
883+
afqmc's best result is 76.48, and lr, bs, dropout_p are: 3e-05 16 0.1
884+
tnews's best result is 58.47, and lr, bs, dropout_p are: 5e-05 16 0.1
885+
iflytek's best result is 61.18, and lr, bs, dropout_p are: 5e-05 16 0.0
886+
ocnli's best result is 80.20, and lr, bs, dropout_p are: 2e-05 16 0.1
887+
cluewsc2020's best result is 88.49, and lr, bs, dropout_p are: 2e-05 32 0.0
888+
csl's best result is 82.80, lr, bs, and dropout_p are: 5e-05 64 0.1
889+
cmrc2018's best result is 69.52/89.02, and lr, bs, dropout_p are: 1e-05 32 0.1
890+
```
891+
892+
另外,如遇意外情况(如机器重启)导致训练中断,可以直接再次启动 `grid_search.py` 脚本,之前已完成(输出完整日志)的任务则会直接跳过。
893+
894+
<a name="GridSearch脚本说明"></a>
895+
896+
#### Grid Search 脚本说明
897+
898+
本节介绍 grid_search_tools 目录下各个脚本的功能:
899+
900+
- `grid_search.py` Grid Search 任务入口脚本,该脚本负责调度 GPU 资源,可自动将 7 个分类任务、3 个阅读理解下所有超参数对应的任务完成,训练完成后会自动调用抽取结果的脚本 `extract_result.sh` 打印出所有任务的最佳结果和对应的超参。
901+
- `warmup_dataset_and_model.py` 首次运行时,该脚本完成模型下载(如需)、数据集下载,阅读理解任务数据预处理、预处理文件缓存等工作,再次运行则会检查这些文件是否存在,存在则跳过。该脚本由 `grid_search.py` 在 Grid Search 训练前自动调用,预处理 cache 文件生成后,后面所有训练任务即可加载缓存文件避免重复进行数据预处理。如果该阶段任务失败,大多需要检查网络,解决之后需重启 `grid_search.py`,直到训练正常开始。该脚本也可手动调用,需要 1 个参数,模型名称或目录。该脚本在使用 Intel(R) Xeon(R) Gold 6271C CPU 且 `--num_proc`默认为 4 的情况下需约 30 分钟左右完成,可以更改 `run_mrc.sh` 中的 `--num_proc` 参数以改变生成 cache 的进程数。需要注意的是,若改变 num_proc,之前的缓存则不能再使用,该脚本会重新处理数据并生成新的 cache,cache 相关内容可查看[datasets.Dataset.map文档](https://huggingface.co/docs/datasets/v2.0.0/package_reference/main_classes?highlight=map#datasets.Dataset.map)。
902+
- `extract_result.sh` 从日志抽取每个任务的最佳结果和对应的最佳超参并打印,`grid_search.py` 在完成训练任务后会自动调用,也可手动调用,需要 1 个参数:模型名称或目录。手动调用前需要确认训练均全部完成,并且保证该目录下有分类和阅读理解所有任务的日志。
903+
- `run_mrc.sh` 阅读理解任务的启动脚本。
904+
- `run_cls.sh` 分类任务的启动脚本。
905+
906+
<a name="参加CLUE竞赛"></a>
868907

869908
## 参加 CLUE 竞赛
870909

871910
对各个任务运行预测脚本,汇总多个结果文件压缩之后,即可提交至 CLUE 官网进行评测。
872911

873912
下面 2 小节会分别介绍分类、阅读理解任务产生预测结果的方法。
874913

914+
<a name="分类任务"></a>
915+
875916
### 分类任务
876917

877918
以 TNEWS 为例,可以直接使用脚本 `classification/run_clue_classifier.py` 对单个任务进行预测,注意脚本启动时需要传入参数 `--do_predict`。假设 TNEWS 模型所在路径为 `${TNEWS_MODEL}`,运行如下脚本可得到模型在测试集上的预测结果,预测结果会写入地址 `${OUTPUT_DIR}/tnews_predict.json`
@@ -887,6 +928,7 @@ python run_clue_classifier.py \
887928
--output_dir ${OUTPUT_DIR} \
888929
--do_predict \
889930
```
931+
<a name="阅读理解任务"></a>
890932

891933
### 阅读理解任务
892934

examples/benchmark/clue/classification/run_clue_classifier.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from paddlenlp.data import DataCollatorWithPadding
3333
from paddlenlp.transformers import LinearDecayWithWarmup
3434
from paddlenlp.transformers import AutoModelForSequenceClassification, AutoTokenizer
35+
from paddlenlp.utils.log import logger
3536

3637
METRIC_CLASSES = {
3738
"afqmc": Accuracy,
@@ -180,7 +181,7 @@ def evaluate(model, loss_fct, metric, data_loader):
180181
correct = metric.compute(logits, labels)
181182
metric.update(correct)
182183
res = metric.accumulate()
183-
print("eval loss: %f, acc: %s, " % (loss.numpy(), res), end='')
184+
logger.info("eval loss: %f, acc: %s, " % (loss.numpy(), res))
184185
model.train()
185186
return res
186187

@@ -284,7 +285,7 @@ def do_eval(args):
284285
correct = metric.compute(logits, labels)
285286
metric.update(correct)
286287
res = metric.accumulate()
287-
print("acc: %s\n, " % (res), end='')
288+
logger.info("acc: %s\n, " % (res))
288289

289290

290291
def do_train(args):
@@ -397,7 +398,7 @@ def do_train(args):
397398
lr_scheduler.step()
398399
optimizer.clear_grad()
399400
if global_step % args.logging_steps == 0:
400-
print(
401+
logger.info(
401402
"global step %d/%d, epoch: %d, batch: %d, rank_id: %s, loss: %f, lr: %.10f, speed: %.4f step/s"
402403
% (global_step, num_training_steps, epoch, step,
403404
paddle.distributed.get_rank(), loss,
@@ -407,7 +408,8 @@ def do_train(args):
407408
if global_step % args.save_steps == 0 or global_step == num_training_steps:
408409
tic_eval = time.time()
409410
acc = evaluate(model, loss_fct, metric, dev_data_loader)
410-
print("eval done total : %s s" % (time.time() - tic_eval))
411+
logger.info("eval done total : %s s" %
412+
(time.time() - tic_eval))
411413
if acc > best_acc:
412414
best_acc = acc
413415
if args.save_best_model:
@@ -420,9 +422,9 @@ def do_train(args):
420422
model_to_save.save_pretrained(output_dir)
421423
tokenizer.save_pretrained(output_dir)
422424
if global_step >= num_training_steps:
423-
print("best_acc: %.2f" % (best_acc * 100))
425+
logger.info("best_result: %.2f" % (best_acc * 100))
424426
return
425-
print("best_acc: %.2f" % (best_acc * 100))
427+
logger.info("best_result: %.2f" % (best_acc * 100))
426428

427429

428430
def do_predict(args):

examples/benchmark/clue/grid_search_tools/extract_acc.sh

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
unset GREP_OPTIONS
16+
17+
MODEL_PATH=$1
18+
declare -A dict
19+
20+
for task in afqmc tnews iflytek cmnli ocnli cluewsc2020 csl cmrc2018 chid c3
21+
do
22+
if [ $task == 'cmrc2018' ]; then
23+
dict[${task}]=`cat ${MODEL_PATH}/${task}/*|grep best_result|awk '{print $7}' |awk '{print substr($0,1,11)}'|awk '$0>x{x=$0};END{print x}'`
24+
else
25+
dict[${task}]=`cat ${MODEL_PATH}/${task}/*|grep best_result|awk '{print $7}' |awk '{print substr($0,1,5)}'|awk '$0>x{x=$0};END{print x}'`
26+
fi
27+
done
28+
29+
echo -e AFQMC"\t"TNEWS"\t"IFLYTEK"\t"CMNLI"\t"OCNLI"\t"CLUEWSC2020"\t"CSL"\t"CMRC2018"\t"CHID"\t"C3
30+
31+
for task in afqmc tnews iflytek cmnli ocnli cluewsc2020 csl cmrc2018 chid c3
32+
do
33+
echo -e -n "${dict[$task]}\t"
34+
done
35+
36+
echo -e "\n====================================================================\nBest hyper-parameters list: \n===================================================================="
37+
for task in afqmc tnews iflytek cmnli ocnli cluewsc2020 csl cmrc2018 chid c3
38+
do
39+
if [ -z ${dict[$task]} ]
40+
then
41+
echo ${dict[$task]} > test
42+
continue
43+
fi
44+
s=`find ${MODEL_PATH}/${task}/* | xargs grep -rin "best_result: ${dict[$task]}"|awk '{split($1, hy, "/"); print(hy[3])}'`
45+
s=`echo $s|awk '{split($1, hy, "."); print hy[1]"."hy[2]}'`
46+
s=`echo $s|awk '{split($1, hy, "_"); print hy[1] " " hy[2] " "hy[3]}'`
47+
echo -e "${task}'s best result is ${dict[$task]}, and lr, bs, dropout_p are: "$s
48+
done

0 commit comments

Comments
 (0)