使用飞桨PaddlePaddle-源于产业实践的开源深度学习平台 深度学习框架复现InfoXLM 论文的实验结果。复现概要:
- Sentence Retrieval 结果与原始论文一致;
- XNLI 推理与原始论文结果接近。
[TOC]
我们推荐在一个新的虚拟环境中使用飞桨PaddlePaddle。
conda create -n infoxlm_paddle python=3.9.6
source activate infoxlm_paddle
conda install paddlepaddle-gpu==2.3.0 cudatoolkit=11.2 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/Paddle/ -c conda-forge -n infoxlm_paddle
微软团队给出的原始模型权重可以使用如下的命令导出为飞桨PaddlePaddle的权重文件。
-
从 Huggingface/InfoXLM下载所有的文件,并放置在目录
model_checkpoints/original_pytorch_huggingface
下,完成后,目录应该如下:model_checkpoints/ original_pytorch_huggingface/ config.json pytorch_model.bin sentencepiece.bpe.model tokenizer.json
-
确认已经进入刚才创建的infoxlm_paddle虚拟环境,执行如下操作:
cd model_checkpoints python converter.py --torch original_pytorch_huggingface/pytorch_model.bin --paddle converted_paddle/model_state.pdparams
转换后的权重保存在
converted_paddle/model_state.pdparams
, 命令行的输出应该与model_checkpoints/conversion_log.txt
一致. -
执行如下命令,运行自动化测试脚本,确保模型权重已经转换成功:
python -m pytest ./tests/
您应该能看到大多数的测试都能通过,这意味着您的模型权重已经转换成功。(有部分测试函数针对的是微调后的模型)
按照原始InfoXLM论文,我们提供了针对下游任务微调InfoXLM的程序,其中,在调用数据方面,我们使用了方便的paddlenlp.datasets
API。不过使用paddlenlp.trainer
相关的API时有时候会出现loss=nan的情况,所以我们手动实现了相关的训练过程,如trainer_manual.py
所示。
训练模型
python trainer_manual.py \
--train_batch_size=8 \
--max_length=128 \
--learning_rate=5e-5 \
--fp16 \
--train_lang=en \
--gradient_accumulation_steps 8
微调过的权重通过网盘提供,下载信息在"model_checkpoints\finetuned_paddle\download.txt".
快速验证训练和微调效果
以下命令会直接加载网盘提供的权重,并且训练模型两步,然后直接运行validation和test推理,并且计算出gap_score。
python trainer_manual.py \
--train_batch_size=4 \
--max_length=64 \
--learning_rate=1e-8 \
--train_lang=en \
--eval_lang=zh \
--logging_steps=2 \
--fp16 \
--save_steps=2 \
--quick_verify
日志中会多出一行global_steps 2 loss: 0.24736906
,loss较低是因为模型已经训练好了,这里只是验证梯度流是完整的。同时,程序会自动完成15个语言的XNLI 验证集、测试集效果检验。并且计算出gap_score(英文的性能减去其他语言的平均性能,越接近0越说明模型跨语言泛化能力好):
我们提供的权重有如下的gap_score,10.28%的分数略好于论文报告。
Step 2 val_gap_score -0.1034 test_gap_score -0.1028, the lower the better
我们提供了在15个语言的数据集上进行评估的程序eval.py
,评估结果如下:
python eval.py
The results are shown here, comparing to the original paper report:
语言 | Paddle Version | Original Paper |
---|---|---|
AR | 0.731 | 0.742 |
BG | 0.787 | 0.793 |
DE | 0.781 | 0.793 |
EL | 0.771 | 0.778 |
EN | 0.854 | 0.864 |
ES | 0.805 | 0.809 |
FR | 0.799 | 0.803 |
HI | 0.711 | 0.722 |
RU | 0.768 | 0.776 |
SW | 0.674 | 0.675 |
TH | 0.741 | 0.746 |
TR | 0.750 | 0.756 |
UR | 0.668 | 0.673 |
VI | 0.758 | 0.771 |
ZH | 0.760 | 0.770 |
平均 | **0.760 **( 误差 0.5%) | 0.765 |
Non-English 平均分是0.751,English的分数是0.854,两者相差10.3%,与原文一致。
讨论
我们认为两个关键的差异在于:
- 原始论文微调使用的batch_size = 256,而我们复现的版本因为硬件限制,实际有效的batch_size是32
- 原始论文每5000步即评估一次val set精度,我们只在每个epoch结束的时候评估了。
更小的batch_size 影响了一些性能,我们注意到有些epoch中,中文的精度可以达到77.0%,比起实际提交的微调版本高出一个点,但是这个epoch在其他语言上的表现平均不如提交的版本。可见更小的batch_size 导致的性能波动还是可观的。
该任务不需要训练,只需要使用导出的原始预训练权重即可完成。原文中给出了参考的Tatoeba数据集使用的论文:[1812.10464] Massively Multilingual Sentence Embeddings for Zero-Shot Cross-Lingual Transfer and Beyond (arxiv.org),我们阅读论文后找到了论文开源的数据集合和评测方法,来源:facebookresearch/LASER: Language-Agnostic SEntence Representations (github.com),并且将评测过程使用paddle重现了;
Sentence Retrieval任务是评测跨语言的同样含义的句子(例如一对互译的句子),经过预训练模型编码后,其表征是否有足够的相似度。InfoXLM论文使用了Tatoeba里与XNLI的14个语言 -- 英文互译的数据集;每个语言和英语的互译句子有1000句。针对一个语言评测时,我们执行以下操作:
- 将这1000句话的英文和外语版本(共2000句)encode,取出InfoXLM第7层transformer输出的向量,并沿token的方向取平均,得到1 * 768的向量,并作L2标准化;
- 对En->X 计算句子取回的准确率:
- 针对每个英语的句子,计算该句子的向量与X语言1000句的向量的余弦相似度,取出余弦相似度最高的句子;
- 计算取出的句子确实是对应译文的平均概率,为准确率。
- 对X-> En 计算句子取回的准确率:
- 与上述过程类似,针对每个X语言句子计算英语1000句中的相似度
我们已经在git中附带了要用到的Tatoeba数据,位于experiments/sentence_retrieval/datasets
。如果您完成了原始权重的转换,您可以通过以下命令行复现上述实验的过程:
python experiments/sentence_retrieval/run_experiments.py
执行后会输出log.txt 和 sentence_retrieval_results.csv,结果如下,表格中所有指标与论文报告(table-2)完全一致。
X | en->x | x->en |
---|---|---|
ar | 0.686 | 0.591 |
bg | 0.787 | 0.787 |
de | 0.951 | 0.939 |
el | 0.726 | 0.622 |
es | 0.872 | 0.882 |
fr | 0.84 | 0.794 |
hi | 0.883 | 0.871 |
ru | 0.857 | 0.838 |
sw | 0.408 | 0.395 |
th | 0.912 | 0.850 |
tr | 0.847 | 0.832 |
ur | 0.733 | 0.73 |
vi | 0.92 | 0.896 |
zh | 0.864 | 0.864 |
Average | 0.806 | 0.778 |
我们提供了模型的导出和部署程序,如果您下载了微调后的权重(finetuned_paddle),您可以直接使用这个权重部署模型,如下:
python TIPC/export_paddle/export_model.py --model_path=model_checkpoints/finetuned_paddle --save_inference_dir ./xnli_exported_model
导出成功后,目标文件夹xnli_exported_model
会保存如下文件:
xnli_exported_models/
added_tokens.json
inference.pdiparams
inference.pdiparams.info
inference.pdmodel
special_tokens_map.json
spiece.model
tokenizer_config.json
使用这个模型进行推理的操作如下:
python TIPC/inference_paddle/infer.py --model_dir ./xnli_exported_models --use_gpu True --warmup 1 --text "You don't have to stay here.<sep>You can not leave."
其中--text参数提供了本次推理的输入数据,格式为"前提<sep>假设"
,语言无需额外指定,所有语言输入都能通过提供的sentencepiece模型进行分词。推理输出如下:
[2022-06-05 23:07:24,056] [ INFO] - Adding <pad> to the vocabulary
[2022-06-05 23:07:24,057] [ INFO] - Adding <mask> to the vocabulary
[2022-06-05 23:07:24,057] [ INFO] - Adding <pad> to the vocabulary
[2022-06-05 23:07:24,058] [ INFO] - Adding <mask> to the vocabulary
[2022-06-05 23:07:24,058] [ INFO] - Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.
text: You don't have to stay here. <sep> You can not leave., label_id: 0, prob: 0.8804811835289001, label: contradiction
推理成功,结果为contradiction(概率0.880),与模型本身一致。
详情请参考Serving 安装。在这里我们推荐使用linux docker的方式来安装,原生版本无论在windows还是unix环境中不可控因素较难排查。我们提供的Git代码经测试可以在windows + linux docker cpu only 的版本中成功部署。
为了方便,启动的时候可以挂载上当前的目录
# 启动 CPU Docker
docker pull registry.baidubce.com/paddlepaddle/serving:0.9.0-devel
docker run -p 9292:9292 --name test_cpu -v ${PWD}:/home/infoxlm_paddle -dit registry.baidubce.com/paddlepaddle/serving:0.9.0-devel bash
docker exec -it test_cpu bash
git clone https://github.com/PaddlePaddle/Serving
并完成页面内其他的步骤,安装依赖项目
使用4.1章节中导出的模型,部署推理服务,如下: