From 15c0b8b44a9c999527cbdb6877ea446c61260e6c Mon Sep 17 00:00:00 2001 From: Guanghua Yu <742925032@qq.com> Date: Thu, 14 Jul 2022 13:53:10 +0800 Subject: [PATCH] cherry pick some pr (#1295) * fixed-docs (#1283) * [documentation] fix typos (#1287) * add YOLOv7 ACT example (#1291) Co-authored-by: leiqing <54695910+leiqing1@users.noreply.github.com> Co-authored-by: minghaoBD <79566150+minghaoBD@users.noreply.github.com> --- README.md | 2 +- example/auto_compression/README.md | 415 +++++++++++------- .../auto_compression/pytorch_yolov6/README.md | 4 +- .../auto_compression/pytorch_yolov7/README.md | 152 +++++++ .../configs/yolov7_qat_dis.yaml | 30 ++ .../pytorch_yolov7/configs/yolov7_reader.yaml | 27 ++ .../pytorch_yolov7/cpp_infer/CMakeLists.txt | 263 +++++++++++ .../pytorch_yolov7/cpp_infer/README.md | 51 +++ .../pytorch_yolov7/cpp_infer/compile.sh | 37 ++ .../pytorch_yolov7/cpp_infer/trt_run.cc | 116 +++++ .../auto_compression/pytorch_yolov7/eval.py | 151 +++++++ .../pytorch_yolov7/images/000000570688.jpg | Bin 0 -> 138365 bytes .../pytorch_yolov7/paddle_trt_infer.py | 322 ++++++++++++++ .../pytorch_yolov7/post_process.py | 173 ++++++++ .../pytorch_yolov7/post_quant.py | 104 +++++ .../auto_compression/pytorch_yolov7/run.py | 172 ++++++++ .../semantic_segmentation/README.md | 16 +- 17 files changed, 1861 insertions(+), 174 deletions(-) create mode 100644 example/auto_compression/pytorch_yolov7/README.md create mode 100644 example/auto_compression/pytorch_yolov7/configs/yolov7_qat_dis.yaml create mode 100644 example/auto_compression/pytorch_yolov7/configs/yolov7_reader.yaml create mode 100644 example/auto_compression/pytorch_yolov7/cpp_infer/CMakeLists.txt create mode 100644 example/auto_compression/pytorch_yolov7/cpp_infer/README.md create mode 100644 example/auto_compression/pytorch_yolov7/cpp_infer/compile.sh create mode 100644 example/auto_compression/pytorch_yolov7/cpp_infer/trt_run.cc create mode 100644 example/auto_compression/pytorch_yolov7/eval.py create mode 100644 example/auto_compression/pytorch_yolov7/images/000000570688.jpg create mode 100644 example/auto_compression/pytorch_yolov7/paddle_trt_infer.py create mode 100644 example/auto_compression/pytorch_yolov7/post_process.py create mode 100644 example/auto_compression/pytorch_yolov7/post_quant.py create mode 100644 example/auto_compression/pytorch_yolov7/run.py diff --git a/README.md b/README.md index 0e2ce025..5289fb2e 100755 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ PaddleSlim是一个专注于深度学习模型压缩的工具库,提供**低 - 支持代码无感知压缩:用户只需提供推理模型文件和数据,既可进行离线量化(PTQ)、量化训练(QAT)、稀疏训练等压缩任务。 - 支持自动策略选择,根据任务特点和部署环境特性:自动搜索合适的离线量化方法,自动搜索最佳的压缩策略组合方式。 - 发布[自然语言处理](example/auto_compression/nlp)、[图像语义分割](example/auto_compression/semantic_segmentation)、[图像目标检测](example/auto_compression/detection)三个方向的自动化压缩示例。 - - 发布`X2Paddle`模型自动化压缩方案:[YOLOv5](example/auto_compression/pytorch_yolov5)、[YOLOv6](example/auto_compression/pytorch_yolov6)、[HuggingFace](example/auto_compression/pytorch_huggingface)、[MobileNet](example/auto_compression/tensorflow_mobilenet)。 + - 发布`X2Paddle`模型自动化压缩方案:[YOLOv5](example/auto_compression/pytorch_yolov5)、[YOLOv6](example/auto_compression/pytorch_yolov6)、[YOLOv7](example/auto_compression/pytorch_yolov7)、[HuggingFace](example/auto_compression/pytorch_huggingface)、[MobileNet](example/auto_compression/tensorflow_mobilenet)。 - 升级量化功能 diff --git a/example/auto_compression/README.md b/example/auto_compression/README.md index c9c9a91d..e907908b 100644 --- a/example/auto_compression/README.md +++ b/example/auto_compression/README.md @@ -1,165 +1,250 @@ -# 自动化压缩工具ACT(Auto Compression Toolkit) - -## 简介 -PaddleSlim推出全新自动化压缩工具(ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。ACT自动化压缩工具主要特性如下: -- **『更便捷』**:开发者无需了解或修改模型源码,直接使用导出的预测模型进行压缩; -- **『更智能』**:开发者简单配置即可启动压缩,ACT工具会自动优化得到最好预测模型; -- **『更丰富』**:ACT中提供了量化训练、蒸馏、结构化剪枝、非结构化剪枝、多种离线量化方法及超参搜索等等,可任意搭配使用。 - - -## 环境准备 - -- 安装PaddlePaddle >= 2.3 (从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装) -- 安装PaddleSlim >=2.3 - -(1)安装paddlepaddle: -```shell -# CPU -pip install paddlepaddle -# GPU -pip install paddlepaddle-gpu -``` - -(2)安装paddleslim: -```shell -pip install paddleslim -``` - -## 快速上手 - -- 1.准备模型及数据集 - -```shell -# 下载MobileNet预测模型 -wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV1_infer.tar -tar -xf MobileNetV1_infer.tar -# 下载ImageNet小型数据集 -wget https://sys-p0.bj.bcebos.com/slim_ci/ILSVRC2012_data_demo.tar.gz -tar -xf ILSVRC2012_data_demo.tar.gz -``` - -- 2.运行 - -```python -# 导入依赖包 -import paddle -from PIL import Image -from paddle.vision.datasets import DatasetFolder -from paddle.vision.transforms import transforms -from paddleslim.auto_compression import AutoCompression -paddle.enable_static() -# 定义DataSet -class ImageNetDataset(DatasetFolder): - def __init__(self, path, image_size=224): - super(ImageNetDataset, self).__init__(path) - normalize = transforms.Normalize( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.120, 57.375]) - self.transform = transforms.Compose([ - transforms.Resize(256), - transforms.CenterCrop(image_size), transforms.Transpose(), - normalize - ]) - - def __getitem__(self, idx): - img_path, _ = self.samples[idx] - return self.transform(Image.open(img_path).convert('RGB')) - - def __len__(self): - return len(self.samples) - -# 定义DataLoader -train_dataset = ImageNetDataset("./ILSVRC2012_data_demo/ILSVRC2012/train/") -image = paddle.static.data( - name='inputs', shape=[None] + [3, 224, 224], dtype='float32') -train_loader = paddle.io.DataLoader(train_dataset, feed_list=[image], batch_size=32, return_list=False) -# 开始自动压缩 -ac = AutoCompression( - model_dir="./MobileNetV1_infer", - model_filename="inference.pdmodel", - params_filename="inference.pdiparams", - save_dir="MobileNetV1_quant", - config={'Quantization': {}, "HyperParameterOptimization": {'ptq_algo': ['avg'], 'max_quant_count': 3}}, - train_dataloader=train_loader, - eval_dataloader=train_loader) -ac.compress() -``` - -- 3.测试精度 - -测试压缩前模型的精度: -```shell -CUDA_VISIBLE_DEVICES=0 python ./image_classification/eval.py -### Eval Top1: 0.7171724759615384 -``` - -测试量化模型的精度: -```shell -CUDA_VISIBLE_DEVICES=0 python ./image_classification/eval.py --model_dir='MobileNetV1_quant' -### Eval Top1: 0.7166466346153846 -``` - -量化后模型的精度相比量化前的模型几乎精度无损,由于是使用的超参搜索的方法来选择的量化参数,所以每次运行得到的量化模型精度会有些许波动。 - -- 4.推理速度测试 -量化模型速度的测试依赖推理库的支持,所以确保安装的是带有TensorRT的PaddlePaddle。以下示例和展示的测试结果是基于Tesla V100、CUDA 10.2、python3.7得到的。 - -使用以下指令查看本地cuda版本,并且在[下载链接](https://paddleinference.paddlepaddle.org.cn/master/user_guides/download_lib.html#python)中下载对应cuda版本和对应python版本的paddlepaddle安装包。 -```shell -cat /usr/local/cuda/version.txt ### CUDA Version 10.2.89 -### 10.2.89 为cuda版本号,可以根据这个版本号选择需要安装的带有TensorRT的PaddlePaddle安装包。 -``` - -安装下载的whl包: -``` -### 这里通过wget下载到的是python3.7、cuda10.2的PaddlePaddle安装包,若您的环境和示例环境不同,请依赖您自己机器的环境下载对应的安装包,否则运行示例代码会报错。 -wget https://paddle-inference-lib.bj.bcebos.com/2.3.0/python/Linux/GPU/x86-64_gcc8.2_avx_mkl_cuda10.2_cudnn8.1.1_trt7.2.3.4/paddlepaddle_gpu-2.3.0-cp37-cp37m-linux_x86_64.whl -pip install paddlepaddle_gpu-2.3.0-cp37-cp37m-linux_x86_64.whl --force-reinstall -``` - -测试FP32模型的速度 -``` -python ./image_classification/infer.py -### using tensorrt FP32 batch size: 1 time(ms): 0.6140608787536621 -``` - -测试FP16模型的速度 -``` -python ./image_classification/infer.py --use_fp16=True -### using tensorrt FP16 batch size: 1 time(ms): 0.5795984268188477 -``` - -测试INT8模型的速度 -``` -python ./image_classification/infer.py --model_dir=./MobileNetV1_quant/ --use_int8=True -### using tensorrt INT8 batch size: 1 time(ms): 0.5213963985443115 -``` - -**提示:** -- DataLoader传入的数据集是待压缩模型所用的数据集,DataLoader继承自`paddle.io.DataLoader`。可以直接使用模型套件中的DataLoader,或者根据[paddle.io.DataLoader](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/io/DataLoader_cn.html#dataloader)自定义所需要的DataLoader。 -- 自动化压缩Config中定义量化、蒸馏、剪枝等压缩算法会合并执行,压缩策略有:量化+蒸馏,剪枝+蒸馏等等。示例中选择的配置为离线量化超参搜索。 -- 如果要压缩的模型参数是存储在各自分离的文件中,需要先通过[convert.py](./convert.py) 脚本将其保存成一个单独的二进制文件。 - -## 应用示例 - -#### [图像分类](./image_classification) - -#### [目标检测](./detection) - -#### [语义分割](./semantic_segmentation) - -#### [NLP](./nlp) - -#### X2Paddle - -- [PyTorch YOLOv5](./pytorch_yolov5) -- [HuggingFace](./pytorch_huggingface) -- [TensorFlow MobileNet](./tensorflow_mobilenet) - -#### 即将发布 -- [ ] 更多自动化压缩应用示例 - -## 其他 - -- ACT可以自动处理常见的预测模型,如果有更特殊的改造需求,可以参考[ACT超参配置教程](./hyperparameter_tutorial.md)来进行单独配置压缩策略。 - -- 如果你发现任何关于ACT自动化压缩工具的问题或者是建议, 欢迎通过[GitHub Issues](https://github.com/PaddlePaddle/PaddleSlim/issues)给我们提issues。同时欢迎贡献更多优秀模型,共建开源生态。 +# 模型自动化压缩工具ACT(Auto Compression Toolkit) + +------------------------------------------------------------------------------------------ + +

+ + + + + + + + + +

+ +

+ 特性 | + Benchmark | + 安装 | + 快速开始 | + 进阶使用 | + 社区交流 +

+ +## **简介** + +PaddleSlim推出全新自动化压缩工具(Auto Compression Toolkit, ACT),旨在通过Source-Free的方式,自动对预测模型进行压缩,压缩后模型可直接部署应用。 + +## **News** 📢 + +* 🎉 2022.7.6 [**PaddleSlim v2.3.0**](https://github.com/PaddlePaddle/PaddleSlim/releases/tag/v2.3.0)全新发布!目前已经在图像分类、目标检测、图像分割、NLP等20多个模型验证正向效果。 +* 🔥 2022.7.14 晚 20:30,PaddleSlim自动压缩天使用户沟通会。与开发者共同探讨模型压缩痛点问题,欢迎大家扫码报名入群获取会议链接。 + +
+ +
+ +## **特性** + +- **🚀『解耦训练代码』** :开发者无需了解或修改模型源码,直接使用导出的预测模型进行压缩; +- **🎛️『全流程自动优化』** :开发者简单配置即可启动压缩,ACT工具会自动优化得到最好预测模型; +- **📦『支持丰富压缩算法』** :ACT中提供了量化训练、蒸馏、结构化剪枝、非结构化剪枝、多种离线量化方法及超参搜索等等,可任意搭配使用 + +### **ACT核心思想** + +相比于传统手工压缩,自动化压缩的“自动”主要体现在4个方面:解耦训练代码、离线量化超参搜索、算法 + +

+ +

+ +### **模型压缩效果示例** + +ACT相比传统的模型压缩方法, + +- 代码量减少 50% 以上 +- 压缩精度与手工压缩基本持平。在 PP-YOLOE 模型上,效果还优于手动压缩, +- 自动化压缩后的推理性能收益与手工压缩持平,相比压缩前,推理速度可以提升1.4~7.1倍。 + +

+ +

+ +### **模型压缩效果Benchmark** + + + + + +| 模型类型 | model name | 压缩前
精度(Top1 Acc %) | 压缩后
精度(Top1 Acc %) | 压缩前
推理时延(ms) | 压缩后
推理时延(ms) | 推理
加速比 | 芯片 | +| ------------------------------- | ---------------------------- | ---------------------- | ---------------------- | ---------------- | ---------------- | ---------- | ----------------- | +| [图像分类](./image_classification) | MobileNetV1 | 70.90 | 70.57 | 33.15 | 13.64 | **2.43** | SDM865(骁龙865) | +| [图像分类](./image_classification) | ShuffleNetV2_x1_0 | 68.65 | 68.32 | 10.43 | 5.51 | **1.89** | SDM865(骁龙865) | +| [图像分类](./image_classification) | SqueezeNet1_0_infer | 59.60 | 59.45 | 35.98 | 16.96 | **2.12** | SDM865(骁龙865) | +| [图像分类](./image_classification) | PPLCNetV2_base | 76.86 | 76.43 | 36.50 | 15.79 | **2.31** | SDM865(骁龙865) | +| [图像分类](./image_classification) | ResNet50_vd | 79.12 | 78.74 | 3.19 | 0.92 | **3.47** | NVIDIA Tesla T4 | +| [语义分割](./semantic_segmentation) | PPHGNet_tiny | 79.59 | 79.20 | 2.82 | 0.98 | **2.88** | NVIDIA Tesla T4 | +| [语义分割](./semantic_segmentation) | PP-HumanSeg-Lite | 92.87 | 92.35 | 56.36 | 37.71 | **1.49** | SDM710 | +| [语义分割](./semantic_segmentation) | PP-LiteSeg | 77.04 | 76.93 | 1.43 | 1.16 | **1.23** | NVIDIA Tesla T4 | +| [语义分割](./semantic_segmentation) | HRNet | 78.97 | 78.90 | 8.19 | 5.81 | **1.41** | NVIDIA Tesla T4 | +| [语义分割](./semantic_segmentation) | UNet | 65.00 | 64.93 | 15.29 | 10.23 | **1.49** | NVIDIA Tesla T4 | +| NLP | PP-MiniLM | 72.81 | 72.44 | 128.01 | 17.97 | **7.12** | NVIDIA Tesla T4 | +| NLP | ERNIE 3.0-Medium | 73.09 | 72.40 | 29.25(fp16) | 19.61 | **1.49** | NVIDIA Tesla T4 | +| [目标检测](./pytorch_yolov5) | YOLOv5s
(PyTorch) | 37.40 | 36.9 | 5.95 | 1.87 | **3.18** | NVIDIA Tesla T4 | +| [目标检测](./pytorch_yolov6) | YOLOv6s
(PyTorch) | 42.4 | 41.3 | 9.06 | 1.83 | **4.95** | NVIDIA Tesla T4 | +| [目标检测](./pytorch_yolov7) | YOLOv7
(PyTorch) | 51.1 | 50.8 | 26.84 | 4.55 | **5.89** | NVIDIA Tesla T4 | +| [目标检测](./detection) | PP-YOLOE-l | 50.9 | 50.6 | 11.2 | 6.7 | **1.67** | NVIDIA Tesla V100 | +| [图像分类](./image_classification) | MobileNetV1
(TensorFlow) | 71.0 | 70.22 | 30.45 | 15.86 | **1.92** | SDMM865(骁龙865) | + +- 备注:目标检测精度指标为mAP(0.5:0.95)精度测量结果。图像分割精度指标为IoU精度测量结果。 +- 更多飞桨模型应用示例及Benchmark可以参考:[图像分类](./image_classification),[目标检测](./detection),[语义分割](./semantic_segmentation),[自然语言处理](./nlp) +- 更多其它框架应用示例及Benchmark可以参考:[YOLOv5(PyTorch)](./pytorch_yolov5),[YOLOv6(PyTorch)](./pytorch_yolov6),[YOLOv7(PyTorch)](./pytorch_yolov7),[HuggingFace(PyTorch)](./pytorch_huggingface),[MobileNet(TensorFlow)](./tensorflow_mobilenet)。 + +## **环境准备** + +- 安装PaddlePaddle >= 2.3.1:(可以参考[飞桨官网安装文档](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装) + + ```shell + # CPU + pip install paddlepaddle --upgrade + # GPU + pip install paddlepaddle-gpu --upgrade + ``` + +- 安装PaddleSlim >=2.3.0: + + ```shell + pip install paddleslim + ``` + +## **快速开始** + +- **1. 准备模型及数据集** + +```shell +# 下载MobileNet预测模型 +wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/MobileNetV1_infer.tar +tar -xf MobileNetV1_infer.tar +# 下载ImageNet小型数据集 +wget https://sys-p0.bj.bcebos.com/slim_ci/ILSVRC2012_data_demo.tar.gz +tar -xf ILSVRC2012_data_demo.tar.gz +``` + +- **2.运行自动化压缩** + +```python +# 导入依赖包 +import paddle +from PIL import Image +from paddle.vision.datasets import DatasetFolder +from paddle.vision.transforms import transforms +from paddleslim.auto_compression import AutoCompression +paddle.enable_static() +# 定义DataSet +class ImageNetDataset(DatasetFolder): + def __init__(self, path, image_size=224): + super(ImageNetDataset, self).__init__(path) + normalize = transforms.Normalize( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.120, 57.375]) + self.transform = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(image_size), transforms.Transpose(), + normalize + ]) + + def __getitem__(self, idx): + img_path, _ = self.samples[idx] + return self.transform(Image.open(img_path).convert('RGB')) + + def __len__(self): + return len(self.samples) + +# 定义DataLoader +train_dataset = ImageNetDataset("./ILSVRC2012_data_demo/ILSVRC2012/train/") +image = paddle.static.data( + name='inputs', shape=[None] + [3, 224, 224], dtype='float32') +train_loader = paddle.io.DataLoader(train_dataset, feed_list=[image], batch_size=32, return_list=False) +# 开始自动压缩 +ac = AutoCompression( + model_dir="./MobileNetV1_infer", + model_filename="inference.pdmodel", + params_filename="inference.pdiparams", + save_dir="MobileNetV1_quant", + config={'Quantization': {}, "HyperParameterOptimization": {'ptq_algo': ['avg'], 'max_quant_count': 3}}, + train_dataloader=train_loader, + eval_dataloader=train_loader) +ac.compress() +``` + +- **3.精度测试** + + - 测试压缩前模型的精度: + + ```shell + CUDA_VISIBLE_DEVICES=0 python ./image_classification/eval.py + ### Eval Top1: 0.7171724759615384 + ``` + + - 测试量化模型的精度: + + ```shell + CUDA_VISIBLE_DEVICES=0 python ./image_classification/eval.py --model_dir='MobileNetV1_quant' + ### Eval Top1: 0.7166466346153846 + ``` + + - 量化后模型的精度相比量化前的模型几乎精度无损,由于是使用的超参搜索的方法来选择的量化参数,所以每次运行得到的量化模型精度会有些许波动。 + +- **4.推理速度测试** + + - 量化模型速度的测试依赖推理库的支持,所以确保安装的是带有TensorRT的PaddlePaddle。以下示例和展示的测试结果是基于Tesla V100、CUDA 10.2、python3.7得到的。 + + - 使用以下指令查看本地cuda版本,并且在[下载链接](https://paddleinference.paddlepaddle.org.cn/master/user_guides/download_lib.html#python)中下载对应cuda版本和对应python版本的paddlepaddle安装包。 + + ```shell + cat /usr/local/cuda/version.txt ### CUDA Version 10.2.89 + ### 10.2.89 为cuda版本号,可以根据这个版本号选择需要安装的带有TensorRT的PaddlePaddle安装包。 + ``` + + - 安装下载的whl包:(这里通过wget下载到的是python3.7、cuda10.2的PaddlePaddle安装包,若您的环境和示例环境不同,请依赖您自己机器的环境下载对应的安装包,否则运行示例代码会报错。) + + ``` + wget https://paddle-inference-lib.bj.bcebos.com/2.3.0/python/Linux/GPU/x86-64_gcc8.2_avx_mkl_cuda10.2_cudnn8.1.1_trt7.2.3.4/paddlepaddle_gpu-2.3.0-cp37-cp37m-linux_x86_64.whl + pip install paddlepaddle_gpu-2.3.0-cp37-cp37m-linux_x86_64.whl --force-reinstall + ``` + + - 测试FP32模型的速度 + + ``` + python ./image_classification/infer.py + ### using tensorrt FP32 batch size: 1 time(ms): 0.6140608787536621 + ``` + + - 测试FP16模型的速度 + + ``` + python ./image_classification/infer.py --use_fp16=True + ### using tensorrt FP16 batch size: 1 time(ms): 0.5795984268188477 + ``` + + - 测试INT8模型的速度 + + ``` + python ./image_classification/infer.py --model_dir=./MobileNetV1_quant/ --use_int8=True + ### using tensorrt INT8 batch size: 1 time(ms): 0.5213963985443115 + ``` + + - **提示:** + + - DataLoader传入的数据集是待压缩模型所用的数据集,DataLoader继承自`paddle.io.DataLoader`。可以直接使用模型套件中的DataLoader,或者根据[paddle.io.DataLoader](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/io/DataLoader_cn.html#dataloader)自定义所需要的DataLoader。 + - 自动化压缩Config中定义量化、蒸馏、剪枝等压缩算法会合并执行,压缩策略有:量化+蒸馏,剪枝+蒸馏等等。示例中选择的配置为离线量化超参搜索。 + - 如果要压缩的模型参数是存储在各自分离的文件中,需要先通过[convert.py](./convert.py) 脚本将其保存成一个单独的二进制文件。 + +## 进阶使用 + +- ACT可以自动处理常见的预测模型,如果有更特殊的改造需求,可以参考[ACT超参配置教程](./hyperparameter_tutorial.md)来进行单独配置压缩策略。 + +## 社区交流 + +- 微信扫描二维码并填写问卷之后,加入技术交流群 + +
+ +
+ +- 如果你发现任何关于ACT自动化压缩工具的问题或者是建议, 欢迎通过[GitHub Issues](https://github.com/PaddlePaddle/PaddleSlim/issues)给我们提issues。同时欢迎贡献更多优秀模型,共建开源生态。 + +## License + +本项目遵循[Apache-2.0开源协议](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/LICENSE) diff --git a/example/auto_compression/pytorch_yolov6/README.md b/example/auto_compression/pytorch_yolov6/README.md index 41a61b54..7cdb5464 100644 --- a/example/auto_compression/pytorch_yolov6/README.md +++ b/example/auto_compression/pytorch_yolov6/README.md @@ -22,7 +22,7 @@ | 模型 | 策略 | 输入尺寸 | mAPval
0.5:0.95 | 预测时延FP32
(ms) |预测时延FP16
(ms) | 预测时延INT8
(ms) | 配置文件 | Inference模型 | | :-------- |:-------- |:--------: | :---------------------: | :----------------: | :----------------: | :---------------: | :-----------------------------: | :-----------------------------: | -| YOLOv6s | Base模型 | 640*640 | 42.4 | 9.06ms | 2.90ms | - | - | [Model](https://bj.bcebos.com/v1/paddle-slim-models/detection/yolov6s_infer.tar) | +| YOLOv6s | Base模型 | 640*640 | 42.4 | 9.06ms | 2.90ms | - | - | [Model](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov6s_infer.tar) | | YOLOv6s | KL离线量化 | 640*640 | 30.3 | - | - | 1.83ms | - | - | | YOLOv6s | 量化蒸馏训练 | 640*640 | **41.3** | - | - | **1.83ms** | [config](./configs/yolov6s_qat_dis.yaml) | [Model](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov6s_quant.tar) | @@ -83,7 +83,7 @@ pip install x2paddle sympy onnx x2paddle --framework=onnx --model=yolov6s.onnx --save_dir=pd_model cp -r pd_model/inference_model/ yolov6s_infer ``` -即可得到YOLOv6s模型的预测模型(`model.pdmodel` 和 `model.pdiparams`)。如想快速体验,可直接下载上方表格中YOLOv6s的[Paddle预测模型](https://bj.bcebos.com/v1/paddle-slim-models/detection/yolov6s_infer.tar)。 +即可得到YOLOv6s模型的预测模型(`model.pdmodel` 和 `model.pdiparams`)。如想快速体验,可直接下载上方表格中YOLOv6s的[Paddle预测模型](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov6s_infer.tar)。 预测模型的格式为:`model.pdmodel` 和 `model.pdiparams`两个,带`pdmodel`的是模型文件,带`pdiparams`后缀的是权重文件。 diff --git a/example/auto_compression/pytorch_yolov7/README.md b/example/auto_compression/pytorch_yolov7/README.md new file mode 100644 index 00000000..7dddf99b --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/README.md @@ -0,0 +1,152 @@ +# YOLOv7自动压缩示例 + +目录: +- [1.简介](#1简介) +- [2.Benchmark](#2Benchmark) +- [3.开始自动压缩](#自动压缩流程) + - [3.1 环境准备](#31-准备环境) + - [3.2 准备数据集](#32-准备数据集) + - [3.3 准备预测模型](#33-准备预测模型) + - [3.4 测试模型精度](#34-测试模型精度) + - [3.5 自动压缩并产出模型](#35-自动压缩并产出模型) +- [4.预测部署](#4预测部署) +- [5.FAQ](5FAQ) + +## 1. 简介 + +飞桨模型转换工具[X2Paddle](https://github.com/PaddlePaddle/X2Paddle)支持将```Caffe/TensorFlow/ONNX/PyTorch```的模型一键转为飞桨(PaddlePaddle)的预测模型。借助X2Paddle的能力,各种框架的推理模型可以很方便的使用PaddleSlim的自动化压缩功能。 + +本示例将以[WongKinYiu/yolov7](https://github.com/WongKinYiu/yolov7)目标检测模型为例,将PyTorch框架模型转换为Paddle框架模型,再使用ACT自动压缩功能进行自动压缩。本示例使用的自动压缩策略为量化训练。 + +## 2.Benchmark + +| 模型 | 策略 | 输入尺寸 | mAPval
0.5:0.95 | 预测时延FP32
(ms) |预测时延FP16
(ms) | 预测时延INT8
(ms) | 配置文件 | Inference模型 | +| :-------- |:-------- |:--------: | :---------------------: | :----------------: | :----------------: | :---------------: | :-----------------------------: | :-----------------------------: | +| YOLOv7 | Base模型 | 640*640 | 51.1 | 26.84ms | 7.44ms | - | - | [Model](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov7_infer.tar) | +| YOLOv7 | KL离线量化 | 640*640 | 50.2 | - | - | 4.55ms | - | - | +| YOLOv7 | 量化蒸馏训练 | 640*640 | **50.8** | - | - | **4.55ms** | [config](./configs/yolov7_qat_dis.yaml) | [Model](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov7_quant.tar) | + +说明: +- mAP的指标均在COCO val2017数据集中评测得到。 +- YOLOv7模型在Tesla T4的GPU环境下开启TensorRT 8.4.1,batch_size=1, 测试脚本是[cpp_infer](./cpp_infer)。 + +## 3. 自动压缩流程 + +#### 3.1 准备环境 +- PaddlePaddle >= 2.3 (可从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装) +- PaddleSlim > 2.3版本 +- PaddleDet >= 2.4 +- [X2Paddle](https://github.com/PaddlePaddle/X2Paddle) >= 1.3.6 +- opencv-python + +(1)安装paddlepaddle: +```shell +# CPU +pip install paddlepaddle +# GPU +pip install paddlepaddle-gpu +``` + +(2)安装paddleslim: +```shell +pip install paddleslim +``` + +(3)安装paddledet: +```shell +pip install paddledet +``` + +注:安装PaddleDet的目的只是为了直接使用PaddleDetection中的Dataloader组件。 + +(4)安装X2Paddle的1.3.6以上版本: +```shell +pip install x2paddle sympy onnx +``` + +#### 3.2 准备数据集 + +本案例默认以COCO数据进行自动压缩实验,并且依赖PaddleDetection中数据读取模块,如果自定义COCO数据,或者其他格式数据,请参考[PaddleDetection数据准备文档](https://github.com/PaddlePaddle/PaddleDetection/blob/release/2.4/docs/tutorials/PrepareDataSet.md) 来准备数据。 + +如果已经准备好数据集,请直接修改[./configs/yolov7_reader.yml]中`EvalDataset`的`dataset_dir`字段为自己数据集路径即可。 + + +#### 3.3 准备预测模型 + +(1)准备ONNX模型: + +可通过[WongKinYiu/yolov7](https://github.com/WongKinYiu/yolov7)的导出脚本来准备ONNX模型,具体步骤如下: +```shell +git clone https://github.com/WongKinYiu/yolov7.git +# 切换分支到u5分支,保持导出的ONNX模型后处理和YOLOv5一致 +git checkout u5 +# 下载好yolov7.pt权重后执行: +python export.py --weights yolov7.pt --include onnx +``` + +也可以直接下载我们已经准备好的[yolov7.onnx](https://paddle-slim-models.bj.bcebos.com/act/yolov7.onnx)。 + + +(2) 转换模型: +``` +x2paddle --framework=onnx --model=yolov7.onnx --save_dir=pd_model +cp -r pd_model/inference_model/ yolov7_infer +``` +即可得到YOLOv7模型的预测模型(`model.pdmodel` 和 `model.pdiparams`)。如想快速体验,可直接下载上方表格中YOLOv7的[Paddle预测模型](https://bj.bcebos.com/v1/paddle-slim-models/act/yolov7_infer.tar)。 + + +预测模型的格式为:`model.pdmodel` 和 `model.pdiparams`两个,带`pdmodel`的是模型文件,带`pdiparams`后缀的是权重文件。 + + +#### 3.4 自动压缩并产出模型 + +蒸馏量化自动压缩示例通过run.py脚本启动,会使用接口```paddleslim.auto_compression.AutoCompression```对模型进行自动压缩。配置config文件中模型路径、蒸馏、量化、和训练等部分的参数,配置完成后便可对模型进行量化和蒸馏。具体运行命令为: + +- 单卡训练: +``` +export CUDA_VISIBLE_DEVICES=0 +python run.py --config_path=./configs/yolov7_qat_dis.yaml --save_dir='./output/' +``` + +- 多卡训练: +``` +CUDA_VISIBLE_DEVICES=0,1,2,3 python -m paddle.distributed.launch --log_dir=log --gpus 0,1,2,3 run.py \ + --config_path=./configs/yolov7_qat_dis.yaml --save_dir='./output/' +``` + +#### 3.5 测试模型精度 + +修改[yolov7_qat_dis.yaml](./configs/yolov7_qat_dis.yaml)中`model_dir`字段为模型存储路径,然后使用eval.py脚本得到模型的mAP: +``` +export CUDA_VISIBLE_DEVICES=0 +python eval.py --config_path=./configs/yolov7_qat_dis.yaml +``` + + +## 4.预测部署 + +#### Paddle-TensorRT C++部署 + +进入[cpp_infer](./cpp_infer)文件夹内,请按照[C++ TensorRT Benchmark测试教程](./cpp_infer/README.md)进行准备环境及编译,然后开始测试: +```shell +# 编译 +bash complie.sh +# 执行 +./build/trt_run --model_file yolov7_quant/model.pdmodel --params_file yolov7_quant/model.pdiparams --run_mode=trt_int8 +``` + +#### Paddle-TensorRT Python部署: + +首先安装带有TensorRT的[Paddle安装包](https://www.paddlepaddle.org.cn/inference/v2.3/user_guides/download_lib.html#python)。 + +然后使用[paddle_trt_infer.py](./paddle_trt_infer.py)进行部署: +```shell +python paddle_trt_infer.py --model_path=output --image_file=images/000000570688.jpg --benchmark=True --run_mode=trt_int8 +``` + +## 5.FAQ + +- 如果想测试离线量化模型精度,可执行: +```shell +python post_quant.py --config_path=./configs/yolov7_qat_dis.yaml +``` diff --git a/example/auto_compression/pytorch_yolov7/configs/yolov7_qat_dis.yaml b/example/auto_compression/pytorch_yolov7/configs/yolov7_qat_dis.yaml new file mode 100644 index 00000000..6607e361 --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/configs/yolov7_qat_dis.yaml @@ -0,0 +1,30 @@ + +Global: + reader_config: configs/yolov7_reader.yaml + input_list: {'image': 'x2paddle_images'} + Evaluation: True + model_dir: ./yolov7_infer + model_filename: model.pdmodel + params_filename: model.pdiparams + +Distillation: + alpha: 1.0 + loss: soft_label + +Quantization: + activation_quantize_type: 'moving_average_abs_max' + quantize_op_types: + - conv2d + - depthwise_conv2d + +TrainConfig: + train_iter: 8000 + eval_iter: 1000 + learning_rate: + type: CosineAnnealingDecay + learning_rate: 0.00003 + T_max: 8000 + optimizer_builder: + optimizer: + type: SGD + weight_decay: 0.00004 diff --git a/example/auto_compression/pytorch_yolov7/configs/yolov7_reader.yaml b/example/auto_compression/pytorch_yolov7/configs/yolov7_reader.yaml new file mode 100644 index 00000000..cb87c3f8 --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/configs/yolov7_reader.yaml @@ -0,0 +1,27 @@ +metric: COCO +num_classes: 80 + +# Datset configuration +TrainDataset: + !COCODataSet + image_dir: train2017 + anno_path: annotations/instances_train2017.json + dataset_dir: dataset/coco/ + +EvalDataset: + !COCODataSet + image_dir: val2017 + anno_path: annotations/instances_val2017.json + dataset_dir: dataset/coco/ + +worker_num: 0 + +# preprocess reader in test +EvalReader: + sample_transforms: + - Decode: {} + - Resize: {target_size: [640, 640], keep_ratio: True} + - Pad: {size: [640, 640], fill_value: [114., 114., 114.]} + - NormalizeImage: {mean: [0, 0, 0], std: [1, 1, 1], is_scale: True} + - Permute: {} + batch_size: 1 diff --git a/example/auto_compression/pytorch_yolov7/cpp_infer/CMakeLists.txt b/example/auto_compression/pytorch_yolov7/cpp_infer/CMakeLists.txt new file mode 100644 index 00000000..d5307c65 --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/cpp_infer/CMakeLists.txt @@ -0,0 +1,263 @@ +cmake_minimum_required(VERSION 3.0) +project(cpp_inference_demo CXX C) +option(WITH_MKL "Compile demo with MKL/OpenBlas support, default use MKL." ON) +option(WITH_GPU "Compile demo with GPU/CPU, default use CPU." OFF) +option(WITH_STATIC_LIB "Compile demo with static/shared library, default use static." ON) +option(USE_TENSORRT "Compile demo with TensorRT." OFF) +option(WITH_ROCM "Compile demo with rocm." OFF) +option(WITH_ONNXRUNTIME "Compile demo with ONNXRuntime" OFF) +option(WITH_ARM "Compile demo with ARM" OFF) +option(WITH_MIPS "Compile demo with MIPS" OFF) +option(WITH_SW "Compile demo with SW" OFF) +option(WITH_XPU "Compile demow ith xpu" OFF) +option(WITH_NPU "Compile demow ith npu" OFF) + +if(NOT WITH_STATIC_LIB) + add_definitions("-DPADDLE_WITH_SHARED_LIB") +else() + # PD_INFER_DECL is mainly used to set the dllimport/dllexport attribute in dynamic library mode. + # Set it to empty in static library mode to avoid compilation issues. + add_definitions("/DPD_INFER_DECL=") +endif() + +macro(safe_set_static_flag) + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + endforeach(flag_var) +endmacro() + +if(NOT DEFINED PADDLE_LIB) + message(FATAL_ERROR "please set PADDLE_LIB with -DPADDLE_LIB=/path/paddle/lib") +endif() +if(NOT DEFINED DEMO_NAME) + message(FATAL_ERROR "please set DEMO_NAME with -DDEMO_NAME=demo_name") +endif() + +include_directories("${PADDLE_LIB}/") +set(PADDLE_LIB_THIRD_PARTY_PATH "${PADDLE_LIB}/third_party/install/") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}protobuf/include") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}glog/include") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}gflags/include") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}xxhash/include") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}cryptopp/include") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}onnxruntime/include") +include_directories("${PADDLE_LIB_THIRD_PARTY_PATH}paddle2onnx/include") + +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}protobuf/lib") +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}glog/lib") +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}gflags/lib") +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}xxhash/lib") +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}cryptopp/lib") +link_directories("${PADDLE_LIB}/paddle/lib") +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}onnxruntime/lib") +link_directories("${PADDLE_LIB_THIRD_PARTY_PATH}paddle2onnx/lib") + +if (WIN32) + add_definitions("/DGOOGLE_GLOG_DLL_DECL=") + option(MSVC_STATIC_CRT "use static C Runtime library by default" ON) + if (MSVC_STATIC_CRT) + if (WITH_MKL) + set(FLAG_OPENMP "/openmp") + endif() + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /bigobj /MTd ${FLAG_OPENMP}") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /bigobj /MT ${FLAG_OPENMP}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj /MTd ${FLAG_OPENMP}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /bigobj /MT ${FLAG_OPENMP}") + safe_set_static_flag() + if (WITH_STATIC_LIB) + add_definitions(-DSTATIC_LIB) + endif() + endif() +else() + if(WITH_MKL) + set(FLAG_OPENMP "-fopenmp") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${FLAG_OPENMP}") +endif() + +if(WITH_GPU) + if(NOT WIN32) + include_directories("/usr/local/cuda/include") + if(CUDA_LIB STREQUAL "") + set(CUDA_LIB "/usr/local/cuda/lib64/" CACHE STRING "CUDA Library") + endif() + else() + include_directories("C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\include") + if(CUDA_LIB STREQUAL "") + set(CUDA_LIB "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0\\lib\\x64") + endif() + endif(NOT WIN32) +endif() + +if (USE_TENSORRT AND WITH_GPU) + set(TENSORRT_ROOT "" CACHE STRING "The root directory of TensorRT library") + if("${TENSORRT_ROOT}" STREQUAL "") + message(FATAL_ERROR "The TENSORRT_ROOT is empty, you must assign it a value with CMake command. Such as: -DTENSORRT_ROOT=TENSORRT_ROOT_PATH ") + endif() + set(TENSORRT_INCLUDE_DIR ${TENSORRT_ROOT}/include) + set(TENSORRT_LIB_DIR ${TENSORRT_ROOT}/lib) + file(READ ${TENSORRT_INCLUDE_DIR}/NvInfer.h TENSORRT_VERSION_FILE_CONTENTS) + string(REGEX MATCH "define NV_TENSORRT_MAJOR +([0-9]+)" TENSORRT_MAJOR_VERSION + "${TENSORRT_VERSION_FILE_CONTENTS}") + if("${TENSORRT_MAJOR_VERSION}" STREQUAL "") + file(READ ${TENSORRT_INCLUDE_DIR}/NvInferVersion.h TENSORRT_VERSION_FILE_CONTENTS) + string(REGEX MATCH "define NV_TENSORRT_MAJOR +([0-9]+)" TENSORRT_MAJOR_VERSION + "${TENSORRT_VERSION_FILE_CONTENTS}") + endif() + if("${TENSORRT_MAJOR_VERSION}" STREQUAL "") + message(SEND_ERROR "Failed to detect TensorRT version.") + endif() + string(REGEX REPLACE "define NV_TENSORRT_MAJOR +([0-9]+)" "\\1" + TENSORRT_MAJOR_VERSION "${TENSORRT_MAJOR_VERSION}") + message(STATUS "Current TensorRT header is ${TENSORRT_INCLUDE_DIR}/NvInfer.h. " + "Current TensorRT version is v${TENSORRT_MAJOR_VERSION}. ") + include_directories("${TENSORRT_INCLUDE_DIR}") + link_directories("${TENSORRT_LIB_DIR}") +endif() + +if(WITH_MKL) + set(MATH_LIB_PATH "${PADDLE_LIB_THIRD_PARTY_PATH}mklml") + include_directories("${MATH_LIB_PATH}/include") + if(WIN32) + set(MATH_LIB ${MATH_LIB_PATH}/lib/mklml${CMAKE_STATIC_LIBRARY_SUFFIX} + ${MATH_LIB_PATH}/lib/libiomp5md${CMAKE_STATIC_LIBRARY_SUFFIX}) + else() + set(MATH_LIB ${MATH_LIB_PATH}/lib/libmklml_intel${CMAKE_SHARED_LIBRARY_SUFFIX} + ${MATH_LIB_PATH}/lib/libiomp5${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + set(MKLDNN_PATH "${PADDLE_LIB_THIRD_PARTY_PATH}mkldnn") + if(EXISTS ${MKLDNN_PATH}) + include_directories("${MKLDNN_PATH}/include") + if(WIN32) + set(MKLDNN_LIB ${MKLDNN_PATH}/lib/mkldnn.lib) + else(WIN32) + set(MKLDNN_LIB ${MKLDNN_PATH}/lib/libmkldnn.so.0) + endif(WIN32) + endif() +elseif((NOT WITH_MIPS) AND (NOT WITH_SW)) + set(OPENBLAS_LIB_PATH "${PADDLE_LIB_THIRD_PARTY_PATH}openblas") + include_directories("${OPENBLAS_LIB_PATH}/include/openblas") + if(WIN32) + set(MATH_LIB ${OPENBLAS_LIB_PATH}/lib/openblas${CMAKE_STATIC_LIBRARY_SUFFIX}) + else() + set(MATH_LIB ${OPENBLAS_LIB_PATH}/lib/libopenblas${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() +endif() + +if(WITH_STATIC_LIB) + set(DEPS ${PADDLE_LIB}/paddle/lib/libpaddle_inference${CMAKE_STATIC_LIBRARY_SUFFIX}) +else() + if(WIN32) + set(DEPS ${PADDLE_LIB}/paddle/lib/paddle_inference${CMAKE_STATIC_LIBRARY_SUFFIX}) + else() + set(DEPS ${PADDLE_LIB}/paddle/lib/libpaddle_inference${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() +endif() + +if (WITH_ONNXRUNTIME) + if(WIN32) + set(DEPS ${DEPS} ${PADDLE_LIB_THIRD_PARTY_PATH}onnxruntime/lib/onnxruntime.lib paddle2onnx) + elseif(APPLE) + set(DEPS ${DEPS} ${PADDLE_LIB_THIRD_PARTY_PATH}onnxruntime/lib/libonnxruntime.1.10.0.dylib paddle2onnx) + else() + set(DEPS ${DEPS} ${PADDLE_LIB_THIRD_PARTY_PATH}onnxruntime/lib/libonnxruntime.so.1.10.0 paddle2onnx) + endif() +endif() + +if (NOT WIN32) + set(EXTERNAL_LIB "-lrt -ldl -lpthread") + set(DEPS ${DEPS} + ${MATH_LIB} ${MKLDNN_LIB} + glog gflags protobuf xxhash cryptopp + ${EXTERNAL_LIB}) +else() + set(DEPS ${DEPS} + ${MATH_LIB} ${MKLDNN_LIB} + glog gflags_static libprotobuf xxhash cryptopp-static ${EXTERNAL_LIB}) + set(DEPS ${DEPS} shlwapi.lib) +endif(NOT WIN32) + +if(WITH_GPU) + if(NOT WIN32) + if (USE_TENSORRT) + set(DEPS ${DEPS} ${TENSORRT_LIB_DIR}/libnvinfer${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${TENSORRT_LIB_DIR}/libnvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + if(USE_TENSORRT) + set(DEPS ${DEPS} ${TENSORRT_LIB_DIR}/nvinfer${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${TENSORRT_LIB_DIR}/nvinfer_plugin${CMAKE_STATIC_LIBRARY_SUFFIX}) + if(${TENSORRT_MAJOR_VERSION} GREATER_EQUAL 7) + set(DEPS ${DEPS} ${TENSORRT_LIB_DIR}/myelin64_1${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + endif() + set(DEPS ${DEPS} ${CUDA_LIB}/cudart${CMAKE_STATIC_LIBRARY_SUFFIX} ) + set(DEPS ${DEPS} ${CUDA_LIB}/cublas${CMAKE_STATIC_LIBRARY_SUFFIX} ) + set(DEPS ${DEPS} ${CUDA_LIB}/cudnn${CMAKE_STATIC_LIBRARY_SUFFIX} ) + endif() +endif() + +if(WITH_ROCM AND NOT WIN32) + set(DEPS ${DEPS} ${ROCM_LIB}/libamdhip64${CMAKE_SHARED_LIBRARY_SUFFIX}) +endif() + +if(WITH_XPU AND NOT WIN32) + set(XPU_INSTALL_PATH "${PADDLE_LIB_THIRD_PARTY_PATH}xpu") + set(DEPS ${DEPS} ${XPU_INSTALL_PATH}/lib/libxpuapi${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${XPU_INSTALL_PATH}/lib/libxpurt${CMAKE_SHARED_LIBRARY_SUFFIX}) +endif() + +if(WITH_NPU AND NOT WIN32) + set(DEPS ${DEPS} ${ASCEND_DIR}/ascend-toolkit/latest/fwkacllib/lib64/libgraph${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${ASCEND_DIR}/ascend-toolkit/latest/fwkacllib/lib64/libge_runner${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${ASCEND_DIR}/ascend-toolkit/latest/fwkacllib/lib64/libascendcl${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${ASCEND_DIR}/ascend-toolkit/latest/fwkacllib/lib64/libascendcl${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${ASCEND_DIR}/ascend-toolkit/latest/fwkacllib/lib64/libacl_op_compiler${CMAKE_SHARED_LIBRARY_SUFFIX}) +endif() + +add_executable(${DEMO_NAME} ${DEMO_NAME}.cc) +target_link_libraries(${DEMO_NAME} ${DEPS}) +if(WIN32) + if(USE_TENSORRT) + add_custom_command(TARGET ${DEMO_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${TENSORRT_LIB_DIR}/nvinfer${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE} + COMMAND ${CMAKE_COMMAND} -E copy ${TENSORRT_LIB_DIR}/nvinfer_plugin${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE} + ) + if(${TENSORRT_MAJOR_VERSION} GREATER_EQUAL 7) + add_custom_command(TARGET ${DEMO_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${TENSORRT_LIB_DIR}/myelin64_1${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}) + endif() + endif() + if(WITH_MKL) + add_custom_command(TARGET ${DEMO_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${MATH_LIB_PATH}/lib/mklml.dll ${CMAKE_BINARY_DIR}/Release + COMMAND ${CMAKE_COMMAND} -E copy ${MATH_LIB_PATH}/lib/libiomp5md.dll ${CMAKE_BINARY_DIR}/Release + COMMAND ${CMAKE_COMMAND} -E copy ${MKLDNN_PATH}/lib/mkldnn.dll ${CMAKE_BINARY_DIR}/Release + ) + else() + add_custom_command(TARGET ${DEMO_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${OPENBLAS_LIB_PATH}/lib/openblas.dll ${CMAKE_BINARY_DIR}/Release + ) + endif() + if(WITH_ONNXRUNTIME) + add_custom_command(TARGET ${DEMO_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${PADDLE_LIB_THIRD_PARTY_PATH}onnxruntime/lib/onnxruntime.dll + ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE} + COMMAND ${CMAKE_COMMAND} -E copy ${PADDLE_LIB_THIRD_PARTY_PATH}paddle2onnx/lib/paddle2onnx.dll + ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE} + ) + endif() + if(NOT WITH_STATIC_LIB) + add_custom_command(TARGET ${DEMO_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${PADDLE_LIB}/paddle/lib/paddle_inference.dll" ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE} + ) + endif() +endif() diff --git a/example/auto_compression/pytorch_yolov7/cpp_infer/README.md b/example/auto_compression/pytorch_yolov7/cpp_infer/README.md new file mode 100644 index 00000000..04c1c23b --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/cpp_infer/README.md @@ -0,0 +1,51 @@ +# YOLOv7 TensorRT Benchmark测试(Linux) + +## 环境准备 + +- CUDA、CUDNN:确认环境中已经安装CUDA和CUDNN,并且提前获取其安装路径。 + +- TensorRT:可通过NVIDIA官网下载[TensorRT 8.4.1.5](https://developer.nvidia.com/compute/machine-learning/tensorrt/secure/8.4.1/tars/tensorrt-8.4.1.5.linux.x86_64-gnu.cuda-11.6.cudnn8.4.tar.gz)或其他版本安装包。 + +- Paddle Inference C++预测库:编译develop版本请参考[编译文档](https://www.paddlepaddle.org.cn/inference/user_guides/source_compile.html)。编译完成后,会在build目录下生成`paddle_inference_install_dir`文件夹,这个就是我们需要的C++预测库文件。 + +## 编译可执行程序 + +- (1)修改`compile.sh`中依赖库路径,主要是以下内容: +```shell +# Paddle Inference预测库路径 +LIB_DIR=/root/auto_compress/Paddle/build/paddle_inference_install_dir/ +# CUDNN路径 +CUDNN_LIB=/usr/lib/x86_64-linux-gnu/ +# CUDA路径 +CUDA_LIB=/usr/local/cuda/lib64 +# TensorRT安装包路径,为TRT资源包解压完成后的绝对路径,其中包含`lib`和`include`文件夹 +TENSORRT_ROOT=/root/auto_compress/trt/trt8.4/ +``` + +## 测试 + +- FP32 +``` +./build/trt_run --model_file yolov7_infer/model.pdmodel --params_file yolov7_infer/model.pdiparams --run_mode=trt_fp32 +``` + +- FP16 +``` +./build/trt_run --model_file yolov7_infer/model.pdmodel --params_file yolov7_infer/model.pdiparams --run_mode=trt_fp16 +``` + +- INT8 +``` +./build/trt_run --model_file yolov7_quant/model.pdmodel --params_file yolov7_quant/model.pdiparams --run_mode=trt_int8 +``` + +## 性能对比 + +| 预测库 | 模型 | 预测时延FP32
(ms) |预测时延FP16
(ms) | 预测时延INT8
(ms) | +| :--------: | :--------: |:-------- |:--------: | :---------------------: | +| Paddle TensorRT | YOLOv7 | 26.84ms | 7.44ms | 4.55ms | +| TensorRT | YOLOv7 | 28.25ms | 7.23ms | 4.67ms | + +环境: +- Tesla T4,TensorRT 8.4.1,CUDA 11.2 +- batch_size=1 diff --git a/example/auto_compression/pytorch_yolov7/cpp_infer/compile.sh b/example/auto_compression/pytorch_yolov7/cpp_infer/compile.sh new file mode 100644 index 00000000..afff924b --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/cpp_infer/compile.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set +x +set -e + +work_path=$(dirname $(readlink -f $0)) + +mkdir -p build +cd build +rm -rf * + +DEMO_NAME=trt_run + +WITH_MKL=ON +WITH_GPU=ON +USE_TENSORRT=ON + +LIB_DIR=/root/auto_compress/Paddle/build/paddle_inference_install_dir/ +CUDNN_LIB=/usr/lib/x86_64-linux-gnu/ +CUDA_LIB=/usr/local/cuda/lib64 +TENSORRT_ROOT=/root/auto_compress/trt/trt8.4/ + +WITH_ROCM=OFF +ROCM_LIB=/opt/rocm/lib + +cmake .. -DPADDLE_LIB=${LIB_DIR} \ + -DWITH_MKL=${WITH_MKL} \ + -DDEMO_NAME=${DEMO_NAME} \ + -DWITH_GPU=${WITH_GPU} \ + -DWITH_STATIC_LIB=OFF \ + -DUSE_TENSORRT=${USE_TENSORRT} \ + -DWITH_ROCM=${WITH_ROCM} \ + -DROCM_LIB=${ROCM_LIB} \ + -DCUDNN_LIB=${CUDNN_LIB} \ + -DCUDA_LIB=${CUDA_LIB} \ + -DTENSORRT_ROOT=${TENSORRT_ROOT} + +make -j diff --git a/example/auto_compression/pytorch_yolov7/cpp_infer/trt_run.cc b/example/auto_compression/pytorch_yolov7/cpp_infer/trt_run.cc new file mode 100644 index 00000000..0ae055ac --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/cpp_infer/trt_run.cc @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "paddle/include/paddle_inference_api.h" +#include "paddle/include/experimental/phi/common/float16.h" + +using paddle_infer::Config; +using paddle_infer::Predictor; +using paddle_infer::CreatePredictor; +using paddle_infer::PrecisionType; +using phi::dtype::float16; + +DEFINE_string(model_dir, "", "Directory of the inference model."); +DEFINE_string(model_file, "", "Path of the inference model file."); +DEFINE_string(params_file, "", "Path of the inference params file."); +DEFINE_string(run_mode, "trt_fp32", "run_mode which can be: trt_fp32, trt_fp16 and trt_int8"); +DEFINE_int32(batch_size, 1, "Batch size."); +DEFINE_int32(gpu_id, 0, "GPU card ID num."); +DEFINE_int32(trt_min_subgraph_size, 3, "tensorrt min_subgraph_size"); +DEFINE_int32(warmup, 50, "warmup"); +DEFINE_int32(repeats, 1000, "repeats"); + +using Time = decltype(std::chrono::high_resolution_clock::now()); +Time time() { return std::chrono::high_resolution_clock::now(); }; +double time_diff(Time t1, Time t2) { + typedef std::chrono::microseconds ms; + auto diff = t2 - t1; + ms counter = std::chrono::duration_cast(diff); + return counter.count() / 1000.0; +} + +std::shared_ptr InitPredictor() { + Config config; + std::string model_path; + if (FLAGS_model_dir != "") { + config.SetModel(FLAGS_model_dir); + model_path = FLAGS_model_dir.substr(0, FLAGS_model_dir.find_last_of("/")); + } else { + config.SetModel(FLAGS_model_file, FLAGS_params_file); + model_path = FLAGS_model_file.substr(0, FLAGS_model_file.find_last_of("/")); + } + // enable tune + std::cout << "model_path: " << model_path << std::endl; + config.EnableUseGpu(256, FLAGS_gpu_id); + if (FLAGS_run_mode == "trt_fp32") { + config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, FLAGS_trt_min_subgraph_size, + PrecisionType::kFloat32, false, false); + } else if (FLAGS_run_mode == "trt_fp16") { + config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, FLAGS_trt_min_subgraph_size, + PrecisionType::kHalf, false, false); + } else if (FLAGS_run_mode == "trt_int8") { + config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, FLAGS_trt_min_subgraph_size, + PrecisionType::kInt8, false, false); + } + config.EnableMemoryOptim(); + config.SwitchIrOptim(true); + return CreatePredictor(config); +} + +template +void run(Predictor *predictor, const std::vector &input, + const std::vector &input_shape, type* out_data, std::vector out_shape) { + + // prepare input + int input_num = std::accumulate(input_shape.begin(), input_shape.end(), 1, + std::multiplies()); + + auto input_names = predictor->GetInputNames(); + auto input_t = predictor->GetInputHandle(input_names[0]); + input_t->Reshape(input_shape); + input_t->CopyFromCpu(input.data()); + + for (int i = 0; i < FLAGS_warmup; ++i) + CHECK(predictor->Run()); + + auto st = time(); + for (int i = 0; i < FLAGS_repeats; ++i) { + auto input_names = predictor->GetInputNames(); + auto input_t = predictor->GetInputHandle(input_names[0]); + input_t->Reshape(input_shape); + input_t->CopyFromCpu(input.data()); + + CHECK(predictor->Run()); + + auto output_names = predictor->GetOutputNames(); + auto output_t = predictor->GetOutputHandle(output_names[0]); + std::vector output_shape = output_t->shape(); + output_t -> ShareExternalData(out_data, out_shape, paddle_infer::PlaceType::kGPU); + } + + LOG(INFO) << "[" << FLAGS_run_mode << " bs-" << FLAGS_batch_size << " ] run avg time is " << time_diff(st, time()) / FLAGS_repeats + << " ms"; +} + +int main(int argc, char *argv[]) { + google::ParseCommandLineFlags(&argc, &argv, true); + auto predictor = InitPredictor(); + std::vector input_shape = {FLAGS_batch_size, 3, 640, 640}; + // float16 + using dtype = float16; + std::vector input_data(FLAGS_batch_size * 3 * 640 * 640, dtype(1.0)); + + dtype *out_data; + int out_data_size = FLAGS_batch_size * 25200 * 85; + cudaHostAlloc((void**)&out_data, sizeof(float) * out_data_size, cudaHostAllocMapped); + + std::vector out_shape{ FLAGS_batch_size, 1, 25200, 85}; + run(predictor.get(), input_data, input_shape, out_data, out_shape); + return 0; +} diff --git a/example/auto_compression/pytorch_yolov7/eval.py b/example/auto_compression/pytorch_yolov7/eval.py new file mode 100644 index 00000000..478c4e1a --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/eval.py @@ -0,0 +1,151 @@ +# Copyright (c) 2022 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 sys +import numpy as np +import argparse +import paddle +from ppdet.core.workspace import load_config, merge_config +from ppdet.core.workspace import create +from ppdet.metrics import COCOMetric, VOCMetric +from paddleslim.auto_compression.config_helpers import load_config as load_slim_config + +from post_process import YOLOv7PostProcess + + +def argsparser(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--config_path', + type=str, + default=None, + help="path of compression strategy config.", + required=True) + parser.add_argument( + '--devices', + type=str, + default='gpu', + help="which device used to compress.") + + return parser + + +def reader_wrapper(reader, input_list): + def gen(): + for data in reader: + in_dict = {} + if isinstance(input_list, list): + for input_name in input_list: + in_dict[input_name] = data[input_name] + elif isinstance(input_list, dict): + for input_name in input_list.keys(): + in_dict[input_list[input_name]] = data[input_name] + yield in_dict + + return gen + + +def convert_numpy_data(data, metric): + data_all = {} + data_all = {k: np.array(v) for k, v in data.items()} + if isinstance(metric, VOCMetric): + for k, v in data_all.items(): + if not isinstance(v[0], np.ndarray): + tmp_list = [] + for t in v: + tmp_list.append(np.array(t)) + data_all[k] = np.array(tmp_list) + else: + data_all = {k: np.array(v) for k, v in data.items()} + return data_all + + +def eval(): + + place = paddle.CUDAPlace(0) if FLAGS.devices == 'gpu' else paddle.CPUPlace() + exe = paddle.static.Executor(place) + + val_program, feed_target_names, fetch_targets = paddle.static.load_inference_model( + global_config["model_dir"], + exe, + model_filename=global_config["model_filename"], + params_filename=global_config["params_filename"]) + print('Loaded model from: {}'.format(global_config["model_dir"])) + + metric = global_config['metric'] + for batch_id, data in enumerate(val_loader): + data_all = convert_numpy_data(data, metric) + data_input = {} + for k, v in data.items(): + if isinstance(global_config['input_list'], list): + if k in global_config['input_list']: + data_input[k] = np.array(v) + elif isinstance(global_config['input_list'], dict): + if k in global_config['input_list'].keys(): + data_input[global_config['input_list'][k]] = np.array(v) + outs = exe.run(val_program, + feed=data_input, + fetch_list=fetch_targets, + return_numpy=False) + res = {} + postprocess = YOLOv7PostProcess( + score_threshold=0.001, nms_threshold=0.65, multi_label=True) + res = postprocess(np.array(outs[0]), data_all['scale_factor']) + metric.update(data_all, res) + if batch_id % 100 == 0: + print('Eval iter:', batch_id) + metric.accumulate() + metric.log() + metric.reset() + + +def main(): + global global_config + all_config = load_slim_config(FLAGS.config_path) + global_config = all_config["Global"] + reader_cfg = load_config(global_config['reader_config']) + + dataset = reader_cfg['EvalDataset'] + global val_loader + val_loader = create('EvalReader')(reader_cfg['EvalDataset'], + reader_cfg['worker_num'], + return_list=True) + metric = None + if reader_cfg['metric'] == 'COCO': + clsid2catid = {v: k for k, v in dataset.catid2clsid.items()} + anno_file = dataset.get_anno() + metric = COCOMetric( + anno_file=anno_file, clsid2catid=clsid2catid, IouType='bbox') + elif reader_cfg['metric'] == 'VOC': + metric = VOCMetric( + label_list=dataset.get_label_list(), + class_num=reader_cfg['num_classes'], + map_type=reader_cfg['map_type']) + else: + raise ValueError("metric currently only supports COCO and VOC.") + global_config['metric'] = metric + + eval() + + +if __name__ == '__main__': + paddle.enable_static() + parser = argsparser() + FLAGS = parser.parse_args() + + assert FLAGS.devices in ['cpu', 'gpu', 'xpu', 'npu'] + paddle.set_device(FLAGS.devices) + + main() diff --git a/example/auto_compression/pytorch_yolov7/images/000000570688.jpg b/example/auto_compression/pytorch_yolov7/images/000000570688.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cb304bd56c4010c08611a30dcca58ea9140cea54 GIT binary patch literal 138365 zcmb5V3p~^B|35rulbBHE+i@z?~H~WK5`T$!vo2HfV;o1;E=dz2QM$Te`Fx;LK%mZj|4CcG|I-g*+Xv(R*thqe;>6_9Kp+3Lxw)VR z0?E)pp!LGd&DEOC&GlNKj`twYqll3B*o1%b0?LT*1A+df?>{NZj=_;(vB9w4!{QZt z_77r2*#G4G{q_HHH!>m~XzxFlBJfu}EXvOZ_|^s5SrHa@=(oHZsLCxg*a>JW2nEPv zpCEG#-$FXT@=G2|mBeGz321P6y+>fNx-2#kQY0>-swn(0}54FA#izJ_M-)>45C$ z0m$0{`5DL+6by<4g@Iy$?A|JK#(-);Dpu515yT|sdPzrrp5Q{Qh* z!m>S<{$0W;K&v>=8Q?i0C;^Zofd9gP{G|WU^S^uVf3^CLCcpFl*8JZ+JV5I5?MVJl zeIt^cfjoJ3^8ZWgL;tDE5%lTbBlO=bI{Tk;Kp0@GNWdrLKXv_gz5kW-ze@-M>iyqQ z{#UR217i;Z{j1&xm>N)8^#6=O*gq}){az6Mr!EjFERh7fM(N?GPnpm2KO$$t&(q-L9evRoSNU zyAUx6;2B9Nu#^;7W$Tu$D*wN)%|{?5S+Nh|X%b>eAaNxz2_>=3XCN(L@`y?OUgrPz z5)+pONl3}aN`mEp`yLSBKA^a`q=b~Tq@*NBQA`|Yf)Yp?Dx+hj>>#_#J6I)#vJJks zuo{fM^LXgTc3u3(eW%jrRS}M{JM>UKY9VnMPV}Oh+9zC~JPk1r&=dbv9WY77r6eS! zWn_W7j!Gagpeo>!k_2j%1T+IwmC%udnmI^$2gfK=3V%rNs=lML_wmrjZE*BFUS{8^ z*mU}KU7wmKT$E!*>*g3pUgDpJlt6Z%uNCH6quR$WBscFrSSM}UjbfRLDM-kuBT(kz zRtl| z1%{KhKCjA^7GS<0?LE^-E?K+eq-0VfEnHju9faByD2hSm&9tO*Yi&3=d>XmUWmHy0 zb?BOD32(4H*U&&kVy?yq=hJN?bfx&~H>yb2ctveFW4kne(1tfwjyy>l~JJ8bQaL$R3<#xoF? ze#N^1=XHLb>52M?%sa=~dXvgk(WDIN`*txyHKSQAa?@QU12rZ(-EnR97E`X!0IOA`HeV*jZILbMeBb1UaKW` z@)XWz#7GbI`TG??V#v5tt$k6N#59SQbx4M4(il~g&FkqXmmQLJSegD#$K)wSDqGfr zSY{dHgX11}vud1I>>R7ryY+-y=sGHb;ehiB7t~B=-pp`fa78DJ=IRYCoN6`A3!mhP zr9Y=-N!+umUtMU}al~{a%Fkav=gBXL_lk>~ARANJdycPI$VD~FI8qK+PElQNy8M!V z9@$JpoUS?aMFAAfw`r>{)r}}D>Zr6l=dj(r*L-=Ml)*Bl7^ps}SRZ_=j*xzWSM4>U zS`@2d9US|y@(KZglgW)gh0tZ&wZ`_@@}h)$N5-6QUQ9Y~mwidiPtHd= z?8()`Zk@Yh)shtT?>`M0zf`4pDKWma0IRMU-jM6R^;{N?2zjfl_iWHYBBe25?n&fK z^`8NEZ~5P~p*#q>+`rQ5@G3p!s`?AFwH;emkTP1&*1z2_EQ`DK>*}{5(O}cND|4Cm zx7?x6d%N%Jn7<{zm+rX(+UNb`F0Od-B(D0V76* ze1E@V%i}AqjE}U>+tM~lr)K7Fx(EDmF+a`7GJfWOx<`~~9dt}Ca9v6h+XkWo_2y^tTfi|)FWXSnYZ>H_yk+4jSHs_WXkppSf$z+ zLrPZ4ZF658mj>nYC;8Z*zFb^DM+!d@l~z!qYCcbezk;{QbXx9jy^@O^>>%&0MS1{* z`!v+n;H$K?$u2tmNXbe&9xixq!OCDpUHUoKJWX2%by&bafkO+{nWoCyE_-0RJ*~L_ znLB}0cQDEIC@nB885dAxuVG{AN!-ep_9N0@9EL==lA~1d7m1DP!`d$}YoiNjym61S zp=W9W$-;hbC@!#UrI#TQ>Vk{QSmGAL82OU}rTLAXg=>py*Xs&fF=87*W6*2%Q$Iz7 zOuFggbvt)le^fy}pNCZ-;v~cEhrmgjpm9%ML-rI+-4eMqsvs|8(awRnyAX*TFWEu0KPHigc#D%`b;QSX573t$QvM$uxX4b5AisXto}Mm%R%?1>;TH% zp;hwSPm~T^u}X7&fXa#Rg10B99tW#bM<;6#BPb+hf-Y6r65imDT`8cdR~d!H@G*_( z&P`BP-ZN>noV_XKPc|w%`P%L}aR|4(8jBmMT6R~;3^apvg7{~0MgRlKJ!zeE7y0UR32kZ$f1L$k+^&?d9mA_cm}D5(Can@U zNELWsS;vot!I-tS@}aHJsBMi5tV`Hdes|KOo4w25F4zFAcM{#UW2L!o@U6?UlY)k< z9dTHPby9yC#ZuCg+&@?I+-dQ+aa!E$@|)I{3c{rR+=jZcw3o#|mYN@}IroU37hHcF zoioE&wmglesKLk4crUniT($Cc*D17viOnYHO6dgzyocuwrD!Ejy4dD=rjrPapE!)- zNz@Wr3p~Kkl{;bah{tnEaNSs)Mh;N+=qAMB#{z))?7mnvqUkog8;2fpJfJzSQ^xNj@0=+>`Ru zHESo|PWH?uD9jkG-t$!Y(;q^Isi&~~!%Kttq9Qk?uU??138zOj9;QvK~zPqXJv$d$=E3G9#h_d z^o(0xC3JzY&MmwNUBz-pW~aG2oNiTxNlvXx%aom~jD%j*(NWV}BQD|O5w`pIKh?wb z;wwZLfo|{KnED+!xf)saFwtqIjA>|1d`~DLna0f3l3=l(?U>8xq^jf6m zM#$`(XVrM+B|6M#K=$J8UBRw)_oC}+BgIz;i5fz zzScUa9&lmn(o%z2B>HmSTwQG}V|^3kSYuNBN#K#ZF6@&0TZ&^#ZXs=9x~L=I{I!R| ztaMZt-fXI6t?h|IW1S8}f4nFq)swd#FTK`_yM7a9B-8Qh+#7d)tz4}3$(S{v{xHj8 z{+pfqe&w48_%|^TA;-atbgSYS0ZZSt1825noPYEsS^QaL{W$tyEBe_K8g$6RlaDTN zvntoki^>3{j1lP)oZ$PS*`>chnaXn}-jkJ-Y%^GS*N9 zH`Q-=p1$XL2fSZ8UQ-|Aun&87$Z5F5s@EB!WFm%ua-M^)F7x^O0p)QDl>MY~_{THQWx zf|_p%k3QrU@_WiXL+`PEVi%SMsx0e~SU9^JcMf~>BYe+UySLq$c`xu*Q@yqmLWw(` zUJU9^L=)cZ-6qIbv%p^6C@5*FqvOlsaOm}h>#%@sRwM)+7``S=KP2AcsSaT*4^W{v z=ZcAqQmbmXxMxvqBOZs5PWnuorsKPvg_h!2rNYIbnoz{@Kqf3HM6fYm2aHNWFG?jH zZ}YIbR2g>%)oQq_l1Bqj(I+QTW6b~^qG!jW^aO&w5-qn%}mX2XBr>g)o*IHDAQhw zzX;|s@~=2*mlSdRzuD%-EQk~+UBFuv1k6k{6Z!gROE-=HA5v}iL}wl)@AE2Ab2pQx zhigxwCs+2Ao5Jk9_1wM}+`cnhH=`eopYPXN7a~p-XGcx+7Jx0Gh3}TiOsdx9sjrJ=kQ=PcEh%m!!SG zb3MjTNGS)pyh}21Ra;%$0l5iEJJjvWwvmw?KK@NtGlw%6#&(I!_b}&(LjLT= ziAbEtph%_$zZd0%IN(rHGR4FZW$h~8x;4C4%gnQ(m7`uS`o9bRvPve=WzE)8jZs2e zT*WANPdahPO25}xNrTC`7@*1L6e?-lAW{f zKTl6r^vm*Qyl#lft#ouWg#|a%HbKWNa1eNR3~N+wR89N1tpAys#?nhH1K#kB#L-|c zFrx3t(%L|(yIqN6eWK{rE4Y-Kqbf<)Bw=Rj(yZ&fW+PqH&=9XB6FX8te5!Z4VaY2PUK^#{8PuQhzR!MtQX zdg-zM&-{sTjU&#*%UTn?_vf-Y;bt)N;b&^poy9#HJmFE*n^H2gGSlYLaj!t+((4MV zyT|+|vgD;(StEYTvR<`*Iq{WK53`r!xqfHVykFO^IG7!5{VBI%9Uv_W^>@{>+HmyB z%IL!H#cE&4yjif^KR4-0aP?J;*aTfHi=foVbd0MXtqv@>;LR9(^=QXn%TJkK$|CY2 zD70@h^r~h+d3I%UtHKQp)=BVuSrYLDRMC7OqsDkIMQg)_Lq*{*GCUV&QucSVNxmnr zvpc`-rstPd?dW8;XM|4D1LYN8svkv6C`PQVx3ysp$gG16Bt@}_CSgVaY(^tquvE)p~L^}a7{qa==z?wTBwy>2;{Q1`%Z&tlPm~GFf`~!guAeI83VD9Uktv#JhD;p$&fT)QX^L!Jd|! zQVNk|!5@i;utnFrRK5eIKW2QcdOH}~C)y|_s2hcn7iwFn9HabA&`V3~2vWP6Xa5Bm zQ;o9vOx5JMvd0JCqNSec8ddg0f+ZiMtZz&#?kZz+&b?D4BfTB9Q`IdSiJ|ota1pW3 zDN`1hfyMNY_KF!Z%c_8O>nW@xfzg~gSFMbID?HT#-do8Du-s(J#?d6W6EoG_7{Z{{ z3wf9huxSkc=R#dn?TFF2x!HzM#NkJSWG5m1%@-Z5m_^a)qXyq^L1NyeWP5OHZfN$@ zWelNMNvqR1d{OwQuO}JzTO$ zmwI5FvKwfX^Eez{lO5l-p|P)G`yv=QKhsTJEqhg!H$e>QvFPTl>v~(v_6Hr> zIGwpJb;a;OoSK*8aRzB4?07^+-23&oW@_95Fj9G|R1^ZP-$T}o$2v!7+mg7BYTp&V zGBf=WX~FsvT;N=jY?81| zBDfh*l887m2M!pm#$W_2VfOv1CQ2vGvvYNtAz9yE3Tbhx)p97nh8yM^ou{w(teDO-W34 zEcUT2-yM=`Piyy}dN(<)ZLrZUvY>N3T^${-xXLM36IMyIx->MZNe8T0s*;rSw9+^` za_4b)6*ge-*R=P<9wnprxu4vj{X>D}uMDhJE_s z1fjIdN6uhHFYu~b6+(xB4Okvt)+k=pWwY#=`3}9?% zMKuaddV&~M6A`z>i755=yuQdmxfm(hv_&a@W86+v9g=LArUZuD@i2{wHGzt-jV_c! zmxu0pcZLH_;nKAv0kLUo8xCXB)0~OMKb)uTSF!rMJb>D{D;OEHVdt*A0|zmpOya`S z5k~y)DP1Pzbkjb~wqf_ThlDIx&yZKDgHjB8nyJ2RxByE64r4v-q3k@Y9rU)=M0vy#=>Zdw6Jw zB_UI8$}#}l+j#gKkHIaZEpkVsp9rvzdrOaVTV|)w2XF!7nr*xO7AU5!5}DXh>h(s>GO{{A)%UHaEb1UcF!(!Cx^n~c2sI56P#WI>D$_lDIsP1&26$5xkD(~xfOC7 z&ann@H%vjFH|$ufFh`L!k(0>&jRSw`nx*Zn4|re-_llm$Itzj1`4 zh@zon`$PgEPJo@AT_Lz2)wG)nfR|t$rBcvql|&dpF+;|COQEK-8)`!ae!5w;evkZ4 z1TQY@e{G;h(pw+S!uHp|XKsBsO@5>MAo-tp3GXOJx+7J2XblHo0(}AFe$CUG>3O5b ze(gGzjMJ7TnM(yWtID*q^{cByrEA!CM~=ZeQd7y;Z7Kjvq0lfQZg+)R_@L_LIGgW1 z=G}$1s{E;5LP9)}V4rQztVW)AX?b)40xcfCi+${P_+Ew7r@c9U=Q5ZHOKTfl z4!aoX*5r7TMm1c3ey`a&YRP*qZu(QVbIdK6|MX`HO&=7338kydQ*l1UXge~}e#FN_ z=IdOIw``K2*3Zp~Ap6^WXybUnyK@f}81db$5b$w#n}S}lP}dTnKR1q!#uv`6qhPEh zp1r-|?Wu7WvrxIR1nn>N$RZ?e3Y~|5qfhl?RllVggIPwi-%$XnqY6tR&d`Hw?n{UW zjJ}8T!l{8QRa{bKB)Fmx`*=+VQ!%5H3K7zpYg2P|xc~xS(}oz;lPLlgr3On#LH^1{ zYNQp@5&>Ud73f8k2$ndMAw8@0?lGcdW_U-Bus5UM2EvZ|3pCVJFvG~(2qMM19%I?` z95<>$1i$+*t?rOYy;aJ<*gEuDrlD=7$58swwzHr1xHIF(>^IYIe-M6*aQ{6u#5ZL6{P(5^t?s13~ZQU#rj* z1Ea)4ZL>qS4jupF!)@^z^S?ZCdRJ>i(F+NAhRdg7_r~sjd-8`nO6`RDyyt&mNb`G=LqL1MRt9y?yYMteQ1ExdQy4tnxe%-W8fkG5MzrTo#Ea7{B_ zgtQ&`Ra#xish>~*JA1LA{zslsRd6a{e^s2)>yJ9I+qPNSR3XXl&BtG*JO9wi?ms-{UyILZ9f{C-P{RDB?b%-XxHT{|v2T@NAgi>p)mZ6dadySC)GJpS%nVE=eu@OI zs-AQ_Th?z+tt-t#*sLXTDjK%2zbtcdx&p)1Ho71e!J790lXTn0QJ;Pc#AxjA=HhEj>8i~mJSjZ}j?HIx!vn*+KvX_Z!Jyy-0rS%b&4O+TX z2Xb9~St-}n?y9-vIus4Yt%P_a9O@OwO?m7RI56ba6$+5LVQat|;TjLskx%~1?zr(6 zakOOkT0T`=AvbXe-VwDfeBONJjl)Hcq=X@vP?NmLx!I=0yVBcYIMgozUxA4&CcAA= zBzO^wJn>_Rhj+-HY6LcPg%wMLu@{>^jz$eB4)Wb)wYAt^selE`WYsl5Y_9?EiGtM> z5bR*JN;E| zbdJIGI~F;Wp*Qzc@5&(9TXPGS>;}=YROn=TmdehbY#tg~5H0g-n(@7a-?;(c0MTRs zNX*g%;0h0mddbs;*aVlwX?<>jM8H+efN;W2U z*@dko^QBxi^QQGmZ|0(-DhG!?L@(|o-Nkdccjx>>X5llX&ux0m^}9ISo7?Z*@cU9< z8)NknE^=t;wSNpYJc34|!Iu5mKc+uP>%3BcUzOpKY>M{Zi9AE}zIpf+_rt;2K8jfw zsOV$+Y&eVF{yZS;c8|}qCl?JOsq){x6m~wfx+467*l`)Ovq0mB+P$sc{7plS?#x~5 zo_bj-tdM!0qWy6sd#R~xDXz))bx2Yg>TmI>udGnNVl($+k+=8n{^>sBaAbG9Ui9NI zedGSCU*h_piNE^Zcpqs4ku5a4jtz(f4bICv-TkQ(d-Et!>7dx_7OIAH+Q5U3BkQku zv!fy7s2%sq>(-2la?9CW#>cKyR7SnId9bUsTCn?1i?tB> z>P12r#>Fpje(+S4*4Ks&eZ}0B7#k@~!J?3W!)^_s=;$1Xh` zu8Q+s8EreW*6>3+yR$^={Urxb1+Z(w_V~81%}-0O7~URVGx!2x`3~P! zIm_3)7u+ffORq|o?Tx20nuZBUPWc~6dA}5?wgoqP|3vbp2&>PhB7I9=c12oI&yO2^ zZo*qgqvA~)C8oKUrc`u1^>v)QIr{Hk^8Ysb#`2iXozmY6!6J0q)+@_W(ecPDfiM+HRn5mau?@*?I$9z{(_A5oI>z!KHg; zw%c}#F#QyRgK0O`;9uvKxzO|?TH$OX8XID<%yZ~cU7&1&$Wprsnp5p8iH2<6iogvC zc4ga&;zBP4LhSZ5Q%Oz;LlV}z%P}vdf|TDl-Eh0RGE{|0j$09&#t(^G5l3yH^CDVz zEmD$sQt3Uh5Gl3;iTjXRca@eBMphYgHAHF9104Zc-Dn`mQ zVQ(hq_OCcswhZG6x~3~EzVB!L+1occK(}f#OtsmMY^%;o;Npm0Im)#+?aXpW<#ppK#i?B}71iyV4jN{_n>%8W8wZPJKU`)=pH6UJbsbU|C} zSMx$`zYF z6TJ0#Zm}{_mOWK>O;%}5h!8`{Jf5wVDaG%#9V0lZt_l)dYb4Lv+@Hp$72G~smx(Y+ zHyan!8}9OAw=?RXApnkrSK{lV!Bl)PC)2F5ITKWnLDL*eCcZd1p%WxJiWsg6keI87 z)=Ns7e+UNxEG}D)8+a*3gKhu9WRR7>LALf&`mp@JNL6ULA4k6VzixB+bKD?O@^eX5 z9X4=ka6ranV|{f7^XY*9yXHO5%x=j)dz za`+pS!jFIV56?H&&b9k7uNto5pOzL49=@UMw<5Ia1%d-f6y%viGv-0N0bJ5Na`0PIq;sh?G=p#2oR65 zcMfHSZ-~68c+4-Bwhgd2fY?X56tq`yP$cn^rl%;<((2@ne z=hE$D0t4pU(OpW$)6Qzwl^!qXl_{#r8I{;jS3+X@Es0NpHbK{h(~q&>0BA4EU~2Q$ zP`qn?Y$5k|sG}6Jo3Uhbu~e63M0sCWZN!3iOjS!EgVrzF2=z%1lEMWn11#WQ1c~Sn zcElD?ofz0h zOI_pw)Rkh(hZ3sLqIC+-ot}^<|0(3+1c$>{&U4bkeQ!;5M2bQ5@GYx} z9O#J7i9!2A+?Ia$%A~sziIr+i1OgZ`K#oa4lT_wWZY<6QId*6);SLU9gi z3zNX25siJ6{6;2_OFYjcvwl$990*0Gbv=C zG+57SiHE)XBFLtVXP^IzlUcvONn_A@B3*W5GRbX2R)(OZA0E*%Q{(EF%iiQ3kc$Xu zg7kv*Pg1l4X@djTvC&uRmMrpjCuVQO6fjc9)#;f(aY)%wJ~(ocC(fj7J1ddROoS_HUc4V8<o3>(RlnM2Blnh--RS>KVp#E>Tf@yqYU1KCeU0p?Nz1;| zbX$M=pLtftHbDtDoX=jLU2O`>4OP|imHCzP$@8<_+ryin)ocj}|Mrio=D1VXI2vFk z35yop?zTGobu|Stsa0b^j1ev>#%qVl+1`6&mT@NU4PvUfqSU~*_YE9wi}@M((4+p< zZKRA%zwYPHWtI0v(m=9Xoay*`{i}(*X(oea!C_Pb{s&;3%iiS{kndNvX9nS0Kx9%B z745J34b|=>`5Vc6o(cT^4m+TVD=Wv%fm@E2tBL(u|kAN&dF6^R-Skj?v9&|5lwsdpa2N4 z)`=6$N7I~`C#Tj(yC6o3y;Lv8?XNW!uxUmkyZdUUCZw07lI`mUk{V1)@gfh@{1IEX z8v&Q6yXa0?h^D7Y@Wn;B^DBZ`Io(4ghkz}<_r?`YSF_DpI517~u_4-mYF*jE_RR12 z!n!gwSK1Gri?_DJB>DoO;CAq`NWdD>hoSQm*gQ+ff!i^natpKyc4{j`7+Nf05cEMcQU2)9HwEL1VDXTL<2sVK))%- z?&VzlLA0u*e7L$C3LH~dgK1#Y^Viz+7jo!zD8skAWQ_e|OywGv@vE5!vX#K{_@6AVmuf}M!{ehk7O!p(O{c|ojVjiU0BpN)U>;x8O5!@ zDvjIK6&#{+TWK%RGESZBDS%k`1m6Q}fswm>q{M4`?cBk-MnK#y*B(-w-JX^n!N^mt z7(2?u7tT%n|NNFr%n9;@}oBr zH)*rFPSSzPCyEkzE_*c2sjj0=Qz4T=-jJ+PWrL+tH8QYrc?u0}U7oK!19vKmtA^aj zrhTafV+YGoB%Fi3e&;4=tDNO@w)WZPOpiXLmp?hBK{okG#0F>7gIKFX?u1zq00WS% z(Dpc~Kq^F&PkX5Qibv|Oh6*S`DbbqylH6a(7xY0#~IcQA?VI)@yFyk)h&;TM% zMdlU+o4+9+mGqbsf-30MjuL1Pp} zOZ0oF#?9yIGCQ@GEEO6YFIo|XY;&>w+Q(7N1^E*c4y(cb_Efo?X^AQTKSQ@I4H-}` z;qTd{W@ zUhm4*u8*r$mt=WTraT+bQug!Xm_}xpXsg@-Fm7o*jut5w02l~U=%a)Z2^qenrS1i$ z?jE}B#G0VKO((B|kI8YJoXrppfz!Quty7!nk`l2fT#(&IrVlPa<|l{2GU9ix z1IE^)^%P*h#UHK_aFCDHvfJjy2Xb*h_Ah`M(@LL{1Q{(Z$RPfly11^+(<>k;TFdf~ zDdiT=@?^B;dcW4x?2A zFsMO({ZAp!4PhkG?k;2P)M588@qWh^Dnzjb(~SIIJa+|iOX#Q(aKj%tmm2+h?KvabD(MMI}KAe;%Ft|!IE!!^Qaq*rL+L1sIZv1Oam_$=e z?V_bhT@-_AnzIuD@6F75&27zpH(d0+fGP%s^riyb-}c7C3B8PlSAaQ}Czw&mfdES# zqpB)du!!L@@-&g2)wqB!tKsj7MXluEkeRvmCc8l2AY)&QxFrAbJ)g${=w69j-5?9i z-sIIYQaGVt^luyAs=}IuvE@VgE6#|p)|E4z;~#UI5Vx7PE*&+mmB|fn^f#@NR9Y~% zp+)+9>RjIXxO4BrlbSDm1Wk7}^ml$Z2%QOQn=qXOX>mhWq%y}J9vNL7jPpC3Fe0jq zd5=Vb?7vsSxlRUMuBDf^dE7k0sGeC;!`h>&W6t# zRlMjizS5MGg?{sFxX}IA)hEt^=1^*hSl%^S!EOz$-4I3-+rQB%+jMnd-Q;EqW7Nys ze0S>86VH1mXGq&;+`s;rDq$QU@N`LTyIrmF<*MPI_!rLm2hQ8CVv`-gc{hRv z!n@D){I$lo@!@CkE2D->%k0j+kry|SF_jTX#A@*>`AHE`55>6oaQ#j-O0Hr8c%yOn zhYvPNT@rB4yxvmThxFFZOMt^NviFw+`>p-M;mgcX9nJe>yDJp}XI)lwsykSWB&#Y) znx2pZ72M{b^FZ110HxaXIK&7rg;eA|Z#-P6Ryv4tE+f|ZvTWwxC2$x=keUk{i36Fs zMrBLOb4AtC(|cD$f+*_l6581xtAbiz^8^5IMR9uR*lqomNxG0Q8Fr*BKmsTCR9tnf zQGKL6S?e@eYVCGX>;1drp5Vpd{dmZJ|4Shq?RCB;5F=(hx>=v!O~qeE7?rInROF11 z%pQ-p-D)$N1%g!&xaQilH}BBl?+U0dE(+!gzX7AUN}dy|3E!P0E?rhFPh>R(Ds1f6 z&4UHBXMR*%N*PnKLKx+XP6Oexb`)<+fbCxex(`LiVUmCG(1C!u?WGlsII?u9BVAHq zav)R1j1O#Kv*lnx6TWBAU7aE;@l3 zvrL~|hl;iB$WSU&yX4;Bml`iOwWIJ)-=B6VJpJ=~MP=mMhl|^vpJ$#18yFiM4@4e0 zEjgbDx2x@Y_R20(L+gG1`4qdSjg!-%i3i^wp4B?j_$&3Y-^au0f8V`*CUbiyHhMUB z?MY-p!TD`<2O^SatlANimWAg}qscizcfH*HC%@SgayZ~IjIbX#I2IP#BAKSG`~+4WX!JFIxX{5>Q} zV$AvAvs=%lQhxopNFxU(&^(&-$8k$ruy=ijx#O3)hn#ok|9J5IW^<80?n%%dmGp!D zPqkc=ekAO?^y9szng2DDgi|XJ2R+nIdzmBMFRxB2);^=2{`Efajr3Tj-Jfmh6<0uK zBIkBVCCEJWV|ySWS?_fBe0)%+;KxdC4J-_zm(?7}y|EM}*6 zv~ZmdeYiJfF!$!tSHH1~rC-i8J81fKwVUm?o>8zT*<6?Dv~~C{YqOZoI^YMHH;hQS z8dkSm?j`qONxMGovew|8Q3NJnYhIEJb-dO!N)|9rJi}GyfL8{%=E^ zSBTp3oA@l`iL+!2v?SRSr6Z2QT|fYSZ#rHkrpCwqcJO$20TRM!MxFMSl@f)w@aQ@I zFK90Xb%oP6Xeq$Ci=8aM&rBH7t@Z7E3|P3b69#So`z=%KwSpyHC0P94{CAtSAux7s z<0zEMLGn8}%IZ}BKiQI~<)9O&yw-Nf{MtDC(N=(V*Xv z%u;Vl1hO8<)|CrYUJ{+>F>0erR0qmfRrYyFsm%aRV>CA>1hzsT)=r=d2GmujR` z5elxhwg;>OP*4;_b#}JyawaqNfdHrb9Uv%BU46Nex)3{KW2J+w@@cfI3ViQ6m6|vH zc}i2iAd{OQ16~oc;jpUMzRFV|FR!aqn7P?f5&(I^(^`IEL${dUq8M0DZ({0-8R=u_f&yDPN_ zNRAq?Pq+KM1LE57P+4X+KEUFQKVXhov}F>!gV%}rklsk#Z$RIfGx&*h6^8Q1Ld z%?Bf8U7xD47r1mES1Rf-0*E8O#J<-vfll6;t&{F^%(rpZKSgvg9y`Z6h62$&mEr#?;c{fN!qHB8U%BqbF*4ns- zLjGQJK6`Hyl(Jvb%Vq`sVs_vE{c@C6R4OA&E4-0x z(hwAYG|6B#O7276>hy>)ciRBC58#^v=QnunrWyJ@zowoyB@O3k?^)E<4a*$_!L5s#3b90!bXhrKx? z$iO^;t7|p8-{vB{;BGp;=Nqxs$$S&^XH^3LtN!}MW3;DJ0)hCPjj_))*QanHk0FZ# zm?71WTb&l7_4#`1;gdJqnA&Qq0#DT0&r`{|SVN^Lh?cvpHbDuCG$ui1Vv^);|#%P?S zrHrX$PE*_xH0NiT7K}(S9Z${sr}1=+q>3?ueRGU($l#tUdqwaqjS@7<)l(YUqp?-i zV4H=-{2~u9gxA+nkLwabtJ&Qs9CUv0tvMDDZvNa_ zTh@zf5S^p&ukTigS(0)<@c3&r826>Si`X<-sWs+e_;FmYXz2pH?fX_{uvr{LYIEb2 zsY8KrQy%#I)0dWjs7SSV&!A`;E5!M{+X|kh1dTCj@Wn$0t~7l-CJTh(UWPe;t?^2N zVfNJ{(LwO>_Ocoiisl1iCcPM7LVMsdS)R!N#l{J+_!LkaM&5ZI;1OuMg`;7TO!9+O z0Z`gHN=G`dvJUXd%BC3vMtaiuWlnJ$t{)(7{oOCLPftBAdmUKOR;OK%fy+M?IRtkj zP&aUZCdc(Dh$cxa_q}Vd%?3d6EU-S7v9)bg0X=VB{dBBYKu6VY$LaMcEzQ;7^hssF zVF%(1eUkZ+3=scvRYZO(-a^DTRT^S*_0K7Mq>S*6y1|0>?%G z0yjmS!1FCjUg8uB?Qf5Jw(7m;1PneTL!43HA?z(RuEEy|hel0uaqFpt+*U6+snohG zV?1MiZbk5#>-V(X24GQ(n^3%$IS~BS0jtlN>J3*|NdDuw>U~Y%0^ zQdKr#pp?ul{%#}u8-Eb@Vu$60s-Hlgpj`kGTBQ@qWoZB)4+So3rHozBH`vDRSm5Ai zD1k-ePIbFW@@=QkDD0#2c5mHH8OWa)MV_Eq=S&qwI`fFhUeXr}T!41TaMFyySJ`c| zd>AqF3BDUxMBHVs!$JmF2r(Tu?bXwkRRqTEx9lRouaqJUnU>HNhH9j<#Ou4IQD1DT zj~dYialQ_R*I##i5_I>!Px`BFZ22_#KJISaMY~VsuP#xNmY!VP?V_qGx%Ae8ZFovS zBiJ-v!f?dm=<%wV+ll_F0nJ;UQI<#JgsD3Qdk*gNeH`HE5_ledw0T(Iv8b=b?R_6= zn3}!yC2)w|Be5iU6O_B5c8+>%^`>Rv!a<(*^h22)j}81!hKy_E_(a}*d)bfNADL7k zyF~59_|`WxHEez1XvY$BU zq-EUe?XpVwzvk-7V9u2?&VFJXRxe3^6d5!H998kG(TImq!~lMu!qpaR1z{U-gbzyX zVt%p_#$!+T$_~DtM%Wa<*8xVEQI!vHGO$Yw(*+CY0~{6G!C#8>0*1d zrrN1ql;dK1v$X-%@)L0>a$9}q^Mc7m?Lkkc4{^As{lkvw`B^$A>TE@!H9F!}ip>QG zc&K?&60v{8M1ad911ItXxLqbrpFfE4H^y?2&)%_8uNI}*{`H8AdgKrji+;FKit++j zo4|1(g(l}F)g^!i2%SV1j%>Gp_y_^JU3uY4I3@P<%4ZO38@!E(#P3{TxU>jG1= z8gFpo>hf?=)*(_sLB@9!cZLRpwxUD_oSn=li>osRE@}I9?fvsYJHOj&@lPyotPvR1 zQ0Dr+Z_>H=5o8eC-E3r!M*eymeTI=QB12rRSF;BrHH!c%c6Or+VZ0yfHPxQE1)yc8 zJVl=b&TP$9Bd<$7NM7avD|yV6Q&p8O*{&i>EwgTKO8wW3QUp%lZ`(}D`v>1QDx=*THM zE6l`j&I-suzPva2>!-4V64g1u>^9DxwYyhskG!`cdc`f6=e1g%c-YZ{wAubyKF#b}De3NJ>7`S;yF}^k zbP18}E@_eOknWNeknYm&{rKk$v#v8fbK9bJDVDT-TZ**KGT6adhL^sI@E*U}awajh&ssG=J(6 z(IK>7LT2lvHY*QqSjWy!*>&kbJLr`!DSkzv{PoX|pchxKCsKOlgbcq`bYF)mUaX?? ztNFd85l8A974w%bCKNd@>@qzKwG3R}D7qs!n_pCJ8jqAH6*s-oZvq)gX44D5?CdG3 zYrC0Kb1>AsJ>&FyV?4W1rkiFi^+zZs@qi)t1C4QrcZF7jr9E%^%?QH0Iv;oQc|uJ< z%amL5*^eLYtvx$Jl}E0;bp$H%(%(PjfyLe2+{;C?TnRQ&+^2D@?wS`v`WLtnAotKs z=ZrTA_8g02{q*h$_O-lM84{92(&|U~nJb70^kwY-}}y$8}8 z&~O8IV5ytn3zzHy%%yP&YYslMOw!5Y?*$ZYyBPrMGETakR_FE6CP)R53JrySuk;YC z3`s*4ddjPN1{OXoJ|o0qg%-K(47+_kGzJN%*Cal%6@ekI@_u%SKzgsWCAWjI&=f$O zgEet*Sl1xZd^*FgxvTJmgu{8rDm2nt)nBgHk5hHWDBOW2LJEueVR3jhw?A;p&Z1Cv zJuG7es;1|q(Gag*X~YlLPsd8PIso6n0883WKrI+1!^w!uEWwGGgg!wmZ8pX<3jFk!PqWHP{*1UKzk- zNa5mUM*EFGap|*QgT9NVABilUasnGc$k|bF7J&2e%?s!98s&`<>qT-Vmp`v}LCZbN9nK?TO|&IcIm~$(GxHJ_+yA^SS1Uml#`Qecx2|1 z$MmU3Z}7$Sjay2%cGaaBQh5B&UC`*!lM$E|gMkwRAoQ0WkH%uD@?6EbR(Y6!_ekD- z4y6WAcke0zJHZka#5X0x7>sl*ARcZ|C3+eV0oU$J(`g4>+IC$@_k_eFs}WTJ@juXL zZA4F{7t~!U-oFMyxYP{9pjr7Qr zIs;~mMycQZkVNL6AXQiMOKmQdDXH!LOz!4Aa!7OkaO+`B1vtFKi{1{n(g$?D@~TeD z&0fAV6lp60!fjSrQS z=X6&U-#f8epZ#m@3}flFbHNWo<8%k~9Byq%{YSGz6kT8&ZF40NjuM8l zk83(qMUP3I{Tv_v?B~fk+E?L3wfg4)!KA029%V>z>Dp)n-+aotIp(1;&G6itjJC#o zTD`VdUEIeo=;I5;t&s((jcV&Z5dQQ4x1&#g>C%&J~$AJy$&lcsB&HQV21kV40eJlYp{UFeBMPi$=yu7iO{s+;E;3E7dIg0$xPenTHuOpxK?L5o)BXshX-_Fcr>&6s$WpNP+~-v z1Fc)mdT*T{0q>61BoowAE1GjvyNQ+?1YXqsk93R&)*+(?}(@Lig z?^l%#ho>NW18Y8lW=1(jyo%&lU%KK!$ zDq?m{*kQmUjaB+sz>(yeyg?6lbSYCE=xv7eeJ)z-`v;O@*brV8@M&F;KZ2gLSG-HF zEKcC2N!O>3(G2pXgnnS11nuVTQWFpI7Z6+`3e8z8rneV=Z9{Fxsj}O#=M~&Bp34|_ z79p({iAX=HQ}}f9%~3W3^J_cBFkES2XkhJl*lr8g^l&Ep zd6_h%%tPK5HLs>4<;P;mz%O?!_L{skQZztqnaw@nV_W!gkz zESI!Q2i(fE*%dubLYUXwq0+|`@?C^Y-+on$?_60rOX63oSU7VKM!Vr=On4tVi+?b1 zZe95KeRc@%@tey_s1M&OZ#lOr6Ojx3#pi+ipRk)iHZbZ@xm)?2`o>i@9ly&NwCW=7^&bs<81OmSFy>H+5>K3uZf=1!3 zXe`@*M$PhG;ay&n;K}0Grxf3!D?EW~IQl!xR8P)k%V8+nWHx@uhaf`z+Eqlu; z1oyV|^^cQM58ND!(v%a`(_l{LV)I~Z(wvLjcVb7rF26~-kxTN_DMB&yjs`vbQV+vG zHnj9efuv*_A)4Ty6tFf=YySE7&(`Bt3oJ_P3S zF^SMjqLGq0QPif5h(E#7qI+awdZ^U*i=neekVEgwL#s#eDrz4Z{j~&~=XCfpPd1*gJ6h-|0a_Csj<)9mai&@qRi&&ZC1774i%ejcD{v6NIFte>Cm$q6KDogfQdo0e`0O{AFJ#R508UKHGRy zTraX_W%wzgm4!`j~MduD1NBqUXv)#tpPewIS zQYuRlwtQL}OO4}i(0jb-twv^oh$Ce-W(86kGBdP6Z9MzS@o0laac@pS`;qD}njx7d z8(wKh`JmL*;lFs&mzvvln2 z(s#5&wf(!u2ilM+do)H(64~DLQ1o|VCh3gLZ$CVFjZ^H9a+BBRI2V{!3i~<}EF7>f z7xHnlHR>{?R-bK2zH~|DoiU@Y-w6rVZvO+}?D`UqOTM{o=ngVf=D;S_ikfqCBu9qY zs4s2=C(L_1E}+~^dh_a(kmcHXKUUbJ%F4o(e1*Za)3Lr)DC`~(cbQOVei6HjWQxN+ zo2g$XXI-ZzEi$cQUa~j;80<|IbaHDzuC9GIxQd)si|M?h2=*g zFwy=o^n0*^lfZ)`odz!|5QhBY4OUb!0aND&c9>`c8|VHNyNLC3zoU!A?y&arKJpGG-Y*G=UpU|PjThMt z`1S>Lcz(nV(kT>B@9#GJq^y`mZn4F_h!UVvQNA)oslo%j15?U%)>UvK&oh?By8MK* z42juxgjU1^sL`$0&}*n1_8|))%#@-L91Z?Jf0e6M zM^o@VVxD~pLbX-xcxG0cc2sBZl&0kxTbsLOcQP3^XP@9QAHI6e##P0_qwJhUyx7kj zIx}>V$DN&EF(oTBQNF9R`(omLQht47yy1_770jJmC??_E{A9mmns;m3aQYm0O(^zP zz^-(yHB+wZ2tNt@TWBEMvb%Wd9$_eyT19#7fJ0Oi9`B}rlc@7C)yhO;=)m60-RYG7YWcBR?z5A#>b1b1 z)9$&uBbQ6unA+RLOa~{twes*j(vl+Z!$(#zwad5WsQ8MQN*I-mf5_6%vs46gZKQ5< zpElV?rS|6;7Tnxc`?Zz|SJEd-+FM>Rh@Ne(N^-YU@wBuQ{8umLr_?d*=wosD_*Cge z%3~|h2S;|>?-m`LJmKCvFAx1d`LLneQ9>3aWS$#gZGW2aWSu$MR@AO2BP{zIh~}Q{ zU)crwwM$RUgTM9N;REJl*Ws;xZ}BdS$FzzS4FcYIQ9#f0CZX0#DCOMU!_F@%BK(m3qSO_vR7cio2To%2PP@t+OJZD2L%J}hd*V0DAwYt<9}P%sOG31Sfe!4O5@v0!mKbRnD)lue%q~O~FE}G=Nli;p!^#R? zVNDfa*y>J7fi4q32LU-{=@yN9ged^p$(nstt3I)rU%%CR{o>l{>fBi{@m0RKpCXiG zz#Nlp3>q{r{v{_ZET6fuv$073_d+R8ycWgs%;aOK4?BOzX5ew(XSZ`R`MR%el$fQ7syMu+#95Wp4G@ux%<_VPrp_x; z!vU5|)-VB&n(hfTWLN)VN#?env0Lz-xAVRFwS*P+v(hCqCdsMoq%^z8PdR^43^6AEvQw z0asp#at%NOh~DrThnT4iA;p_OcLn7rbPQcVpsh(~xp<%C&gY~eKaUr}qwVxw?Q%=} zmFylA@kuJ*Y(LzAd$AGWhN^zM?koQi?}?#>F9CfCYaJ5p)oCd@TIWi~rtzG`6s0kJ zJjA{|Q>C$!>!qZqVwWM6{%Q!+YG04dQQpO2GF)vEy^5Mr4%Sp+xOTD){n{{5Y+eN- zlXFEI_lxi7#H_7|9@6?Y5G)ohiVGlv?>8jh@BCz;T>k2CIcG9y-=%=@O;z}tkf_n$pqtV!4_h&zGIi^ zUg)j_raKjqX2RgN{c^@y{Ive!V5#4dEShR^&Rr}|&@|mgg|id;VuOk;alx^O6P5K6U~+ZwT?}OH&E_Jw)#$tVo#_J~uFnk-XTO3AFoi zbAaE`q}BZl$Q^sZz�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%0D4VMGW#Js4Aj+!>|GYQ+%PHsS*#KMEBnG$Kv zeYBe|RHu~_KGeH1i^<((2T8@IxCrrn^4@vmTu*|#RfvgMw5OcbnO$We$&0iu&*i>-khE6Hy^%Hm&q>*SC zCOh6a4Yb02R1^`!WSeMIU<;seJ8Sx@iIPnVNM27q6+~P48 zpua;#7Ee(Hcme3d#JL2dV1PAqM8{t^Nb-ipM*~B>F9Y)t0x&CKodfEk#A$Z@Y<`yp zsR5QD%-hj~AH4Fg65db3Gk~HDMDyaj6{X$q} zM*P&8f4rF?cEVLAjM2J0xE({YFd3$izICo$&$+BoTbi7%1`{OTC=&$cv9K^cra44Q z1iVRxZG$|9aCjkRIa#4`x~2#I1{q%?>!Zqa@kOtR>eAPQaSSv!z$1JcKSr?hIL*WH zDqF_|5@efitGiL>URhzJFc@uX&c~Z%ru?}B7hSS7{<=G79s=Q-)`2~U2F{lhQ)34{ z;z5-k9QWL6A6b5ys@hISH2T

#oy!`N);SsOiWwvC-xPQkZb#0s1vjtn2!IgMJ5c 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

KgbW0626^0HI!q^+J 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

xGbeq`b*lQpge4|)Ru{Kft<-09=sS z1UU`~$pgp($?ib^01=_Yi{+ApXyk%=#Qb|q@spQ$*?FHH?c3|;LxY;wrwCqI5D6VY zzy#oAAAhLrsf`e&TQ6nQUTWzorlF{=wi4yih2t_FAd(1X97kc3><+5&xYkf>nAZlr z(K8qFzfP)65^kmU8=N+p0ZUU=Euy|^MgreSMibO&4>)q z#A`#^59ThIix~N2%yuV83u)C12AcuVMmIIm=MClm024hu-Z>M~jlM>TOfCUcL&xRM z%0D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 RmToMuVX&ENIq_+-|JejjX*B=< literal 0 HcmV?d00001 diff --git a/example/auto_compression/pytorch_yolov7/paddle_trt_infer.py b/example/auto_compression/pytorch_yolov7/paddle_trt_infer.py new file mode 100644 index 00000000..fedc9cc1 --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/paddle_trt_infer.py @@ -0,0 +1,322 @@ +# Copyright (c) 2022 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 cv2 +import numpy as np +import argparse +import time + +from paddle.inference import Config +from paddle.inference import create_predictor + +from post_process import YOLOv7PostProcess + +CLASS_LABEL = [ + 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', + 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', + 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', + 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', + 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', + 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', + 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', + 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', + 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', + 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', + 'hair drier', 'toothbrush' +] + + +def generate_scale(im, target_shape, keep_ratio=True): + """ + Args: + im (np.ndarray): image (np.ndarray) + Returns: + im_scale_x: the resize ratio of X + im_scale_y: the resize ratio of Y + """ + origin_shape = im.shape[:2] + if keep_ratio: + im_size_min = np.min(origin_shape) + im_size_max = np.max(origin_shape) + target_size_min = np.min(target_shape) + target_size_max = np.max(target_shape) + im_scale = float(target_size_min) / float(im_size_min) + if np.round(im_scale * im_size_max) > target_size_max: + im_scale = float(target_size_max) / float(im_size_max) + im_scale_x = im_scale + im_scale_y = im_scale + else: + resize_h, resize_w = target_shape + im_scale_y = resize_h / float(origin_shape[0]) + im_scale_x = resize_w / float(origin_shape[1]) + return im_scale_y, im_scale_x + + +def image_preprocess(img_path, target_shape): + img = cv2.imread(img_path) + # Resize + im_scale_y, im_scale_x = generate_scale(img, target_shape) + img = cv2.resize( + img, + None, + None, + fx=im_scale_x, + fy=im_scale_y, + interpolation=cv2.INTER_LINEAR) + # Pad + im_h, im_w = img.shape[:2] + h, w = target_shape[:] + if h != im_h or w != im_w: + canvas = np.ones((h, w, 3), dtype=np.float32) + canvas *= np.array([114.0, 114.0, 114.0], dtype=np.float32) + canvas[0:im_h, 0:im_w, :] = img.astype(np.float32) + img = canvas + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + img = np.transpose(img, [2, 0, 1]) / 255 + img = np.expand_dims(img, 0) + scale_factor = np.array([[im_scale_y, im_scale_x]]) + return img.astype(np.float32), scale_factor + + +def get_color_map_list(num_classes): + color_map = num_classes * [0, 0, 0] + for i in range(0, num_classes): + j = 0 + lab = i + while lab: + color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j)) + color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j)) + color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j)) + j += 1 + lab >>= 3 + color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)] + return color_map + + +def draw_box(image_file, results, class_label, threshold=0.5): + srcimg = cv2.imread(image_file, 1) + for i in range(len(results)): + color_list = get_color_map_list(len(class_label)) + clsid2color = {} + classid, conf = int(results[i, 0]), results[i, 1] + if conf < threshold: + continue + xmin, ymin, xmax, ymax = int(results[i, 2]), int(results[i, 3]), int( + results[i, 4]), int(results[i, 5]) + + if classid not in clsid2color: + clsid2color[classid] = color_list[classid] + color = tuple(clsid2color[classid]) + + cv2.rectangle(srcimg, (xmin, ymin), (xmax, ymax), color, thickness=2) + print(class_label[classid] + ': ' + str(round(conf, 3))) + cv2.putText( + srcimg, + class_label[classid] + ':' + str(round(conf, 3)), (xmin, ymin - 10), + cv2.FONT_HERSHEY_SIMPLEX, + 0.8, (0, 255, 0), + thickness=2) + return srcimg + + +def load_predictor(model_dir, + run_mode='paddle', + batch_size=1, + device='CPU', + min_subgraph_size=3, + use_dynamic_shape=False, + trt_min_shape=1, + trt_max_shape=1280, + trt_opt_shape=640, + trt_calib_mode=False, + cpu_threads=1, + enable_mkldnn=False, + enable_mkldnn_bfloat16=False, + delete_shuffle_pass=False): + """set AnalysisConfig, generate AnalysisPredictor + Args: + model_dir (str): root path of __model__ and __params__ + device (str): Choose the device you want to run, it can be: CPU/GPU/XPU, default is CPU + run_mode (str): mode of running(paddle/trt_fp32/trt_fp16/trt_int8) + use_dynamic_shape (bool): use dynamic shape or not + trt_min_shape (int): min shape for dynamic shape in trt + trt_max_shape (int): max shape for dynamic shape in trt + trt_opt_shape (int): opt shape for dynamic shape in trt + trt_calib_mode (bool): If the model is produced by TRT offline quantitative + calibration, trt_calib_mode need to set True + delete_shuffle_pass (bool): whether to remove shuffle_channel_detect_pass in TensorRT. + Used by action model. + Returns: + predictor (PaddlePredictor): AnalysisPredictor + Raises: + ValueError: predict by TensorRT need device == 'GPU'. + """ + if device != 'GPU' and run_mode != 'paddle': + raise ValueError( + "Predict by TensorRT mode: {}, expect device=='GPU', but device == {}" + .format(run_mode, device)) + config = Config( + os.path.join(model_dir, 'model.pdmodel'), + os.path.join(model_dir, 'model.pdiparams')) + if device == 'GPU': + # initial GPU memory(M), device ID + config.enable_use_gpu(200, 0) + # optimize graph and fuse op + config.switch_ir_optim(True) + elif device == 'XPU': + config.enable_lite_engine() + config.enable_xpu(10 * 1024 * 1024) + else: + config.disable_gpu() + config.set_cpu_math_library_num_threads(cpu_threads) + if enable_mkldnn: + try: + # cache 10 different shapes for mkldnn to avoid memory leak + config.set_mkldnn_cache_capacity(10) + config.enable_mkldnn() + if enable_mkldnn_bfloat16: + config.enable_mkldnn_bfloat16() + except Exception as e: + print( + "The current environment does not support `mkldnn`, so disable mkldnn." + ) + pass + + precision_map = { + 'trt_int8': Config.Precision.Int8, + 'trt_fp32': Config.Precision.Float32, + 'trt_fp16': Config.Precision.Half + } + if run_mode in precision_map.keys(): + config.enable_tensorrt_engine( + workspace_size=(1 << 25) * batch_size, + max_batch_size=batch_size, + min_subgraph_size=min_subgraph_size, + precision_mode=precision_map[run_mode], + use_static=False, + use_calib_mode=trt_calib_mode) + + if use_dynamic_shape: + min_input_shape = { + 'image': [batch_size, 3, trt_min_shape, trt_min_shape] + } + max_input_shape = { + 'image': [batch_size, 3, trt_max_shape, trt_max_shape] + } + opt_input_shape = { + 'image': [batch_size, 3, trt_opt_shape, trt_opt_shape] + } + config.set_trt_dynamic_shape_info(min_input_shape, max_input_shape, + opt_input_shape) + print('trt set dynamic shape done!') + + # disable print log when predict + config.disable_glog_info() + # enable shared memory + config.enable_memory_optim() + # disable feed, fetch OP, needed by zero_copy_run + config.switch_use_feed_fetch_ops(False) + if delete_shuffle_pass: + config.delete_pass("shuffle_channel_detect_pass") + predictor = create_predictor(config) + return predictor + + +def predict_image(predictor, + image_file, + image_shape=[640, 640], + warmup=1, + repeats=1, + threshold=0.5, + arch='YOLOv5'): + img, scale_factor = image_preprocess(image_file, image_shape) + inputs = {} + if arch == 'YOLOv5': + inputs['x2paddle_images'] = img + input_names = predictor.get_input_names() + for i in range(len(input_names)): + input_tensor = predictor.get_input_handle(input_names[i]) + input_tensor.copy_from_cpu(inputs[input_names[i]]) + + for i in range(warmup): + predictor.run() + + np_boxes = None + predict_time = 0. + time_min = float("inf") + time_max = float('-inf') + for i in range(repeats): + start_time = time.time() + predictor.run() + output_names = predictor.get_output_names() + boxes_tensor = predictor.get_output_handle(output_names[0]) + np_boxes = boxes_tensor.copy_to_cpu() + end_time = time.time() + timed = end_time - start_time + time_min = min(time_min, timed) + time_max = max(time_max, timed) + predict_time += timed + + time_avg = predict_time / repeats + print('Inference time(ms): min={}, max={}, avg={}'.format( + round(time_min * 1000, 2), + round(time_max * 1000, 1), round(time_avg * 1000, 1))) + postprocess = YOLOv7PostProcess( + score_threshold=0.001, nms_threshold=0.65, multi_label=True) + res = postprocess(np_boxes, scale_factor) + res_img = draw_box( + image_file, res['bbox'], CLASS_LABEL, threshold=threshold) + cv2.imwrite('result.jpg', res_img) + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_file', type=str, default=None, help="image path") + parser.add_argument( + '--model_path', type=str, help="inference model filepath") + parser.add_argument( + '--benchmark', + type=bool, + default=False, + help="Whether run benchmark or not.") + parser.add_argument( + '--run_mode', + type=str, + default='paddle', + help="mode of running(paddle/trt_fp32/trt_fp16/trt_int8)") + parser.add_argument( + '--device', + type=str, + default='GPU', + help="Choose the device you want to run, it can be: CPU/GPU/XPU, default is GPU" + ) + parser.add_argument('--img_shape', type=int, default=640, help="input_size") + args = parser.parse_args() + + predictor = load_predictor( + args.model_path, run_mode=args.run_mode, device=args.device) + warmup, repeats = 1, 1 + if args.benchmark: + warmup, repeats = 50, 100 + predict_image( + predictor, + args.image_file, + image_shape=[args.img_shape, args.img_shape], + warmup=warmup, + repeats=repeats) diff --git a/example/auto_compression/pytorch_yolov7/post_process.py b/example/auto_compression/pytorch_yolov7/post_process.py new file mode 100644 index 00000000..853693da --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/post_process.py @@ -0,0 +1,173 @@ +# Copyright (c) 2022 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 numpy as np +import cv2 + + +def box_area(boxes): + """ + Args: + boxes(np.ndarray): [N, 4] + return: [N] + """ + return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]) + + +def box_iou(box1, box2): + """ + Args: + box1(np.ndarray): [N, 4] + box2(np.ndarray): [M, 4] + return: [N, M] + """ + area1 = box_area(box1) + area2 = box_area(box2) + lt = np.maximum(box1[:, np.newaxis, :2], box2[:, :2]) + rb = np.minimum(box1[:, np.newaxis, 2:], box2[:, 2:]) + wh = rb - lt + wh = np.maximum(0, wh) + inter = wh[:, :, 0] * wh[:, :, 1] + iou = inter / (area1[:, np.newaxis] + area2 - inter) + return iou + + +def nms(boxes, scores, iou_threshold): + """ + Non Max Suppression numpy implementation. + args: + boxes(np.ndarray): [N, 4] + scores(np.ndarray): [N, 1] + iou_threshold(float): Threshold of IoU. + """ + idxs = scores.argsort() + keep = [] + while idxs.size > 0: + max_score_index = idxs[-1] + max_score_box = boxes[max_score_index][None, :] + keep.append(max_score_index) + if idxs.size == 1: + break + idxs = idxs[:-1] + other_boxes = boxes[idxs] + ious = box_iou(max_score_box, other_boxes) + idxs = idxs[ious[0] <= iou_threshold] + + keep = np.array(keep) + return keep + + +class YOLOv7PostProcess(object): + """ + Post process of YOLOv6 network. + args: + score_threshold(float): Threshold to filter out bounding boxes with low + confidence score. If not provided, consider all boxes. + nms_threshold(float): The threshold to be used in NMS. + multi_label(bool): Whether keep multi label in boxes. + keep_top_k(int): Number of total bboxes to be kept per image after NMS + step. -1 means keeping all bboxes after NMS step. + """ + + def __init__(self, + score_threshold=0.25, + nms_threshold=0.5, + multi_label=False, + keep_top_k=300): + self.score_threshold = score_threshold + self.nms_threshold = nms_threshold + self.multi_label = multi_label + self.keep_top_k = keep_top_k + + def _xywh2xyxy(self, x): + # Convert from [x, y, w, h] to [x1, y1, x2, y2] + y = np.copy(x) + y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x + y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y + y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x + y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y + return y + + def _non_max_suppression(self, prediction): + max_wh = 4096 # (pixels) minimum and maximum box width and height + nms_top_k = 30000 + + cand_boxes = prediction[..., 4] > self.score_threshold # candidates + output = [np.zeros((0, 6))] * prediction.shape[0] + + for batch_id, boxes in enumerate(prediction): + # Apply constraints + boxes = boxes[cand_boxes[batch_id]] + if not boxes.shape[0]: + continue + # Compute conf (conf = obj_conf * cls_conf) + boxes[:, 5:] *= boxes[:, 4:5] + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + convert_box = self._xywh2xyxy(boxes[:, :4]) + + # Detections matrix nx6 (xyxy, conf, cls) + if self.multi_label: + i, j = (boxes[:, 5:] > self.score_threshold).nonzero() + boxes = np.concatenate( + (convert_box[i], boxes[i, j + 5, None], + j[:, None].astype(np.float32)), + axis=1) + else: + conf = np.max(boxes[:, 5:], axis=1) + j = np.argmax(boxes[:, 5:], axis=1) + re = np.array(conf.reshape(-1) > self.score_threshold) + conf = conf.reshape(-1, 1) + j = j.reshape(-1, 1) + boxes = np.concatenate((convert_box, conf, j), axis=1)[re] + + num_box = boxes.shape[0] + if not num_box: + continue + elif num_box > nms_top_k: + boxes = boxes[boxes[:, 4].argsort()[::-1][:nms_top_k]] + + # Batched NMS + c = boxes[:, 5:6] * max_wh + clean_boxes, scores = boxes[:, :4] + c, boxes[:, 4] + keep = nms(clean_boxes, scores, self.nms_threshold) + # limit detection box num + if keep.shape[0] > self.keep_top_k: + keep = keep[:self.keep_top_k] + output[batch_id] = boxes[keep] + return output + + def __call__(self, outs, scale_factor): + preds = self._non_max_suppression(outs) + bboxs, box_nums = [], [] + for i, pred in enumerate(preds): + if len(pred.shape) > 2: + pred = np.squeeze(pred) + if len(pred.shape) == 1: + pred = pred[np.newaxis, :] + pred_bboxes = pred[:, :4] + scale_factor = np.tile(scale_factor[i][::-1], (1, 2)) + pred_bboxes /= scale_factor + bbox = np.concatenate( + [ + pred[:, -1][:, np.newaxis], pred[:, -2][:, np.newaxis], + pred_bboxes + ], + axis=-1) + bboxs.append(bbox) + box_num = bbox.shape[0] + box_nums.append(box_num) + bboxs = np.concatenate(bboxs, axis=0) + box_nums = np.array(box_nums) + return {'bbox': bboxs, 'bbox_num': box_nums} diff --git a/example/auto_compression/pytorch_yolov7/post_quant.py b/example/auto_compression/pytorch_yolov7/post_quant.py new file mode 100644 index 00000000..8c866727 --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/post_quant.py @@ -0,0 +1,104 @@ +# Copyright (c) 2022 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 sys +import numpy as np +import argparse +import paddle +from ppdet.core.workspace import load_config, merge_config +from ppdet.core.workspace import create +from ppdet.metrics import COCOMetric, VOCMetric +from paddleslim.auto_compression.config_helpers import load_config as load_slim_config +from paddleslim.quant import quant_post_static + + +def argsparser(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--config_path', + type=str, + default=None, + help="path of compression strategy config.", + required=True) + parser.add_argument( + '--save_dir', + type=str, + default='ptq_out', + help="directory to save compressed model.") + parser.add_argument( + '--devices', + type=str, + default='gpu', + help="which device used to compress.") + parser.add_argument( + '--algo', type=str, default='KL', help="post quant algo.") + + return parser + + +def reader_wrapper(reader, input_list): + def gen(): + for data in reader: + in_dict = {} + if isinstance(input_list, list): + for input_name in input_list: + in_dict[input_name] = data[input_name] + elif isinstance(input_list, dict): + for input_name in input_list.keys(): + in_dict[input_list[input_name]] = data[input_name] + yield in_dict + + return gen + + +def main(): + global global_config + all_config = load_slim_config(FLAGS.config_path) + assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" + global_config = all_config["Global"] + reader_cfg = load_config(global_config['reader_config']) + + train_loader = create('EvalReader')(reader_cfg['TrainDataset'], + reader_cfg['worker_num'], + return_list=True) + train_loader = reader_wrapper(train_loader, global_config['input_list']) + + place = paddle.CUDAPlace(0) if FLAGS.devices == 'gpu' else paddle.CPUPlace() + exe = paddle.static.Executor(place) + quant_post_static( + executor=exe, + model_dir=global_config["model_dir"], + quantize_model_path=FLAGS.save_dir, + data_loader=train_loader, + model_filename=global_config["model_filename"], + params_filename=global_config["params_filename"], + batch_size=32, + batch_nums=10, + algo=FLAGS.algo, + hist_percent=0.999, + is_full_quantize=False, + bias_correction=False, + onnx_format=False) + + +if __name__ == '__main__': + paddle.enable_static() + parser = argsparser() + FLAGS = parser.parse_args() + + assert FLAGS.devices in ['cpu', 'gpu', 'xpu', 'npu'] + paddle.set_device(FLAGS.devices) + + main() diff --git a/example/auto_compression/pytorch_yolov7/run.py b/example/auto_compression/pytorch_yolov7/run.py new file mode 100644 index 00000000..ed73d81a --- /dev/null +++ b/example/auto_compression/pytorch_yolov7/run.py @@ -0,0 +1,172 @@ +# Copyright (c) 2022 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 sys +import numpy as np +import argparse +import paddle +from ppdet.core.workspace import load_config, merge_config +from ppdet.core.workspace import create +from ppdet.metrics import COCOMetric, VOCMetric +from paddleslim.auto_compression.config_helpers import load_config as load_slim_config +from paddleslim.auto_compression import AutoCompression + +from post_process import YOLOv7PostProcess + + +def argsparser(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--config_path', + type=str, + default=None, + help="path of compression strategy config.", + required=True) + parser.add_argument( + '--save_dir', + type=str, + default='output', + help="directory to save compressed model.") + parser.add_argument( + '--devices', + type=str, + default='gpu', + help="which device used to compress.") + parser.add_argument( + '--eval', type=bool, default=False, help="whether to run evaluation.") + + return parser + + +def reader_wrapper(reader, input_list): + def gen(): + for data in reader: + in_dict = {} + if isinstance(input_list, list): + for input_name in input_list: + in_dict[input_name] = data[input_name] + elif isinstance(input_list, dict): + for input_name in input_list.keys(): + in_dict[input_list[input_name]] = data[input_name] + yield in_dict + + return gen + + +def convert_numpy_data(data, metric): + data_all = {} + data_all = {k: np.array(v) for k, v in data.items()} + if isinstance(metric, VOCMetric): + for k, v in data_all.items(): + if not isinstance(v[0], np.ndarray): + tmp_list = [] + for t in v: + tmp_list.append(np.array(t)) + data_all[k] = np.array(tmp_list) + else: + data_all = {k: np.array(v) for k, v in data.items()} + return data_all + + +def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list): + metric = global_config['metric'] + for batch_id, data in enumerate(val_loader): + data_all = convert_numpy_data(data, metric) + data_input = {} + for k, v in data.items(): + if isinstance(global_config['input_list'], list): + if k in test_feed_names: + data_input[k] = np.array(v) + elif isinstance(global_config['input_list'], dict): + if k in global_config['input_list'].keys(): + data_input[global_config['input_list'][k]] = np.array(v) + outs = exe.run(compiled_test_program, + feed=data_input, + fetch_list=test_fetch_list, + return_numpy=False) + res = {} + postprocess = YOLOv7PostProcess( + score_threshold=0.001, nms_threshold=0.65, multi_label=True) + res = postprocess(np.array(outs[0]), data_all['scale_factor']) + metric.update(data_all, res) + if batch_id % 100 == 0: + print('Eval iter:', batch_id) + metric.accumulate() + metric.log() + map_res = metric.get_results() + metric.reset() + return map_res['bbox'][0] + + +def main(): + global global_config + all_config = load_slim_config(FLAGS.config_path) + assert "Global" in all_config, f"Key 'Global' not found in config file. \n{all_config}" + global_config = all_config["Global"] + reader_cfg = load_config(global_config['reader_config']) + + train_loader = create('EvalReader')(reader_cfg['TrainDataset'], + reader_cfg['worker_num'], + return_list=True) + train_loader = reader_wrapper(train_loader, global_config['input_list']) + + if 'Evaluation' in global_config.keys() and global_config[ + 'Evaluation'] and paddle.distributed.get_rank() == 0: + eval_func = eval_function + dataset = reader_cfg['EvalDataset'] + global val_loader + _eval_batch_sampler = paddle.io.BatchSampler( + dataset, batch_size=reader_cfg['EvalReader']['batch_size']) + val_loader = create('EvalReader')(dataset, + reader_cfg['worker_num'], + batch_sampler=_eval_batch_sampler, + return_list=True) + metric = None + if reader_cfg['metric'] == 'COCO': + clsid2catid = {v: k for k, v in dataset.catid2clsid.items()} + anno_file = dataset.get_anno() + metric = COCOMetric( + anno_file=anno_file, clsid2catid=clsid2catid, IouType='bbox') + elif reader_cfg['metric'] == 'VOC': + metric = VOCMetric( + label_list=dataset.get_label_list(), + class_num=reader_cfg['num_classes'], + map_type=reader_cfg['map_type']) + else: + raise ValueError("metric currently only supports COCO and VOC.") + global_config['metric'] = metric + else: + eval_func = None + + ac = AutoCompression( + model_dir=global_config["model_dir"], + model_filename=global_config["model_filename"], + params_filename=global_config["params_filename"], + save_dir=FLAGS.save_dir, + config=all_config, + train_dataloader=train_loader, + eval_callback=eval_func) + ac.compress() + + +if __name__ == '__main__': + paddle.enable_static() + parser = argsparser() + FLAGS = parser.parse_args() + + assert FLAGS.devices in ['cpu', 'gpu', 'xpu', 'npu'] + paddle.set_device(FLAGS.devices) + + main() diff --git a/example/auto_compression/semantic_segmentation/README.md b/example/auto_compression/semantic_segmentation/README.md index aa3842eb..f01bb904 100644 --- a/example/auto_compression/semantic_segmentation/README.md +++ b/example/auto_compression/semantic_segmentation/README.md @@ -14,12 +14,10 @@ ## 1.简介 -本示例将以语义分割模型PP-HumanSeg-Lite为例,介绍如何使用PaddleSeg中Inference部署模型进行自动压缩。本示例使用的自动压缩策略为非结构化稀疏、蒸馏和量化、蒸馏。 +本示例将以语义分割模型[PP-HumanSeg-Lite](https://github.com/PaddlePaddle/PaddleSeg/tree/develop/contrib/PP-HumanSeg#portrait-segmentation)为例,介绍如何使用PaddleSeg中Inference部署模型进行自动压缩。本示例使用的自动压缩策略为非结构化稀疏、蒸馏和量化、蒸馏。 ## 2.Benchmark -- [PP-HumanSeg-Lite](https://github.com/PaddlePaddle/PaddleSeg/tree/develop/contrib/PP-HumanSeg#portrait-segmentation) - | 模型 | 策略 | Total IoU | ARM CPU耗时(ms)
thread=1 |Nvidia GPU耗时(ms)| 配置文件 | Inference模型 | |:-----:|:-----:|:----------:|:---------:| :------:|:------:|:------:| | PP-HumanSeg-Lite | Baseline | 92.87 | 56.363 |-| - | [model](https://paddleseg.bj.bcebos.com/dygraph/ppseg/ppseg_lite_portrait_398x224_with_softmax.tar.gz) | @@ -34,7 +32,7 @@ | Deeplabv3-ResNet50 | Baseline | 79.90 | -|12.766| -| [model](https://paddleseg.bj.bcebos.com/tipc/easyedge/RES-paddle2-Deeplabv3-ResNet50.zip)| | Deeplabv3-ResNet50 | 量化训练 | 78.89 | - |8.839|[config](./configs/deeplabv3/deeplabv3_qat.yaml) | - | -- ARM CPU测试环境:`SDM710 2*A75(2.2GHz) 6*A55(1.7GHz)`; +- ARM CPU测试环境:`高通骁龙710处理器(SDM710 2*A75(2.2GHz) 6*A55(1.7GHz))`; - Nvidia GPU测试环境: @@ -65,6 +63,11 @@ pip install paddlepaddle-gpu pip install paddleslim ``` +准备paddleslim示例代码: +```shell +git clone https://github.com/PaddlePaddle/PaddleSlim.git +``` + 安装paddleseg ```shell @@ -77,15 +80,16 @@ pip install paddleseg 开发者可下载开源数据集 (如[AISegment](https://github.com/aisegmentcn/matting_human_datasets)) 或自定义语义分割数据集。请参考[PaddleSeg数据准备文档](https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.5/docs/data/marker/marker_cn.md)来检查、对齐数据格式即可。 -本示例使用示例开源数据集 AISegment 数据集为例介绍如何对PP-HumanSeg-Lite进行自动压缩。示例中的数据集仅用于快速跑通自动压缩流程,并不能复现出 benckmark 表中的压缩效果。 +本示例使用示例开源数据集 AISegment 数据集为例介绍如何对PP-HumanSeg-Lite进行自动压缩。示例数据集仅用于快速跑通自动压缩流程,并不能复现出 benckmark 表中的压缩效果。 可以通过以下命令下载人像分割示例数据: ```shell +cd PaddleSlim/example/auto_compression/semantic_segmentation python ./data/download_data.py mini_humanseg ### 下载后的数据位置为 ./data/humanseg/ ``` -** 提示: ** +**提示:** - PP-HumanSeg-Lite压缩过程使用的数据集 - 数据集:AISegment + PP-HumanSeg14K + 内部自建数据集。其中 AISegment 是开源数据集,可从[链接](https://github.com/aisegmentcn/matting_human_datasets)处获取;PP-HumanSeg14K 是 PaddleSeg 自建数据集,可从[官方渠道](https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.5/contrib/PP-HumanSeg/paper.md#pp-humanseg14k-a-large-scale-teleconferencing-video-dataset)获取;内部数据集不对外公开。 -- GitLab