diff --git a/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/README.md b/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/README.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1752b25c2dbdca1fc132394ae0216c78dffb2148 100644 --- a/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/README.md +++ b/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/README.md @@ -0,0 +1,126 @@ +# Paddle2ONNX 推理 + +# 目录 + +- [1. 简介](#1) +- [2. Paddle2ONNX推理过程](#2) + - [2.1 准备推理环境](#2.1) + - [2.2 模型转换](#2.2) + - [2.3 ONNX 推理](#2.3) +- [3. FAQ](#3) + +## 1. 简介 +Paddle2ONNX 支持将 PaddlePaddle 模型格式转化到 ONNX 模型格式,算子目前稳定支持导出 ONNX Opset 9~11,部分Paddle算子支持更低的ONNX Opset转换。 + +本文档主要介绍 MobileNetV3 模型如何转化为 ONNX 模型,并基于 ONNXRuntime 引擎预测。 + +更多细节可参考 [Paddle2ONNX官方教程](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/README_zh.md) + +## 2. Paddle2ONNX推理过程 +### 2.1 准备推理环境 + +需要准备 Paddle2ONNX 模型转化环境,和 ONNX 模型预测环境 + +- 安装 Paddle2ONNX +``` +python3 -m pip install paddle2onnx +``` + +- 安装 ONNXRuntime +``` +# 建议安装 1.9.0 版本,可根据环境更换版本号 +python3 -m pip install onnxruntime==1.9.0 +``` + +- 下载代码 +```bash +git clone https://github.com/PaddlePaddle/models.git +cd models/tutorials/mobilenetv3_prod/Step6 +``` + +### 2.2 模型转换 + + +- Paddle 模型动转静导出 + +使用下面的命令完成`mobilenet_v3_net`模型的动转静导出。 + +```bash +#下载预训练好的参数 +wget https://paddle-model-ecology.bj.bcebos.com/model/mobilenetv3_reprod/mobilenet_v3_small_pretrained.pdparams +#生成推理模型 +python tools/export_model.py --pretrained=./mobilenet_v3_small_pretrained.pdparams --save-inference-dir="./mobilenet_v3_small_infer" --model=mobilenet_v3_small +``` + +最终在`mobilenet_v3_small_infer/`文件夹下会生成下面的3个文件。 + +``` +mobilenet_v3_small_infer + |----inference.pdiparams : 模型参数文件 + |----inference.pdmodel : 模型结构文件 + |----inference.pdiparams.info: 模型参数信息文件 +``` + +- ONNX 模型转换 + +使用 Paddle2ONNX 将Paddle静态图模型转换为ONNX模型格式: + +``` +paddle2onnx --model_dir=./mobilenetv3_model/ \ +--model_filename=inference.pdmodel \ +--params_filename=inference.pdiparams \ +--save_file=./inference/mobilenetv3_model/model.onnx \ +--opset_version=10 \ +--enable_onnx_checker=True +``` + +执行完毕后,ONNX 模型会被保存在 `./inference/mobilenetv3_model/` 路径下 + +更多关于参数的用法,可参考 [Paddle2ONNX官方教程](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/README_zh.md) + + +### 2.3 ONNX 推理 + +ONNX模型测试步骤如下: + +- Step1:初始化`ONNXRuntime`库并配置相应参数, 并进行预测 +- Step2:`ONNXRuntime`预测结果和`Paddle Inference`预测结果对比 + +对于下面的图像进行预测 + +
+ +
+ +执行如下命令: + +``` +python3 deploy/onnx_python/infer.py \ +--onnx_file ./inference/mobilenetv3_model/model.onnx \ +--params_file ./mobilenet_v3_small_pretrained.pdparams \ +--img_path ./images/demo.jpg +``` + +在`ONNXRuntime`输出结果如下。 + +``` +ONNXRuntime predict: + +class_id: 8, prob: 0.9091270565986633 +``` + +表示预测的类别ID是`8`,置信度为`0.909`,该结果与基于训练引擎的结果完全一致 + +`ONNXRuntime`预测结果和`Paddle Inference`预测结果对比,如下。 + +``` +ONNXRuntime and Paddle Inference result diff: + +The difference of results between ONNXRuntime and Paddle looks good! +max_abs_diff: 1.5646219e-07 +``` + +从`ONNXRuntime`和`Paddle Inference`的预测结果diff可见,两者的结果几乎完全一致 + + +## 3. FAQ diff --git a/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/infer.py b/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/infer.py new file mode 100644 index 0000000000000000000000000000000000000000..aad3e7ab9b1b3e8f625b28aed6cb02e82d64a5d5 --- /dev/null +++ b/tutorials/mobilenetv3_prod/Step6/deploy/onnx_python/infer.py @@ -0,0 +1,105 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import argparse +import numpy as np +from PIL import Image + +import paddle +import paddle.nn as nn +from onnxruntime import InferenceSession + +# 从模型代码中导入模型 +from paddlevision.models import mobilenet_v3_small +from presets import ClassificationPresetEval + + +def infer(): + # Step1:初始化ONNXRuntime库并配置相应参数, 并进行预测 + # 加载ONNX模型 + sess = InferenceSession(FLAGS.onnx_file) + + # define transforms + input_shape = sess.get_inputs()[0].shape[2:] + eval_transforms = ClassificationPresetEval( + crop_size=input_shape, resize_size=FLAGS.crop_size) + # 准备输入 + with open(FLAGS.img_path, 'rb') as f: + img = Image.open(f).convert('RGB') + + img = eval_transforms(img) + img = np.expand_dims(img, axis=0) + + # 模型预测 + ort_outs = sess.run(output_names=None, + input_feed={sess.get_inputs()[0].name: img}) + + output = ort_outs[0] + class_id = output.argmax() + prob = output[0][class_id] + print("ONNXRuntime predict: ") + print(f"class_id: {class_id}, prob: {prob}") + + # Step2:ONNXRuntime预测结果和Paddle Inference预测结果对比 + # 实例化 Paddle 模型 + model = mobilenet_v3_small(pretrained=FLAGS.params_file) + model = nn.Sequential(model, nn.Softmax()) + + # 将模型设置为推理状态 + model.eval() + + # 对比ONNXRuntime和Paddle预测的结果 + paddle_outs = model(paddle.to_tensor(img)) + + diff = ort_outs[0] - paddle_outs.numpy() + max_abs_diff = np.fabs(diff).max() + print("ONNXRuntime and Paddle Inference result diff: ") + if max_abs_diff < 1e-05: + print( + "The difference of results between ONNXRuntime and Paddle looks good!" + ) + else: + relative_diff = max_abs_diff / np.fabs(paddle_outs.numpy()).max() + if relative_diff < 1e-05: + print( + "The difference of results between ONNXRuntime and Paddle looks good!" + ) + else: + print( + "The difference of results between ONNXRuntime and Paddle looks bad!" + ) + print('relative_diff: ', relative_diff) + print('max_abs_diff: ', max_abs_diff) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--onnx_file', + type=str, + default="model.onnx", + help="onnx model filename") + parser.add_argument( + '--params_file', + type=str, + default="model.pdparams", + help="params filename") + parser.add_argument( + '--img_path', type=str, default="image.jpg", help="image filename") + parser.add_argument('--crop_size', default=256, help='crop_szie') + + FLAGS = parser.parse_args() + + infer() diff --git a/tutorials/tipc/paddle2onnx/images/py2onnx_inference_pipeline.png b/tutorials/tipc/paddle2onnx/images/py2onnx_inference_pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..f41ab5247c66b9ce98620e3aeedd0cc7f07ca6a4 Binary files /dev/null and b/tutorials/tipc/paddle2onnx/images/py2onnx_inference_pipeline.png differ diff --git a/tutorials/tipc/paddle2onnx/paddle2onnx.md b/tutorials/tipc/paddle2onnx/paddle2onnx.md index f9968114bda8a071d795cf5776c5d521245a950d..390cbe1d9e8a989428792ba627ce4bc34697086a 100644 --- a/tutorials/tipc/paddle2onnx/paddle2onnx.md +++ b/tutorials/tipc/paddle2onnx/paddle2onnx.md @@ -2,6 +2,198 @@ # 目录 -- [1. 简介](#1---) -- [2. Paddle2ONNX功能开发](#2---) -- [3. FAQ](#3---) +- [1. 简介](#1) +- [2. Paddle2ONNX推理过程开发](#2) + - [2.1 准备环境](#2.1) + - [2.2 转换模型](#2.2) + - [2.3 开发数据预处理程序](#2.3) + - [2.4 开发ONNX模型推理程序](#2.4) + - [2.5 开发数据后处理程序](#2.5) + - [2.6 验证ONNX推理结果正确性](#2.6) +- [3. FAQ](#3) + +## 1. 简介 +Paddle2ONNX 支持将 PaddlePaddle 模型格式转化到 ONNX 模型格式,算子目前稳定支持导出 ONNX Opset 9~11,部分Paddle算子支持更低的ONNX Opset转换。 + +本文档主要介绍飞桨模型如何转化为 ONNX 模型,并基于 ONNXRuntime 引擎的推理过程开发。 + +更多细节可参考 [Paddle2ONNX官方教程](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/README_zh.md) + +## 2. Paddle2ONNX推理过程开发 + +基于Paddle2ONNX的推理过程可以分为6个步骤,如下图所示。 + +
+ +
+ +其中设置了2个核验点,分别为 + +* 准备推理模型 +* 验证推理结果正确性 + + +### 2.1 准备环境 + +**【数据】** + +从验证集或者测试集中抽出至少一张图像,用于后续推理过程验证。 + +**【环境】** + +需要准备 Paddle2ONNX 模型转化环境,和 ONNX 模型预测环境 + +- 安装 Paddle2ONNX +``` +python3 -m pip install paddle2onnx +``` + +- 安装 ONNXRuntime +``` +# 建议安装 1.9.0 版本,可根据环境更换版本号 +python3 -m pip install onnxruntime==1.9.0 +``` + +### 2.2 转换模型 + +- Paddle 模型动转静导出 + +**【基本内容】** + +`模型动转静`方法可以将训练得到的动态图模型转化为用于推理的静态图模型,具体可参考[Linux GPU/CPU 模型推理开发文档](https://github.com/PaddlePaddle/models/blob/release%2F2.2/docs/tipc/train_infer_python/infer_python.md#2.2)中第2.2章节。 + +**【实战】** + +参考MobileNetV3的Paddle2ONNX [说明文档](../../mobilenetv3_prod/Step6/deploy/onnx_python/README.md) 中的第2.2章节 + + +- ONNX 模型转换 + +**【基本内容】** + +使用 Paddle2ONNX 将Paddle静态图模型转换为ONNX模型格式: + +``` +paddle2onnx --model_dir=${your_inference_model_dir} +--model_filename=${your_pdmodel_file} +--params_filename=${your_pdiparams_file} +--save_file=${output_file} +--opset_version=10 +--enable_onnx_checker=True +``` + +- 参数说明: + - ${your_inference_model_dir}指的是Paddle模型所在目录. + - ${your_pdmodel_file}指的是网络结构的文件. + - ${your_pdiparams_file}指的是模型参数的文件. + - ${output_file}指的是需要导出的onnx模型. + - ${opset_version}指的是ONNX Opset,目前稳定支持9~11,默认是10. + - ${enable_onnx_checker}指的是否检查导出为ONNX模型的正确性. + +更多关于参数的用法,可参考 [Paddle2ONNX官方教程](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/README_zh.md) + +**【实战】** + +参考MobileNetV3的Paddle2ONNX [说明文档](../../mobilenetv3_prod/Step6/deploy/onnx_python/README.md) 中的第2.2章节 + +**【核验】** + +执行完毕后,将产出${output_file} ONNX 模型文件,其中文件后缀为.onnx。 + + +### 2.3 开发数据预处理程序 + +**【基本内容】** + +读取指定图像,对其进行数据变换,转化为符合模型推理所需要的输入格式。 + +使用ONNX模型进行推理时,使用的数据预处理方法,和使用Paddle Inference进行推理时的预处理方法一样。 + + +### 2.4 开发ONNX模型推理程序 + +ONNX作为开源的神经网络交换格式,得到大多数推理引擎的部署支持。在本文档中我们采用微软开源的ONNXRuntime推理引擎,进行转换后模型的正确性较验。 + +**【基本内容】** + +初始化`ONNXRuntime`库并配置相应参数, 并进行预测 + +``` +from onnxruntime import InferenceSession + +# 加载ONNX模型 +sess = InferenceSession('${your_onnx_model_name}.onnx') + +# 模型预测 +ort_outs = sess.run(output_names=None, input_feed={sess.get_inputs()[0].name: ${input_data}) +``` + +**【注意事项】** + +${input_data} 是预处理后的数据,作为网络的输入,数据是ndarray类型。 + + +### 2.5 开发数据后处理程序 + +在完成ONNX模型进行推理后,基于不同的任务,需要对网络的输出进行后处理,这部分和使用Paddle Inference进行模型推理后的后处理方法一样。 + + +### 2.6 验证ONNX推理结果正确性 + +**【基本内容】** + +`ONNXRuntime`预测结果和`Paddle Inference`预测结果对比 + +``` +import os +import time +import paddle + +# 从模型代码中导入模型 +from paddlevision.models import mobilenet_v3_small + +# 实例化模型 +model = mobilenet_v3_small('${your_paddle_model_name}.pdparams') + +# 将模型设置为推理状态 +model.eval() + +# 对比ONNXRuntime和Paddle预测的结果 +paddle_outs = model(paddle.to_tensor(${input_data})) + +diff = ort_outs[0] - paddle_outs.numpy() +max_abs_diff = np.fabs(diff).max() +if max_abs_diff < 1e-05: + print("The difference of results between ONNXRuntime and Paddle looks good!") +else: + relative_diff = max_abs_diff / np.fabs(paddle_outs.numpy()).max() + if relative_diff < 1e-05: + print("The difference of results between ONNXRuntime and Paddle looks good!") + else: + print("The difference of results between ONNXRuntime and Paddle looks bad!") + print('relative_diff: ', relative_diff) +print('max_abs_diff: ', max_abs_diff) + +``` + +**【注意事项】** + +${input_data} 是预处理后的数据,和 ONNXRuntime 的输入一样。 + +ort_outs 是 ONNXRuntime 的输出结果 + +paddlevision 模块位于MobileNetV3_prod/Step6目录下 + + +**【核验】** + +执行完毕后,如果 max_abs_diff < 1e-05,那么意味着,ONNXRuntime和Paddle Inference的输出是一致的。 + + +**【实战】** + +参考MobileNetV3的Paddle2ONNX [说明文档](../../mobilenetv3_prod/Step6/deploy/onnx_python/README.md) 中的第2.3章节 + +## 3. FAQ + +如果您在使用该文档完成Paddle模型转ONNX的过程中遇到问题,可以给在[这里](https://github.com/PaddlePaddle/Paddle2ONNX/issues)提一个ISSUE,我们会高优跟进。