## PaddleNLP 常见问题汇总(持续更新) + [【精选】NLP 精选5问](#NLP 精选) + [Q1.1 如何加载自己的本地数据集,以便使用 PaddleNLP 的功能?](#1-1) + [Q1.2 PaddleNLP 会将内置的数据集、模型下载到默认路径,如何修改路径?](#1-2) + [Q1.3 PaddleNLP 中如何保存、加载训练好的模型?](#1-3) + [Q1.4 当训练样本较少时,有什么推荐的方法能提升模型效果吗?](#1-4) + [Q1.5 如何提升模型的性能,提升 QPS?](#1-5) + [【理论篇】NLP 通用问题](#NLP 通用问题 ) + [Q2.1 数据类别分布不均衡, 有哪些应对方法?](#2-2) + [Q2.2 如果使用预训练模型,一般需要多少条样本?](#2-3) + [【实战篇】PaddleNLP 实战问题](#PaddleNLP 实战问题) [数据集和数据处理](#数据问题) + [Q3.1 使用自己的数据集训练预训练模型时,如何引入额外的词表?](#3-1) [模型训练调优](#训练调优问题) + [Q3.2 如何加载自己的预训练模型,进而使用 PaddleNLP 的功能?](#4-1) + [Q3.3 如果训练中断,需要继续热启动训练,如何保证学习率和优化器能从中断地方继续迭代?](#4-2) + [Q3.4 如何冻结模型梯度?](#4-3) + [Q3.5 如何在 eval 阶段打印评价指标,在各 epoch 保存模型参数?](#4-4) + [Q3.6 训练过程中,训练程序意外退出或 Hang 住,应该如何排查?](#4-5) + [Q3.7 在模型验证和测试过程中,如何保证每一次的结果是相同的?](#4-6) + [Q3.8 ERNIE 模型如何返回中间层的输出?](#4-7) [预测部署](#部署问题) + [Q3.9 PaddleNLP 训练好的模型如何部署到服务器 ?](#5-1) + [Q3.10 静态图模型如何转换成动态图模型?](#5-2) + [特定模型和应用场景咨询](#NLP 应用场景) + [Q4.1 【词法分析】LAC 模型,如何自定义标签 label,并继续训练?](#6-1) + [Q4.2 信息抽取任务中,是否推荐使用预训练模型+CRF,怎么实现呢?](#6-2) + [Q4.3 【阅读理解】`MapDatasets`的`map()`方法中对应的`batched=True`怎么理解,在阅读理解任务中为什么必须把参数`batched`设置为`True`?](#6-3) + [Q4.4 【语义匹配】语义索引和语义匹配有什么区别?](#6-4) + [Q4.5 【解语】wordtag 模型如何自定义添加命名实体及对应词类?](#6-5) + [其他使用咨询](#使用咨询问题) + [Q5.1 在 CUDA11使用 PaddlNLP 报错?](#7-1) + [Q5.2 如何设置 parameter?](#7-2) + [Q5.3 GPU 版的 Paddle 虽然能在 CPU 上运行,但是必须要有 GPU 设备吗?](#7-3) + [Q5.4 如何指定用 CPU 还是 GPU 训练模型?](#7-4) + [Q5.5 动态图模型和静态图模型的预测结果一致吗?](#7-5) + [Q5.6 如何可视化 acc、loss 曲线图、模型网络结构图等?](#7-6) ## ⭐️【精选】NLP 精选5问 ##### Q1.1 如何加载自己的本地数据集,以便使用 PaddleNLP 的功能? **A:** 通过使用 PaddleNLP 提供的 `load_dataset`, `MapDataset` 和 `IterDataset` ,可以方便的自定义属于自己的数据集哦,也欢迎您贡献数据集到 PaddleNLP repo。 从本地文件创建数据集时,我们 **推荐** 根据本地数据集的格式给出读取 function 并传入 `load_dataset()` 中创建数据集。 ```python from paddlenlp.datasets import load_dataset def read(data_path): with open(data_path, 'r', encoding='utf-8') as f: # 跳过列名 next(f) for line in f: words, labels = line.strip('\n').split('\t') words = words.split('\002') labels = labels.split('\002') yield {'tokens': words, 'labels': labels} # data_path为read()方法的参数 map_ds = load_dataset(read, data_path='train.txt', lazy=False) iter_ds = load_dataset(read, data_path='train.txt', lazy=True) ``` 如果您习惯使用`paddle.io.Dataset/IterableDataset`来创建数据集也是支持的,您也可以从其他 python 对象如`List`对象创建数据集,详细内容可参照[官方文档-自定义数据集](https://paddlenlp.readthedocs.io/zh/latest/data_prepare/dataset_self_defined.html)。 ##### Q1.2 PaddleNLP 会将内置的数据集、模型下载到默认路径,如何修改路径? **A:** 内置的数据集、模型默认会下载到`$HOME/.paddlenlp/`下,通过配置环境变量可下载到指定路径: (1)Linux 下,设置 `export PPNLP_HOME="xxxx"`,注意不要设置带有中文字符的路径。 (2)Windows 下,同样配置环境变量 PPNLP_HOME 到其他非中文字符路径,重启即可。 ##### Q1.3 PaddleNLP 中如何保存、加载训练好的模型? **A:**(1)PaddleNLP 预训练模型 ​ 保存: ```python model.save_pretrained("./checkpoint') tokenizer.save_pretrained("./checkpoint') ``` ​ 加载: ```python model.from_pretrained("./checkpoint') tokenizer.from_pretrained("./checkpoint') ``` (2)常规模型 保存: ```python emb = paddle.nn.Embedding(10, 10) layer_state_dict = emb.state_dict() paddle.save(layer_state_dict, "emb.pdparams") #保存模型参数 ``` ​ 加载: ```python emb = paddle.nn.Embedding(10, 10) load_layer_state_dict = paddle.load("emb.pdparams") # 读取模型参数 emb.set_state_dict(load_layer_state_dict) # 加载模型参数 ``` ##### Q1.4 当训练样本较少时,有什么推荐的方法能提升模型效果吗? **A:** 增加训练样本带来的效果是最直接的。此外,可以基于我们开源的[预训练模型](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/paddlenlp/transformers)进行热启,再用少量数据集 fine-tune 模型。此外,针对分类、匹配等场景,[小样本学习](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/few_shot)也能够带来不错的效果。 ##### Q1.5 如何提升模型的性能,提升 QPS? **A:** 从工程角度,对于服务器端部署可以使用[Paddle Inference](https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/05_inference_deployment/inference/inference_cn.html)高性能预测引擎进行预测部署。对于 Transformer 类模型的 GPU 预测还可以使用 PaddleNLP 中提供的[FastGeneration](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/paddlenlp/ops)功能来进行快速预测,其集成了[NV FasterTransformer](https://github.com/NVIDIA/FasterTransformer)并进行了功能增强。 从模型策略角度,可以使用一些模型小型化技术来进行模型压缩,如模型蒸馏和裁剪,通过小模型来实现加速。PaddleNLP 中集成了 ERNIE-Tiny 这样一些通用小模型供下游任务微调使用。另外 PaddleNLP 提供了[模型压缩示例](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/model_compression),实现了 DynaBERT、TinyBERT、MiniLM 等方法策略,可以参考对自己的模型进行蒸馏压缩。 ## ⭐️【理论篇】NLP 通用问题 ##### Q2.1 数据类别分布不均衡, 有哪些应对方法? **A:** 可以采用以下几种方法优化类别分布不均衡问题: (1)欠采样:对样本量较多的类别进行欠采样,去除一些样本,使得各类别数目接近。 (2)过采样:对样本量较少的类别进行过采样,选择样本进行复制,使得各类别数目接近。 (3)修改分类阈值:直接使用类别分布不均衡的数据训练分类器,会使得模型在预测时更偏向于多数类,所以不再以0.5为分类阈值,而是针对少数类在模型仅有较小把握时就将样本归为少数类。 (4)代价敏感学习:比如 LR 算法中设置 class_weight 参数。 ##### Q2.2 如果使用预训练模型,一般需要多少条样本? **A:** 很难定义具体需要多少条样本,取决于具体的任务以及数据的质量。如果数据质量没问题的话,分类、文本匹配任务所需数据量级在百级别,翻译则需要百万级能够训练出一个比较鲁棒的模型。如果样本量较少,可以考虑数据增强,或小样本学习。 ## ⭐️【实战篇】PaddleNLP 实战问题 ### 数据集和数据处理 ##### Q3.1 使用自己的数据集训练预训练模型时,如何引入额外的词表? **A:** 预训练模型通常会有配套的 tokenzier 和词典,对于大多数中文预训练模型,如 ERNIE-3.0,使用的都是字粒度的输入,tokenzier 会将句子转换为字粒度的形式,模型无法收到词粒度的输入。如果希望引入额外的词典,需要修改预训练模型的 tokenizer 和词典,可以参考这里[blog](https://kexue.fm/archives/7758/comment-page-1#Tokenizer ),另外注意 embedding 矩阵也要加上这些新增词的 embedding 表示。 另外还有一种方式可以使用这些字典信息,可以将数据中在词典信息中的词进行整体 mask 进行一个 mask language model 的二次预训练,这样经过二次训练的模型就包含了对额外字典的表征。可参考 [PaddleNLP 预训练数据流程](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/model_zoo/ernie-1.0/)。 此外还有些词粒度及字词混合粒度的预训练模型,在这些词粒度的模型下引入额外的词表也会容易些,我们也将持续丰富 PaddleNLP 中的预训练模型。 ### 模型训练调优 ##### Q3.2 如何加载自己的预训练模型,进而使用 PaddleNLP 的功能? **A:** 以 bert 为例,如果是使用 PaddleNLP 训练,通过`save_pretrained()`接口保存的模型,可通过`from_pretrained()`来加载: ```python tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") model = BertModel.from_pretrained("bert-base-uncased") ``` 如果不是上述情况,可以使用如下方式加载模型,也欢迎您贡献模型到 PaddleNLP repo 中。 (1)加载`BertTokenizer`和`BertModel` ```python tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") model = BertModel.from_pretrained("bert-base-uncased") ``` (2)调用`save_pretrained()`生成 `model_config.json`、 ``tokenizer_config.json``、`model_state.pdparams`、 `vocab.txt `文件,保存到`./checkpoint`: ```python tokenizer.save_pretrained("./checkpoint") model.save_pretrained("./checkpoint") ``` (3)修改`model_config.json`、 `tokenizer_config.json`这两个配置文件,指定为自己的模型,之后通过`from_pretrained()`加载模型。 ```python tokenizer = BertTokenizer.from_pretrained("./checkpoint") model = BertModel.from_pretrained("./checkpoint") ``` ##### Q3.3 如果训练中断,需要继续热启动训练,如何保证学习率和优化器能从中断地方继续迭代? **A:** (1)完全恢复训练状态,可以先将`lr`、` optimizer`、`model`的参数保存下来: ```python paddle.save(lr_scheduler.state_dict(), "xxx_lr") paddle.save(optimizer.state_dict(), "xxx_opt") paddle.save(model.state_dict(), "xxx_para") ``` (2)加载`lr`、` optimizer`、`model`参数即可恢复训练: ```python lr_scheduler.set_state_dict(paddle.load("xxxx_lr")) optimizer.set_state_dict(paddle.load("xxx_opt")) model.set_state_dict(paddle.load("xxx_para")) ``` ##### Q3.4 如何冻结模型梯度? **A:** 有多种方法可以尝试: (1)可以直接修改 PaddleNLP 内部代码实现,在需要冻结梯度的地方用 `paddle.no_grad()` 包裹一下 `paddle.no_grad()` 的使用方式,以对 `forward()` 进行冻结为例: ``` python # Method 1 class Model(nn.Layer): def __init__(self, ...): ... def forward(self, ...): with paddle.no_grad(): ... # Method 2 class Model(nn.Layer): def __init__(self, ...): ... @paddle.no_grad() def forward(self, ...): ... ``` `paddle.no_grad()` 的使用也不局限于模型内部实现里面,也可以包裹外部的方法,比如: ``` python @paddle.no_grad() def evaluation(...): ... model = Model(...) model.eval() ... ``` (2)第二种方法:以 ERNIE 为例,将模型输出的 tensor 设置 `stop_gradient` 为 True。可以使用 `register_forward_post_hook` 按照如下的方式尝试: ``` python def forward_post_hook(layer, input, output): output.stop_gradient=True self.ernie.register_forward_post_hook(forward_post_hook) ``` (3)第三种方法:在 `optimizer` 上进行处理,`model.parameters` 是一个 `List`,可以通过 `name` 进行相应的过滤,更新/不更新某些参数,这种方法需要对网络结构的名字有整体了解,因为网络结构的实体名字决定了参数的名字,这个使用方法有一定的门槛: ```python [ p for p in model.parameters() if 'linear' not in p.name] # 这里就可以过滤一下linear层,具体过滤策略可以根据需要来设定 ``` ##### Q3.5 如何在 eval 阶段打印评价指标,在各 epoch 保存模型参数? **A:** 飞桨主框架提供了两种训练与预测的方法,一种是用 [paddle.Model()](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/Model_cn.html)对模型进行封装,通过高层 API 如`Model.fit()`、`Model.evaluate()`、`Model.predict()`等完成模型的训练与预测;另一种就是基于基础 API 常规的训练方式。 (1)对于第一种方法: - 我们可以设置 `paddle.Model.fit() ` API 中的 *eval_data* 和 *eval_freq* 参数在训练过程中打印模型评价指标:*eval_data* 参数是一个可迭代的验证集数据源,*eval_freq* 参数是评估的频率;当*eval_data* 给定后,*eval_freq* 的默认值为1,即每一个 epoch 进行一次评估。注意:在训练前,我们需要在 `Model.prepare()` 接口传入 metrics 参数才能在 eval 时打印模型评价指标。 - 关于模型保存,我们可以设置 `paddle.Model.fit()` 中的 *save_freq* 参数控制模型保存的频率:*save_freq* 的默认值为1,即每一个 epoch 保存一次模型。 (2)对于第二种方法: - 我们在 PaddleNLP 的 examples 目录下提供了常见任务的训练与预测脚本:如[GLUE](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/benchmark/glue) 和 [SQuAD](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/machine_reading_comprehension/SQuAD)等 - 开发者可以参考上述脚本进行自定义训练与预测脚本的开发。 ##### Q3.6 训练过程中,训练程序意外退出或 Hang 住,应该如何排查? **A:** 一般先考虑内存、显存(使用 GPU 训练的话)是否不足,可将训练和评估的 batch size 调小一些。 需要注意,batch size 调小时,学习率 learning rate 也要调小,一般可按等比例调整。 ##### Q3.7 在模型验证和测试过程中,如何保证每一次的结果是相同的? **A:** 在验证和测试过程中常常出现的结果不一致情况一般有以下几种解决方法: (1)确保设置了 eval 模式,并保证数据相关的 seed 设置保证数据一致性。 (2)如果是下游任务模型,查看是否所有模型参数都被导入了,直接使用 bert-base 这种预训练模型是不包含任务相关参数的,要确认导入的是微调后的模型,否则任务相关参数会随机初始化导致出现随机性。 (3)部分算子使用 CUDNN 后端产生的不一致性可以通过环境变量的设置来避免。如果模型中使用了 CNN 相关算子,可以设置`FLAGS_cudnn_deterministic=True`。如果模型中使用了 RNN 相关算子,可以设置`CUBLAS_WORKSPACE_CONFIG=:16:8`或`CUBLAS_WORKSPACE_CONFIG=:4096:2`(CUDNN 10.2以上版本可用,参考[CUDNN 8 release note](https://docs.nvidia.com/deeplearning/cudnn/archives/cudnn-894/release-notes/index.html))。 ##### Q3.8 ERNIE 模型如何返回中间层的输出? **A:** 目前的 API 设计不保留中间层输出,当然在 PaddleNLP 里可以很方便地修改源码。 此外,还可以在`ErnieModel`的`__init__`函数中通过`register_forward_post_hook()`为想要保留输出的 Layer 注册一个`forward_post_hook`函数,在`forward_post_hook`函数中把 Layer 的输出保存到一个全局的`List`里面。`forward_post_hook`函数将会在`forward`函数调用之后被调用,并保存 Layer 输出到全局的`List`。详情参考[`register_forward_post_hook()`](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/Layer_cn.html#register_forward_post_hook)。 ### 预测部署 ##### Q3.9 PaddleNLP 训练好的模型如何部署到服务器 ? **A:** 我们推荐在动态图模式下开发,静态图模式部署。 (1)动转静 动转静,即将动态图的模型转为可用于部署的静态图模型。 动态图接口更加易用,python 风格的交互式编程体验,对于模型开发更为友好,而静态图相比于动态图在性能方面有更绝对的优势。因此动转静提供了这样的桥梁,同时兼顾开发成本和性能。 可以参考官方文档 [动态图转静态图文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/04_dygraph_to_static/index_cn.html),使用 `paddle.jit.to_static` 完成动转静。 另外,在 PaddleNLP 我们也提供了导出静态图模型的例子。 (2)借助 Paddle Inference 部署 动转静之后保存下来的模型可以借助 Paddle Inference 完成高性能推理部署。Paddle Inference 内置高性能的 CPU/GPU Kernel,结合细粒度 OP 横向纵向融合等策略,并集成 TensorRT 实现模型推理的性能提升。具体可以参考文档 [Paddle Inference 简介](https://paddleinference.paddlepaddle.org.cn/master/product_introduction/inference_intro.html)。 为便于初次上手的用户更易理解 NLP 模型如何使用 Paddle Inference,PaddleNLP 也提供了对应的例子以供参考,可以参考 [/PaddleNLP/examples](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/) 下的 deploy 目录。 ##### Q3.10 静态图模型如何转换成动态图模型? **A:** 首先,需要将静态图参数保存成`ndarray`数据,然后将静态图参数名和对应动态图参数名对应,最后保存成动态图参数即可。详情可参考[参数转换脚本](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/paddlenlp/transformers/ernie/static_to_dygraph_params)。 ### ⭐️特定模型和应用场景咨询 ##### Q4.1 【词法分析】LAC 模型,如何自定义标签 label,并继续训练? **A:** 更新 label 文件`tag.dict`,添加 修改下 CRF 的标签数即可。 可参考[自定义标签示例](https://github.com/PaddlePaddle/PaddleNLP/issues/662),[增量训练自定义 LABEL 示例](https://github.com/PaddlePaddle/PaddleNLP/issues/657)。 ##### Q4.2 信息抽取任务中,是否推荐使用预训练模型+CRF,怎么实现呢? **A:** 预训练模型+CRF 是一个通用的序列标注的方法,目前预训练模型对序列信息的表达也是非常强的,也可以尝试直接使用预训练模型对序列标注任务建模。 ##### Q4.3.【阅读理解】`MapDatasets`的`map()`方法中对应的`batched=True`怎么理解,在阅读理解任务中为什么必须把参数`batched`设置为`True`? **A:** `batched=True`就是对整个 batch(这里不一定是训练中的 batch,理解为一组数据就可以)的数据进行 map,即 map 中的 trans_func 接受一组数据为输入,而非逐条进行 map。在阅读理解任务中,根据使用的 doc_stride 不同,一条样本可能被转换成多条 feature,对数据逐条 map 是行不通的,所以需要设置`batched=True`。 ##### Q4.4 【语义匹配】语义索引和语义匹配有什么区别? **A:** 语义索引要解决的核心问题是如何从海量 Doc 中通过 ANN 索引的方式快速、准确地找出与 query 相关的文档,语义匹配要解决的核心问题是对 query 和文档更精细的语义匹配信息建模。换个角度理解, [语义索引](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/semantic_indexing)是要解决搜索、推荐场景下的召回问题,而[语义匹配](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/slm/examples/text_matching)是要解决排序问题,两者要解决的问题不同,所采用的方案也会有很大不同,但两者间存在一些共通的技术点,可以互相借鉴。 ##### Q4.5 【解语】wordtag 模型如何自定义添加命名实体及对应词类? **A:** 其主要依赖于二次构造数据来进行 finetune,同时要更新 termtree 信息。wordtag 分为两个步骤: (1)通过 BIOES 体系进行分词; (2)将分词后的信息和 TermTree 进行匹配。 因此我们需要: (1)分词正确,这里可能依赖于 wordtag 的 finetune 数据,来让分词正确; (2)wordtag 里面也需要把分词正确后 term 打上相应的知识信息。wordtag 自定义 TermTree 的方式将在后续版本提供出来。 可参考[issue](https://github.com/PaddlePaddle/PaddleNLP/issues/822)。 ### ⭐️其他使用咨询 ##### Q5.1 在 CUDA11使用 PaddlNLP 报错? **A:** 在 CUDA11安装,可参考[issue](https://github.com/PaddlePaddle/PaddleNLP/issues/348),其他 CUDA 版本安装可参考 [官方文档](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/conda/linux-conda.html) ##### Q5.2 如何设置 parameter? **A:** 有多种方法: (1)可以通过`set_value()`来设置 parameter,`set_value()`的参数可以是`numpy`或者`tensor`。 ```python layer.weight.set_value( paddle.tensor.normal( mean=0.0, std=self.initializer_range if hasattr(self, "initializer_range") else self.ernie.config["initializer_range"], shape=layer.weight.shape)) ``` (2)通过`create_parameter()`设置参数。 ``` python class MyLayer(paddle.nn.Layer): def __init__(self): super(MyLayer, self).__init__() self._linear = paddle.nn.Linear(1, 1) w_tmp = self.create_parameter([1,1]) self.add_parameter("w_tmp", w_tmp) def forward(self, input): return self._linear(input) mylayer = MyLayer() for name, param in mylayer.named_parameters(): print(name, param) ``` ##### Q5.3 GPU 版的 Paddle 虽然能在 CPU 上运行,但是必须要有 GPU 设备吗? **A:** 不支持 GPU 的设备只能安装 CPU 版本的 PaddlePaddle。 GPU 版本的 PaddlePaddle 如果想只在 CPU 上运行,可以通过 `export CUDA_VISIBLE_DEVICES=-1` 来设置。 ##### Q5.4 如何指定用 CPU 还是 GPU 训练模型? **A:** 一般我们的训练脚本提供了 `--device` 选项,用户可以通过 `--device` 选择需要使用的设备。 具体而言,在 Python 文件中,我们可以通过·paddle.device.set_device()·,设置为 gpu 或者 cpu,可参考 [set_device 文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/device/set_device_cn.html#set-device)。 ##### Q5.5 动态图模型和静态图模型的预测结果一致吗? **A:** 正常情况下,预测结果应当是一致的。如果遇到不一致的情况,可以及时反馈给 PaddleNLP 的开发人员,我们进行处理。 ##### Q5.6 如何可视化 acc、loss 曲线图、模型网络结构图等? **A:** 可使用[VisualDL](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/03_VisualDL/index_cn.html)进行可视化。其中 acc、loss 曲线图的可视化可参考[Scalar——折线图组件](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/03_VisualDL/visualdl_usage_cn.html#scalar)使用指南,模型网络结构的可视化可参考[Graph——网络结构组件](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/03_VisualDL/visualdl_usage_cn.html#graph)使用指南。