# PaddleClas 相关常见问题汇总 - 2021 第2季

## 写在前面

* 我们收集整理了开源以来在issues和用户群中的常见问题并且给出了简要解答，旨在为广大用户提供一些参考，也希望帮助大家少走一些弯路。

* 图像分类、识别、检索领域大佬众多，模型和论文更新速度也很快，本文档回答主要依赖有限的项目实践，难免挂一漏万，如有遗漏和不足，也希望有识之士帮忙补充和修正，万分感谢。

## 目录
* [近期更新](#近期更新)(2021.09.08)
* [精选](#精选)
* [1. 理论篇](#1.理论篇)
    * [1.1 PaddleClas基础知识](#1.1PaddleClas基础知识)
    * [1.2 骨干网络和预训练模型库](#1.2骨干网络和预训练模型库)
    * [1.3 图像分类](#1.3图像分类)
    * [1.4 通用检测模块](#1.4通用检测模块)
    * [1.5 图像识别模块](#1.5图像识别模块)
    * [1.6 检索模块](#1.6检索模块)
* [2. 实战篇](#2.实战篇)
    * [2.1 训练与评估共性问题](#2.1训练与评估共性问题)
    * [2.2 图像分类](#2.2图像分类)
    * [2.3 通用检测模块](#2.3通用检测模块)
    * [2.4 图像识别模块](#2.4图像识别模块)
    * [2.5 检索模块](#2.5检索模块)
    * [2.6 模型预测部署](#2.6模型预测部署)

<a name="近期更新"></a>
## 近期更新

#### Q2.1.7: 在训练时，出现如下报错信息：`ERROR: Unexpected segmentation fault encountered in DataLoader workers.`，如何排查解决问题呢？
**A**：尝试将训练配置文件中的字段 `num_workers` 设置为 `0`；尝试将训练配置文件中的字段 `batch_size` 调小一些；检查数据集格式和配置文件中的数据集路径是否正确。

#### Q2.1.8: 如何在训练时使用 `Mixup` 和 `Cutmix` ？
**A**：
* `Mixup` 的使用方法请参考 [Mixup](../../../ppcls/configs/ImageNet/DataAugment/ResNet50_Mixup.yaml#L63-L65)；`Cuxmix` 请参考 [Cuxmix](../../../ppcls/configs/ImageNet/DataAugment/ResNet50_Cutmix.yaml#L63-L65)。

* 使用 `Mixup` 或 `Cutmix` 做训练时无法计算训练的精度（Acc）指标，因此需要在配置文件中取消 `Metric.Train.TopkAcc` 字段，可参考 [Metric.Train.TopkAcc](../../../ppcls/configs/ImageNet/DataAugment/ResNet50_Cutmix.yaml#L125-L128)。

#### Q2.1.9: 训练配置yaml文件中，字段 `Global.pretrain_model` 和 `Global.checkpoints` 分别用于配置什么呢？
**A**：
* 当需要 `fine-tune` 时，可以通过字段 `Global.pretrain_model` 配置预训练模型权重文件的路径，预训练模型权重文件后缀名通常为 `.pdparams`；
* 在训练过程中，训练程序会自动保存每个epoch结束时的断点信息，包括优化器信息 `.pdopt` 和模型权重信息 `.pdparams`。在训练过程意外中断等情况下，需要恢复训练时，可以通过字段 `Global.checkpoints` 配置训练过程中保存的断点信息文件，例如通过配置 `checkpoints: ./output/ResNet18/epoch_18` 即可恢复18epoch训练结束时的断点信息，PaddleClas将自动加载 `epoch_18.pdopt` 和 `epoch_18.pdparams`，从19epoch继续训练。

#### Q2.6.3: 如何将模型转为 `ONNX` 格式？
**A**：Paddle支持两种转ONNX格式模型的方式，且依赖于 `paddle2onnx` 工具，首先需要安装 `paddle2onnx`：

```shell
pip install paddle2onnx
```

* 从 inference model 转为 ONNX 格式模型：

    以动态图导出的 `combined` 格式 inference model（包含 `.pdmodel` 和 `.pdiparams` 两个文件）为例，使用以下命令进行模型格式转换：
    ```shell
    paddle2onnx --model_dir ${model_path}  --model_filename  ${model_path}/inference.pdmodel --params_filename ${model_path}/inference.pdiparams --save_file ${save_path}/model.onnx --enable_onnx_checker True
    ```
    上述命令中：
    * `model_dir`：该参数下需要包含 `.pdmodel` 和 `.pdiparams` 两个文件；
    * `model_filename`：该参数用于指定参数 `model_dir` 下的 `.pdmodel` 文件路径；
    * `params_filename`：该参数用于指定参数 `model_dir` 下的 `.pdiparams` 文件路径；
    * `save_file`：该参数用于指定转换后的模型保存目录路径。

    关于静态图导出的非 `combined` 格式的 inference model（通常包含文件 `__model__` 和多个参数文件）转换模型格式，以及更多参数说明请参考 paddle2onnx 官方文档 [paddle2onnx](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/README_zh.md#%E5%8F%82%E6%95%B0%E9%80%89%E9%A1%B9)。

* 直接从模型组网代码导出ONNX格式模型：

    以动态图模型组网代码为例，模型类为继承于 `paddle.nn.Layer` 的子类，代码如下所示：

    ```python
    import paddle
    from paddle.static import InputSpec

    class SimpleNet(paddle.nn.Layer):
        def __init__(self):
            pass
        def forward(self, x):
            pass

    net = SimpleNet()
    x_spec = InputSpec(shape=[None, 3, 224, 224], dtype='float32', name='x')
    paddle.onnx.export(layer=net, path="./SimpleNet", input_spec=[x_spec])
    ```
    其中：
    * `InputSpec()` 函数用于描述模型输入的签名信息，包括输入数据的 `shape`、`type` 和 `name`（可省略）；
    * `paddle.onnx.export()` 函数需要指定模型组网对象 `net`，导出模型的保存路径 `save_path`，模型的输入数据描述 `input_spec`。

    需要注意，`paddlepaddle` 版本需大于 `2.0.0`。关于 `paddle.onnx.export()` 函数的更多参数说明请参考[paddle.onnx.export](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/onnx/export_cn.html#export)。

#### Q2.5.4: 在 build 检索底库时，参数 `pq_size` 应该如何设置？
**A**：`pq_size` 是PQ检索算法的参数。PQ检索算法可以简单理解为“分层”检索算法，`pq_size` 是每层的“容量”，因此该参数的设置会影响检索性能，不过，在底库总数据量不太大（小于10000张）的情况下，这个参数对性能的影响很小，因此对于大多数使用场景而言，在构建底库时无需修改该参数。关于PQ检索算法的更多内容，可以查看相关[论文](https://lear.inrialpes.fr/pubs/2011/JDS11/jegou_searching_with_quantization.pdf)。

<a name="精选"></a>
## 精选

<a name="1.理论篇"></a>
## 1. 理论篇

<a name="1.1PaddleClas基础知识"></a>
### 1.1 PaddleClas基础知识

#### Q1.1.1 PaddleClas和PaddleDetection区别
**A**：PaddleClas是一个兼主体检测、图像分类、图像检索于一体的图像识别repo，用于解决大部分图像识别问题，用户可以很方便的使用PaddleClas来解决小样本、多类别的图像识别问题。PaddleDetection提供了目标检测、关键点检测、多目标跟踪等能力，方便用户定位图像中的感兴趣的点和区域，被广泛应用于工业质检、遥感图像检测、无人巡检等项目。

#### Q1.1.3: Momentum 优化器中的 momentum 参数是什么意思呢？
**A**: Momentum 优化器是在 SGD 优化器的基础上引入了“动量”的概念。在 SGD 优化器中，在 `t+1` 时刻，参数 `w` 的更新可表示为：
```latex
w_t+1 = w_t - lr * grad
```
其中，`lr` 为学习率，`grad` 为此时参数 `w` 的梯度。在引入动量的概念后，参数 `w` 的更新可表示为：
```latex
v_t+1 = m * v_t + lr * grad
w_t+1 = w_t - v_t+1
```
其中，`m` 即为动量 `momentum`，表示累积动量的加权值，一般取 `0.9`，当取值小于 `1` 时，则越早期的梯度对当前的影响越小，例如，当动量参数 `m` 取 `0.9` 时，在 `t` 时刻，`t-5` 的梯度加权值为 `0.9 ^ 5 = 0.59049`，而 `t-2` 时刻的梯度加权值为 `0.9 ^ 2 = 0.81`。因此，太过“久远”的梯度信息对当前的参考意义很小，而“最近”的历史梯度信息对当前影响更大，这也是符合直觉的。

<div align="center">
    <img src="../../images/faq/momentum.jpeg" width="400">
</div>

通过引入动量的概念，在参数更新时考虑了历史更新的影响，因此可以加快收敛速度，也改善了 `SGD` 优化器带来的损失（cost、loss）震荡问题。

#### Q1.1.4: PaddleClas 是否有 `Fixing the train-test resolution discrepancy` 这篇论文的实现呢？
**A**: 目前 PaddleClas 没有实现。如果需要，可以尝试自己修改代码。简单来说，该论文所提出的思想是使用较大分辨率作为输入，对已经训练好的模型最后的FC层进行fine-tune。具体操作上，首先在较低分辨率的数据集上对模型网络进行训练，完成训练后，对网络除最后的FC层外的其他层的权重设置参数 `stop_gradient=True`，然后使用较大分辨率的输入对网络进行fine-tune训练。

<a name="1.2骨干网络和预训练模型库"></a>
### 1.2 骨干网络和预训练模型库

<a name="1.3图像分类"></a>
### 1.3 图像分类

#### Q1.3.1: PaddleClas有提供调整图片亮度，对比度，饱和度，色调等方面的数据增强吗？
**A**：PaddleClas提供了多种数据增广方式， 可分为3类：
1. 图像变换类： AutoAugment, RandAugment;  
2. 图像裁剪类： CutOut、RandErasing、HideAndSeek、GridMask；
3. 图像混叠类：Mixup, Cutmix.

其中，RandAngment提供了多种数据增强方式的随机组合，可以满足亮度、对比度、饱和度、色调等多方面的数据增广需求。

<a name="1.4通用检测模块"></a>
### 1.4 通用检测模块

#### Q1.4.1 主体检测是每次只输出一个主体检测框吗？
**A**：主体检测这块的输出数量是可以通过配置文件配置的。在配置文件中Global.threshold控制检测的阈值，小于该阈值的检测框被舍弃，Global.max_det_results控制最大返回的结果数，这两个参数共同决定了输出检测框的数量。

#### Q1.4.2 训练主体检测模型的数据是如何选择的？换成更小的模型会有损精度吗？
**A**：训练数据是在COCO、Object365、RPC、LogoDet等公开数据集中随机抽取的子集。目前我们在2.3版本中推出了超轻量的主体检测模型，具体信息可以参考[主体检测](../image_recognition_pipeline/mainbody_detection.md#2-模型选择)。关于主体检测模型的更多信息请参考[主体检测](../image_recognition_pipeline/mainbody_detection.md)。

#### Q1.4.3: 目前使用的主体检测模型检测在某些场景中会有误检？
**A**：目前的主体检测模型训练时使用了COCO、Object365、RPC、LogoDet等公开数据集，如果被检测数据是类似工业质检等于常见类别差异较大的数据，需要基于目前的检测模型重新微调训练。

<a name="1.5图像识别模块"></a>
### 1.5 图像识别模块

#### Q1.5.1 使用`circle loss`还需加`triplet loss`吗？
**A**：`circle loss`是统一了样本对学习和分类学习的两种形式，如果是分类学习的形式的话，可以增加`triplet loss`。

#### Q1.5.2 如果不是识别开源的四个方向的图片，该使用哪个识别模型？
**A**：建议使用商品识别模型，一来是因为商品覆盖的范围比较广，被识别的图片是商品的概率更大，二来是因为商品识别模型的训练数据使用了5万类别的数据，泛化能力更好，特征会更鲁棒一些。

#### Q1.5.3 最后使用512维的向量，为什么不用1024或者其他维度的呢？
**A**：使用维度小的向量，为了加快计算，在实际使用过程中，可能使用128甚至更小。一般来说，512的维度已经够大，能充分表示特征了。

<a name="1.6检索模块"></a>
### 1.6 检索模块

#### Q1.6.1 PaddleClas目前使用的Möbius向量检索算法支持类似于faiss的那种index.add()的功能吗? 另外，每次构建新的图都要进行train吗？这里的train是为了检索加速还是为了构建相似的图？
**A**：目前在release/2.3分支已经支持faiss检索模块，并且不再支持Möbius。关于Möbius提供的检索算法，是一种基于图的近似最近邻搜索算法，目前支持两种距离计算方式：inner product和L2 distance，但是Möbius暂不支持faiss中提供的index.add功能，如果需要增加检索库的内容，需要从头重新构建新的index. 在每次构建index时，检索算法内部执行的操作是一种类似于train的过程，不同于faiss提供的train接口。因此需要faiss模块的话，可以使用release/2.3分支，需要Möbius的话，目前需要回退到release/2.2分支。

#### Q1.6.2: PaddleClas 图像识别用于 Eval 的配置文件中，`Query` 和 `Gallery` 配置具体是用于做什么呢？
**A**: `Query` 与 `Gallery` 均为数据集配置，其中 `Gallery` 用于配置底库数据，`Query` 用于配置验证集。在进行 Eval 时，首先使用模型对 `Gallery` 底库数据进行前向计算特征向量，特征向量用于构建底库，然后模型对 `Query` 验证集中的数据进行前向计算特征向量，再与底库计算召回率等指标。

<a name="2.实战篇"></a>
## 2. 实战篇

<a name="2.1训练与评估共性问题"></a>
### 2.1 训练与评估共性问题

#### Q2.1.1 PaddleClas 的`train_log`文件在哪里?
**A**：在保存权重的路径中存放了`train.log`。

#### Q2.1.2 模型训练出nan，为什么？
**A**：
1.确保正确加载预训练模型, 最简单的加载方式添加参数`-o Arch.pretrained=True`即可；
2.模型微调时，学习率不要太大，如设置0.001就好。

#### Q2.1.3 可以对视频中每一帧画面进行逐帧预测吗？
**A**：可以，但目前PaddleClas并不支持视频输入。可以尝试修改一下PaddleClas代码，或者预先将视频逐帧转为图像存储，再使用PaddleClas进行预测。

#### Q2.1.4: 数据预处理中，不想对输入数据进行裁剪，该如何设置？或者如何设置剪裁的尺寸。
**A**: PaddleClas 支持的数据预处理算子可在这里查看：`ppcls/data/preprocess/__init__.py`，所有支持的算子均可在配置文件中进行配置，配置的算子名称需要和算子类名一致，参数与对应算子类的构造函数参数一致。如不需要对图像裁剪，则可去掉 `CropImage`、`RandCropImage`，使用 `ResizeImage` 替换即可，可通过其参数设置不同的resize方式， 使用 `size` 参数则直接将图像缩放至固定大小，使用`resize_short` 参数则会维持图像宽高比进行缩放。设置裁剪尺寸时，可通过 `CropImage` 算子的 `size` 参数，或 `RandCropImage` 算子的 `size` 参数。

#### Q2.1.5: PaddlePaddle 安装后，使用报错，无法导入 paddle 下的任何模块（import paddle.xxx），是为什么呢？
**A**: 首先可以使用以下代码测试 Paddle 是否安装正确：
```python
import paddle
paddle.utils.install_check.run_check(）
```
正确安装时，通常会有如下提示：
```
PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.
```
如未能安装成功，则会有相应问题的提示。
另外，在同时安装CPU版本和GPU版本Paddle后，由于两个版本存在冲突，需要将两个版本全部卸载，然后重新安装所需要的版本。

#### Q2.1.6: 使用PaddleClas训练时，如何设置仅保存最优模型？不想保存中间模型。
**A**: PaddleClas在训练过程中，会保存/更新以下三类模型：
1. 最新的模型（`latest.pdopt`， `latest.pdparams`，`latest.pdstates`），当训练意外中断时，可使用最新保存的模型恢复训练；
2. 最优的模型（`best_model.pdopt`，`best_model.pdparams`，`best_model.pdstates`）；
3. 训练过程中，一个epoch结束时的断点（`epoch_xxx.pdopt`，`epoch_xxx.pdparams`，`epoch_xxx.pdstates`）。训练配置文件中 `Global.save_interval` 字段表示该模型的保存间隔。将该字段设置大于总epochs数，则不再保存中间断点模型。

#### Q2.1.7: 在训练时，出现如下报错信息：`ERROR: Unexpected segmentation fault encountered in DataLoader workers.`，如何排查解决问题呢？
**A**：尝试将训练配置文件中的字段 `num_workers` 设置为 `0`；尝试将训练配置文件中的字段 `batch_size` 调小一些；检查数据集格式和配置文件中的数据集路径是否正确。

#### Q2.1.8: 如何在训练时使用 `Mixup` 和 `Cutmix` ？
**A**：
* `Mixup` 的使用方法请参考 [Mixup](../../../ppcls/configs/ImageNet/DataAugment/ResNet50_Mixup.yaml#L63-L65)；`Cuxmix` 请参考 [Cuxmix](../../../ppcls/configs/ImageNet/DataAugment/ResNet50_Cutmix.yaml#L63-L65)。

* 使用 `Mixup` 或 `Cutmix` 做训练时无法计算训练的精度（Acc）指标，因此需要在配置文件中取消 `Metric.Train.TopkAcc` 字段，可参考 [Metric.Train.TopkAcc](../../../ppcls/configs/ImageNet/DataAugment/ResNet50_Cutmix.yaml#L125-L128)。

#### Q2.1.9: 训练配置yaml文件中，字段 `Global.pretrain_model` 和 `Global.checkpoints` 分别用于配置什么呢？
**A**：
* 当需要 `fine-tune` 时，可以通过字段 `Global.pretrain_model` 配置预训练模型权重文件的路径，预训练模型权重文件后缀名通常为 `.pdparams`；
* 在训练过程中，训练程序会自动保存每个epoch结束时的断点信息，包括优化器信息 `.pdopt` 和模型权重信息 `.pdparams`。在训练过程意外中断等情况下，需要恢复训练时，可以通过字段 `Global.checkpoints` 配置训练过程中保存的断点信息文件，例如通过配置 `checkpoints: ./output/ResNet18/epoch_18` 即可恢复18epoch训练结束时的断点信息，PaddleClas将自动加载 `epoch_18.pdopt` 和 `epoch_18.pdparams`，从19epoch继续训练。

<a name="2.2图像分类"></a>
### 2.2 图像分类

#### Q2.2.1 在SSLD中，大模型在500M数据上预训练后蒸馏小模型，然后在1M数据上蒸馏finetune小模型，具体步骤是怎样做的？
**A**：步骤如下：
1. 基于facebook开源的`ResNeXt101-32x16d-wsl`模型去蒸馏得到了`ResNet50-vd`模型；
2. 用这个`ResNet50-vd`，在500W数据集上去蒸馏`MobilNetV3`；
3. 考虑到500W的数据集的分布和100W的数据分布不完全一致，所以这块，在100W上的数据上又finetune了一下，精度有微弱的提升。

#### Q2.2.2 训练SwinTransformer，loss出现nan
**A**：训练SwinTransformer时，请使用版本大于等于 `2.1.1` 的 `Paddle`，并且加载我们提供的预训练模型，学习率也不宜过大。

<a name="2.3通用检测模块"></a>
### 2.3 通用检测模块

#### Q2.3.1 为什么有一些图片检测出的结果就是原图？
**A**：主体检测模型会返回检测框，但事实上为了让后续的识别模型更加准确，在返回检测框的同时也返回了原图。后续会根据原图或者检测框与库中的图片的相似度排序，相似度最高的库中图片的标签即为被识别图片的标签。

#### Q2.3.2：在直播场景中，需要提供一个直播即时识别画面，能够在延迟几秒内找到特征目标物并用框圈起，这个可以实现吗？
**A**：要达到实时的检测效果，需要检测速度达到实时性的要求；PP-YOLO是Paddle团队提供的轻量级目标检测模型，检测速度和精度达到了很好的平衡，可以试试PP-YOLO来做检测. 关于PP-YOLO的使用，可以参照：[PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.1/configs/ppyolo/README_cn.md)。

#### Q2.3.3: 对于未知的标签，加入gallery dataset可以用于后续的分类识别（无需训练），但是如果前面的检测模型对于未知的标签无法定位检测出来，是否还是要训练前面的检测模型？
**A**：如果检测模型在自己的数据集上表现不佳，需要在自己的检测数据集上再finetune下

<a name="2.4图像识别模块"></a>
### 2.4 图像识别模块

#### Q2.4.1: 识别模块预测时报`Illegal instruction`错？
**A**：如果使用的是release/2.2分支，建议更新为release/2.3分支，在release/2.3分支中，我们使用faiss检索模块替换了Möbius检索模型，具体可以参考[向量检索教程](../../../deploy/vector_search/README.md)。如仍存在问题，可以在用户微信群中联系我们，也可以在GitHub提issue。

#### Q2.4.2: 识别模型怎么在预训练模型的基础上进行微调训练？
**A**：识别模型的微调训练和分类模型的微调训练类似，识别模型可以加载商品的预训练模型，训练过程可以参考[识别模型训练](../../zh_CN/models_training/recognition.md)，后续我们也会持续细化这块的文档。

#### Q2.4.3: 训练metric learning时，每个epoch中，无法跑完所有mini-batch，为什么？
**A**：在训练metric learning时，使用的Sampler是DistributedRandomIdentitySampler，该Sampler不会采样全部的图片，导致会让每一个epoch采样的数据不是所有的数据，所以无法跑完显示的mini-batch是正常现象。该问题在release/2.3分支已经优化，请更新到release/2.3使用。

#### Q2.4.4: 有些图片没有识别出结果，为什么？
**A**：在配置文件（如inference_product.yaml）中，`IndexProcess.score_thres`中会控制被识别的图片与库中的图片的余弦相似度的最小值。当余弦相似度小于该值时，不会打印结果。您可以根据自己的实际数据调整该值。

<a name="2.5检索模块"></a>
### 2.5 检索模块

#### Q2.5.1: 添加图片后建索引报`assert text_num >= 2`错？
**A**：请确保data_file.txt中图片路径和图片名称中间的间隔为单个table，而不是空格。

#### Q2.5.2: 新增底库数据需要重新构建索引吗？
**A**：从release/2.3分支起，我们使用faiss检索模块替换了Möbius检索模型，已经支持在不构建底库的前提下新增底库数据，具体可以参考[向量检索教程](../../../deploy/vector_search/README.md)。

#### Q2.5.3: Mac重新编译index.so时报错如下：clang: error: unsupported option '-fopenmp', 该如何处理？
**A**：如果使用的是release/2.2分支，建议更新为release/2.3分支，在release/2.3分支中，我们使用faiss检索模块替换了Möbius检索模型，具体可以参考[向量检索教程](../../../deploy/vector_search/README.md)。如仍存在问题，可以在用户微信群中联系我们，也可以在GitHub提issue。

#### Q2.5.4: 在 build 检索底库时，参数 `pq_size` 应该如何设置？
**A**：`pq_size` 是PQ检索算法的参数。PQ检索算法可以简单理解为“分层”检索算法，`pq_size` 是每层的“容量”，因此该参数的设置会影响检索性能，不过，在底库总数据量不太大（小于10000张）的情况下，这个参数对性能的影响很小，因此对于大多数使用场景而言，在构建底库时无需修改该参数。关于PQ检索算法的更多内容，可以查看相关[论文](https://lear.inrialpes.fr/pubs/2011/JDS11/jegou_searching_with_quantization.pdf)。

<a name="2.6模型预测部署"></a>
### 2.6 模型预测部署

#### Q2.6.1: hub serving方式启动某个模块，怎么添加该模块的参数呢？
**A**：具体可以参考[hub serving参数](../../../deploy/hubserving/clas/params.py)。

#### Q2.6.2: 导出inference模型进行预测部署，准确率异常，为什么呢？
**A**: 该问题通常是由于在导出时未能正确加载模型参数导致的，首先检查模型导出时的日志，是否存在类似下述内容：
```
UserWarning: Skip loading for ***. *** is not found in the provided dict.
```
如果存在，则说明模型权重未能加载成功，请进一步检查配置文件中的 `Global.pretrained_model` 字段，是否正确配置了模型权重文件的路径。模型权重文件后缀名通常为 `pdparams`，注意在配置该路径时无需填写文件后缀名。

#### Q2.6.3: 如何将模型转为 `ONNX` 格式？
**A**：Paddle支持两种转ONNX格式模型的方式，且依赖于 `paddle2onnx` 工具，首先需要安装 `paddle2onnx`：

```shell
pip install paddle2onnx
```

* 从 inference model 转为 ONNX 格式模型：

    以动态图导出的 `combined` 格式 inference model（包含 `.pdmodel` 和 `.pdiparams` 两个文件）为例，使用以下命令进行模型格式转换：
    ```shell
    paddle2onnx --model_dir ${model_path}  --model_filename  ${model_path}/inference.pdmodel --params_filename ${model_path}/inference.pdiparams --save_file ${save_path}/model.onnx --enable_onnx_checker True
    ```
    上述命令中：
    * `model_dir`：该参数下需要包含 `.pdmodel` 和 `.pdiparams` 两个文件；
    * `model_filename`：该参数用于指定参数 `model_dir` 下的 `.pdmodel` 文件路径；
    * `params_filename`：该参数用于指定参数 `model_dir` 下的 `.pdiparams` 文件路径；
    * `save_file`：该参数用于指定转换后的模型保存目录路径。

    关于静态图导出的非 `combined` 格式的 inference model（通常包含文件 `__model__` 和多个参数文件）转换模型格式，以及更多参数说明请参考 paddle2onnx 官方文档 [paddle2onnx](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/README_zh.md#%E5%8F%82%E6%95%B0%E9%80%89%E9%A1%B9)。

* 直接从模型组网代码导出ONNX格式模型：

    以动态图模型组网代码为例，模型类为继承于 `paddle.nn.Layer` 的子类，代码如下所示：

    ```python
    import paddle
    from paddle.static import InputSpec

    class SimpleNet(paddle.nn.Layer):
        def __init__(self):
            pass
        def forward(self, x):
            pass

    net = SimpleNet()
    x_spec = InputSpec(shape=[None, 3, 224, 224], dtype='float32', name='x')
    paddle.onnx.export(layer=net, path="./SimpleNet", input_spec=[x_spec])
    ```
    其中：
    * `InputSpec()` 函数用于描述模型输入的签名信息，包括输入数据的 `shape`、`type` 和 `name`（可省略）；
    * `paddle.onnx.export()` 函数需要指定模型组网对象 `net`，导出模型的保存路径 `save_path`，模型的输入数据描述 `input_spec`。

    需要注意，`paddlepaddle` 版本需大于 `2.0.0`。关于 `paddle.onnx.export()` 函数的更多参数说明请参考[paddle.onnx.export](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/onnx/export_cn.html#export)。
