first commit
Some checks are pending
Build/Publish Develop Docs / deploy (push) Waiting to run

This commit is contained in:
2025-07-02 08:57:16 +03:00
commit 56532cc9a9
1901 changed files with 457695 additions and 0 deletions

View File

@@ -0,0 +1,303 @@
# OCR模型自动压缩示例
目录:
- [OCR模型自动压缩示例](#ocr模型自动压缩示例)
- [1. 简介](#1-简介)
- [2. Benchmark](#2-benchmark)
- [PPOCRV4\_det](#ppocrv4_det)
- [PPOCRV4\_rec](#ppocrv4_rec)
- [3. 自动压缩流程](#3-自动压缩流程)
- [3.1 准备环境](#31-准备环境)
- [3.2 准备数据集](#32-准备数据集)
- [3.2.1 PPOCRV4\_det\_server数据集预处理](#321-ppocrv4_det_server数据集预处理)
- [3.3 准备预测模型](#33-准备预测模型)
- [4.预测部署](#4预测部署)
- [4.1 Paddle Inference 验证性能](#41-paddle-inference-验证性能)
- [4.1.1 使用测试脚本进行批量测试:](#411-使用测试脚本进行批量测试)
- [4.1.2 基于压缩模型进行基于GPU的批量测试](#412-基于压缩模型进行基于gpu的批量测试)
- [4.1.3 基于压缩前模型进行基于GPU的批量测试](#413-基于压缩前模型进行基于gpu的批量测试)
- [4.1.4 基于压缩模型进行基于CPU的批量测试](#414-基于压缩模型进行基于cpu的批量测试)
- [4.2 PaddleLite端侧部署](#42-paddlelite端侧部署)
- [5.FAQ](#5faq)
- [5.1 报错找不到模型文件或者数据集文件](#51-报错找不到模型文件或者数据集文件)
- [5.2 软件环境一致,硬件不同导致精度差异很大?](#52-软件环境一致硬件不同导致精度差异很大)
## 1. 简介
本示例将以图像分类模型PPOCRV3为例介绍如何使用PaddleOCR中Inference部署模型进行自动压缩。本示例使用的自动压缩策略为量化训练和蒸馏。
## 2. Benchmark
### PPOCRV4_det
| 模型 | 策略 | Metric(hmean) | GPU 耗时(ms) | ARM CPU 耗时(ms) | 配置文件 | Inference模型 |
|:------:|:------:|:------:|:------:|:------:|:------:|:------:|
| PP-OCRv4_mobile_det | Baseline | 72.71 | 5.7 | 92.0 | - | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/ch_PP-OCRv4_det_infer.tar) |
| PP-OCRv4_mobile_det | 量化+蒸馏 | 71.10 | 2.3 | 94.1 | [Config](./configs/ppocrv4/ppocrv4_det_qat_dist.yaml) | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/det_mobile_qat_3090.zip) |
| PP-OCRv4_server_det | Baseline | 79.82 | 32.6 | 844.7 | - | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/ch_PP-OCRv4_det_server_infer.tar) |
| PP-OCRv4_server_det | 量化+蒸馏 | 79.27 | 12.3 | 635.0 | [Config](./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml) | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/det_server_qat_3090.zip) |
> - GPU测试环境RTX 3090, cuda11.7+tensorrt8.4.2.4+paddle2.5
> - CPU测试环境Intel(R) Xeon(R) Gold 6226R使用12线程测试
> - PP-OCRv4_server_det在不完整的数据集上测试数据处理流程参考[ppocrv4_det_server数据集预处理](#321-ppocrv4_det_server数据集预处理),仅为了展示自动压缩效果,指标并不具有参考性,模型真实表现请参考[PPOCRV4介绍](../../../doc/doc_ch/PP-OCRv4_introduction.md)
| 模型 | 策略 | Metric(hmean) | GPU 耗时(ms) | ARM CPU 耗时(ms) | 配置文件 | Inference模型 |
|:------:|:------:|:------:|:------:|:------:|:------:|:------:|
| PP-OCRv4_mobile_det | Baseline | 72.71 | 4.7 | 198.4 | - | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/ch_PP-OCRv4_det_infer.tar) |
| PP-OCRv4_mobile_det | 量化+蒸馏 | 71.38 | 3.3 | 205.2 | [Config](./configs/ppocrv4/ppocrv4_det_qat_dist.yaml) | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/det_server_qat_v100.zip) |
| PP-OCRv4_server_det | Baseline | 79.77 | 50.0 | 2159.4 | - | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/ch_PP-OCRv4_det_server_infer.tar) |
| PP-OCRv4_server_det | 量化+蒸馏 | 79.81 | 42.4 | 1834.8 | [Config](./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml) | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/det/det_server_qat_v100.zip) |
> - GPU测试环境Tesla V100, cuda11.7+tensorrt8.4.2.4+paddle2.5.2
> - CPU测试环境Intel(R) Xeon(R) Gold 6271C使用12线程测试
> - PP-OCRv4_server_det在不完整的数据集上测试数据处理流程参考[ppocrv4_det_server数据集预处理](#321-ppocrv4_det_server数据集预处理),仅为了展示自动压缩效果,指标并不具有参考性,模型真实表现请参考[PPOCRV4介绍](../../../doc/doc_ch/PP-OCRv4_introduction.md)
### PPOCRV4_rec
| 模型 | 策略 | Metric(accuracy) | GPU 耗时(ms) | ARM CPU 耗时(ms) | 配置文件 | Inference模型 |
|:------:|:------:|:------:|:------:|:------:|:------:|:------:|
| 中文PPOCRV4-rec_mobile | Baseline | 78.92 | 1.7 | 33.3 | - | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/rec/ch_PP-OCRv4_rec_infer.tar.gz) |
| 中文PPOCRV4-rec_mobile | 量化+蒸馏 | 78.41 | 1.4 | 34.0 | [Config](./configs/ppocrv4/ppocrv4_rec_qat_dist.yaml) | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/rec/rec_mobile_qat.tar.gz) |
| 中文PPOCRV4-rec_server | Baseline | 81.62 | 4.0 | 62.5 | - | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/rec/ch_PP-OCRv4_rec_server_infer.tar.gz) |
| 中文PPOCRV4-rec_server | 量化+蒸馏 | 81.03 | 2.0 | 64.4 | [Config](./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml) | [Model](https://paddle-ocr-models.bj.bcebos.com/ppocrv4_qat/rec/rec_server_qat.tar.gz) |
> - GPU测试环境Tesla V100, cuda11.2+tensorrt8.0.3.4+paddle2.5
> - CPU测试环境Intel(R) Xeon(R) Gold 6271C使用12线程测试
## 3. 自动压缩流程
### 3.1 准备环境
- PaddlePaddle == 2.5 (可从[Paddle官网](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/pip/linux-pip.html)下载安装)
- PaddleSlim == 2.5
- PaddleOCR == develop
安装paddlepaddle
```shell
# CPU
python -m pip install paddlepaddle==2.5.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
# GPU 以Ubuntu、CUDA 10.2为例
python -m pip install paddlepaddle-gpu==2.5.1.post102 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html
```
安装paddleslim 2.5
```shell
pip install paddleslim@git+https://gitee.com/paddlepaddle/PaddleSlim.git@release/2.5
```
安装其他依赖:
```shell
pip install scikit-image imgaug
```
下载PaddleOCR:
```shell
git clone -b release/2.7 https://github.com/PaddlePaddle/PaddleOCR.git
cd PaddleOCR/
pip install -r requirements.txt
```
### 3.2 准备数据集
公开数据集可参考[OCR数据集](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7/doc/doc_ch/dataset/ocr_datasets.md),然后根据程序运行过程中提示放置到对应位置。
#### 3.2.1 PPOCRV4_det_server数据集预处理
PPOCRV4_det_server在使用原始数据集推理时默认将输入图像的最小边缩放到736然而原始数据集中存在一些长宽比很大的图像比如13:1此时再进行缩放就会导致长边的尺寸非常大在实验过程中发现最大的长边尺寸有10000+这导致在构建TensorRT子图的时候显存不足。
为了能顺利跑通自动压缩的流程,展示自动压缩的效果,因此需要对原始数据集进行预处理,将长宽比过大的图像进行剔除,处理脚本可见[ppocrv4_det_server_dataset_process.py](./ppocrv4_det_server_dataset_process.py)。
> 注意:使用不同的数据集需要修改配置文件中`dataset`中数据路径和数据处理部分。
### 3.3 准备预测模型
预测模型的格式为:`model.pdmodel``model.pdiparams`两个,带`pdmodel`的是模型文件,带`pdiparams`后缀的是权重文件。
> 注:其他像`__model__`和`__params__`分别对应`model.pdmodel` 和 `model.pdiparams`文件。
可在[PaddleOCR模型库](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7/doc/doc_ch/models_list.md)中直接获取Inference模型具体可参考下方获取中文PPOCRV4模型示例
```shell
https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_rec_infer.tar
tar -xf ch_PP-OCRv4_rec_infer.tar
```
```shell
wget https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_det_infer.tar
tar -xf ch_PP-OCRv4_det_infer.tar
```
蒸馏量化自动压缩示例通过run.py脚本启动会使用接口 ```paddleslim.auto_compression.AutoCompression``` 对模型进行量化训练和蒸馏。配置config文件中模型路径、数据集路径、蒸馏、量化和训练等部分的参数配置完成后便可开始自动压缩。
**单卡启动**
```shell
export CUDA_VISIBLE_DEVICES=0
python run.py --save_dir='./save_quant_ppocrv4_det/' --config_path='./configs/ppocrv4/ppocrv4_det_qat_dist.yaml'
```
**多卡启动**
若训练任务中包含大量训练数据,如果使用单卡训练,会非常耗时,使用分布式训练可以达到几乎线性的加速比。
```shell
export CUDA_VISIBLE_DEVICES=0,1,2,3
python -m paddle.distributed.launch run.py --save_dir='./save_quant_ppocrv4_det/' --config_path='./configs/ppocrv4/ppocrv4_det_qat_dist.yaml'
```
多卡训练指的是将训练任务按照一定方法拆分到多个训练节点完成数据读取、前向计算、反向梯度计算等过程,并将计算出的梯度上传至服务节点。服务节点在收到所有训练节点传来的梯度后,会将梯度聚合并更新参数。最后将参数发送给训练节点,开始新一轮的训练。多卡训练一轮训练能训练```batch size * num gpus```的数据,比如单卡的```batch size```为32单轮训练的数据量即32而四卡训练的```batch size```为32单轮训练的数据量为128。
注意 ```learning rate``` 与 ```batch size``` 呈线性关系,这里单卡 ```batch size``` 8对应的 ```learning rate``` 为0.00005,那么如果 ```batch size``` 增大4倍改为32```learning rate``` 也需乘以4多卡时 ```batch size``` 为8```learning rate``` 需乘上卡数。所以改变 ```batch size``` 或改变训练卡数都需要对应修改 ```learning rate```。
**验证精度**
根据训练log可以看到模型验证的精度若需再次验证精度修改配置文件```./configs/ppocrv3_det_qat_dist.yaml```中所需验证模型的文件夹路径及模型和参数名称```model_dir, model_filename, params_filename```,然后使用以下命令进行验证:
```shell
export CUDA_VISIBLE_DEVICES=0
python eval.py --config_path='./configs/ppocrv3_det_qat_dist.yaml'
```
## 4.预测部署
#### 4.1 Paddle Inference 验证性能
输出的量化模型也是静态图模型静态图模型在GPU上可以使用TensorRT进行加速在CPU上可以使用MKLDNN进行加速。
TensorRT预测环境配置
1. 如果使用 TesorRT 预测引擎,需安装 ```WITH_TRT=ON``` 的Paddle上述paddle下载的2.5满足打开TensorRT编译的要求。
2. 使用TensorRT预测需要进一步安装TensorRT安装TensorRT的方式参考[TensorRT安装说明](../../../docs/deployment/installtrt.md)。
以下字段用于配置预测参数:
| 参数名 | 含义 |
|:------:|:------:|
| model_path | inference 模型文件所在目录,该目录下需要有文件 .pdmodel 和 .pdiparams 两个文件 |
| model_filename | inference_model_dir文件夹下的模型文件名称 |
| params_filename | inference_model_dir文件夹下的参数文件名称 |
| dataset_config | 数据集配置的config |
| image_file | 待测试单张图片的路径如果设置image_file则dataset_config将无效。 |
| device | 预测时的设备,可选:`CPU`, `GPU`。 |
| use_trt | 是否使用 TesorRT 预测引擎在device为```GPU```时生效。 |
| use_mkldnn | 是否启用```MKL-DNN```加速库,注意```use_mkldnn```在device为```CPU```时生效。 |
| cpu_threads | CPU预测时使用CPU线程数量默认10 |
| precision | 预测时精度,可选:`fp32`, `fp16`, `int8`。 |
准备好预测模型并且修改dataset_config中数据集路径为正确的路径后启动测试
##### 4.1.1 使用测试脚本进行批量测试:
我们提供两个脚本文件用于测试模型自动化压缩的效果,分别是[test_ocr_det.sh](./test_ocr_det.sh)和[test_ocr_rec.sh](./test_ocr_rec.sh),这两个脚本都接收一个`model_type`参数用于区分是测试mobile模型还是server模型可选参数为`mobile`和`server`,使用示例:
```shell
# 测试mobile模型
bash test_ocr_det.sh mobile
bash test_ocr_rec.sh mobile
# 测试server模型
bash test_ocr_det.sh server
bash test_ocr_rec.sh server
```
##### 4.1.2 基于压缩模型进行基于GPU的批量测试
```shell
cd deploy/slim/auto_compression
python test_ocr.py \
--model_path save_quant_ppocrv4_det \
--config_path configs/ppocrv4/ppocrv4_det_qat_dist.yaml \
--device GPU \
--use_trt True \
--precision int8
```
##### 4.1.3 基于压缩前模型进行基于GPU的批量测试
```shell
cd deploy/slim/auto_compression
python test_ocr.py \
--model_path ch_PP-OCRv4_det_infer \
--config_path configs/ppocrv4/ppocrv4_rec_det_dist.yaml \
--device GPU \
--use_trt True \
--precision int8
```
##### 4.1.4 基于压缩模型进行基于CPU的批量测试
- MKLDNN预测
```shell
cd deploy/slim/auto_compression
python test_ocr.py \
--model_path save_quant_ppocrv4_det \
--config_path configs/ppocrv4/ppocrv4_det_qat_dist.yaml \
--device GPU \
--use_trt True \
--use_mkldnn=True \
--precision=int8 \
--cpu_threads=10
```
### 4.2 PaddleLite端侧部署
PaddleLite端侧部署可参考
- [Paddle Lite部署](https://github.com/PaddlePaddle/PaddleOCR/tree/9cdab61d909eb595af849db885c257ca8c74cb57/deploy/lite)
## 5.FAQ
### 5.1 报错找不到模型文件或者数据集文件
如果在推理或者跑ACT时报错找不到模型文件或者数据集文件可以检查一下配置文件中的路径是否正确以det_mobile为例配置文件中的指定模型路径的配置信息如下
```yaml
Global:
model_dir: ./models/ch_PP-OCRv4_det_infer
model_filename: inference.pdmodel
params_filename: inference.pdiparams
```
指定训练集验证集路径的配置信息如下:
```yaml
Train:
dataset:
name: SimpleDataSet
data_dir: datasets/chinese
label_file_list:
- datasets/chinese/zhongce_training_fix_1.6k.txt
- datasets/chinese/label_train_all_f4_part2.txt
- datasets/chinese/label_train_all_f4_part3.txt
- datasets/chinese/label_train_all_f4_part4.txt
- datasets/chinese/label_train_all_f4_part5.txt
- datasets/chinese/synth_en_my_clip.txt
- datasets/chinese/synth_ch_my_clip.txt
- datasets/chinese/synth_en_my_largeword_clip.txt
Eval:
dataset:
name: SimpleDataSet
data_dir: datasets/v4_4_test_dataset
label_file_list:
- datasets/v4_4_test_dataset/label.txt
```
### 5.2 软件环境一致,硬件不同导致精度差异很大?
这种情况是正常的TensorRT针对不同的硬件设备有着不同的优化方法同一种优化策略在不同硬件上可能有着截然不同的表现以本实验的ppocrv4_det_server为举例。截取[test_ocr.py](./test_ocr.py)中的一部分代码如下所示:
```python
if args.precision == 'int8' and "ppocrv4_det_server_qat_dist.yaml" in args.config_path:
# Use the following settings only when the hardware is a Tesla V100. If you are using
# a RTX 3090, use the settings in the else branch.
pred_cfg.enable_tensorrt_engine(
workspace_size=1 << 30,
max_batch_size=1,
min_subgraph_size=30,
precision_mode=precision_map[args.precision],
use_static=True,
use_calib_mode=False, )
pred_cfg.exp_disable_tensorrt_ops(["elementwise_add"])
else:
pred_cfg.enable_tensorrt_engine(
workspace_size=1 << 30,
max_batch_size=1,
min_subgraph_size=4,
precision_mode=precision_map[args.precision],
use_static=True,
use_calib_mode=False, )
```
当硬件为RTX 3090的时候使用else分支中的策略即可得到正常的结果但是当硬件是Tesla V100的时候必须使用if分支中的策略才能保证量化后精度不下降具体结果参考[benchmark](#2-benchmark)。

View File

@@ -0,0 +1,163 @@
Global:
model_type: det
model_dir: ./models/ch_PP-OCRv4_det_infer
model_filename: inference.pdmodel
params_filename: inference.pdiparams
algorithm: DB
Distillation:
alpha: 1.0
loss: l2
QuantAware:
use_pact: false
activation_bits: 8
is_full_quantize: false
onnx_format: false
activation_quantize_type: moving_average_abs_max
weight_quantize_type: channel_wise_abs_max
not_quant_pattern:
- skip_quant
quantize_op_types:
- conv2d
weight_bits: 8
TrainConfig:
epochs: 2
eval_iter: 200
learning_rate:
type: CosineAnnealingDecay
learning_rate: 0.000005
optimizer_builder:
optimizer:
type: Adam
weight_decay: 5.0e-05
PostProcess:
name: DBPostProcess
thresh: 0.3
box_thresh: 0.6
max_candidates: 1000
unclip_ratio: 1.5
Metric:
name: DetMetric
main_indicator: hmean
Train:
dataset:
name: SimpleDataSet
data_dir: datasets/chinese
label_file_list:
- datasets/chinese/zhongce_training_fix_1.6k.txt
- datasets/chinese/label_train_all_f4_part2.txt
- datasets/chinese/label_train_all_f4_part3.txt
- datasets/chinese/label_train_all_f4_part4.txt
- datasets/chinese/label_train_all_f4_part5.txt
- datasets/chinese/synth_en_my_clip.txt
- datasets/chinese/synth_ch_my_clip.txt
- datasets/chinese/synth_en_my_largeword_clip.txt
ratio_list:
- 0.3
- 0.2
- 0.1
- 0.2
- 0.2
- 0.1
- 0.2
- 0.2
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- DetLabelEncode: null
- IaaAugment:
augmenter_args:
- type: Fliplr
args:
p: 0.5
- type: Affine
args:
rotate:
- -10
- 10
- type: Resize
args:
size:
- 0.5
- 3
- EastRandomCropData:
size:
- 960
- 960
max_tries: 50
keep_ratio: true
- MakeBorderMap:
shrink_ratio: 0.4
thresh_min: 0.3
thresh_max: 0.7
- MakeShrinkMap:
shrink_ratio: 0.4
min_text_size: 8
- NormalizeImage:
scale: 1./255.
mean:
- 0.485
- 0.456
- 0.406
std:
- 0.229
- 0.224
- 0.225
order: hwc
- ToCHWImage: null
- KeepKeys:
keep_keys:
- image
- threshold_map
- threshold_mask
- shrink_map
- shrink_mask
loader:
shuffle: true
drop_last: false
batch_size_per_card: 4
num_workers: 4
Eval:
dataset:
name: SimpleDataSet
data_dir: datasets/v4_4_test_dataset
label_file_list:
- datasets/v4_4_test_dataset/label.txt
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- DetLabelEncode: null
- DetResizeForTest:
limit_side_len: 960
limit_type: max
- NormalizeImage:
scale: 1./255.
mean:
- 0.485
- 0.456
- 0.406
std:
- 0.229
- 0.224
- 0.225
order: hwc
- ToCHWImage: null
- KeepKeys:
keep_keys:
- image
- shape
- polys
- ignore_tags
loader:
shuffle: false
drop_last: false
batch_size_per_card: 1
num_workers: 10

View File

@@ -0,0 +1,161 @@
Global:
model_type: det
model_dir: ./models/ch_PP-OCRv4_det_server_infer
model_filename: inference.pdmodel
params_filename: inference.pdiparams
algorithm: DB
Distillation:
alpha: 1.0
loss: l2
QuantAware:
use_pact: false
activation_bits: 8
is_full_quantize: false
onnx_format: false
activation_quantize_type: moving_average_abs_max
weight_quantize_type: channel_wise_abs_max
not_quant_pattern:
- skip_quant
quantize_op_types:
- conv2d
weight_bits: 8
TrainConfig:
epochs: 1
eval_iter: 200
learning_rate:
type: CosineAnnealingDecay
learning_rate: 0.000005
optimizer_builder:
optimizer:
type: Adam
weight_decay: 5.0e-05
PostProcess:
name: DBPostProcess
thresh: 0.3
box_thresh: 0.6
max_candidates: 1000
unclip_ratio: 1.5
Metric:
name: DetMetric
main_indicator: hmean
Train:
dataset:
name: SimpleDataSet
data_dir: datasets/chinese
label_file_list:
- datasets/chinese/zhongce_training_fix_1.6k.txt
- datasets/chinese/label_train_all_f4_part2.txt
- datasets/chinese/label_train_all_f4_part3.txt
- datasets/chinese/label_train_all_f4_part4.txt
- datasets/chinese/label_train_all_f4_part5.txt
- datasets/chinese/synth_en_my_clip.txt
- datasets/chinese/synth_ch_my_clip.txt
- datasets/chinese/synth_en_my_largeword_clip.txt
ratio_list:
- 0.3
- 0.2
- 0.1
- 0.2
- 0.2
- 0.1
- 0.2
- 0.2
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- DetLabelEncode: null
- IaaAugment:
augmenter_args:
- type: Fliplr
args:
p: 0.5
- type: Affine
args:
rotate:
- -10
- 10
- type: Resize
args:
size:
- 0.5
- 3
- EastRandomCropData:
size:
- 960
- 960
max_tries: 50
keep_ratio: true
- MakeBorderMap:
shrink_ratio: 0.4
thresh_min: 0.3
thresh_max: 0.7
- MakeShrinkMap:
shrink_ratio: 0.4
min_text_size: 8
- NormalizeImage:
scale: 1./255.
mean:
- 0.485
- 0.456
- 0.406
std:
- 0.229
- 0.224
- 0.225
order: hwc
- ToCHWImage: null
- KeepKeys:
keep_keys:
- image
- threshold_map
- threshold_mask
- shrink_map
- shrink_mask
loader:
shuffle: true
drop_last: false
batch_size_per_card: 2
num_workers: 8
Eval:
dataset:
name: SimpleDataSet
data_dir: datasets/v4_4_test_dataset_small
label_file_list:
- datasets/v4_4_test_dataset_small/label.txt
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- DetLabelEncode: null
- DetResizeForTest: null
- NormalizeImage:
scale: 1./255.
mean:
- 0.485
- 0.456
- 0.406
std:
- 0.229
- 0.224
- 0.225
order: hwc
- ToCHWImage: null
- KeepKeys:
keep_keys:
- image
- shape
- polys
- ignore_tags
loader:
shuffle: false
drop_last: false
batch_size_per_card: 1
num_workers: 2

View File

@@ -0,0 +1,115 @@
Global:
model_dir: ./models/ch_PP-OCRv4_rec_infer
model_filename: inference.pdmodel
params_filename: inference.pdiparams
model_type: rec
algorithm: SVTR
character_dict_path: ./ppocr_keys_v1.txt
max_text_length: &max_text_length 25
use_space_char: true
Distillation:
alpha: [1.0, 1.0]
loss: ['skd', 'l2']
node:
- ['softmax_11.tmp_0']
- ['linear_170.tmp_1']
QuantAware:
use_pact: false
activation_bits: 8
is_full_quantize: false
onnx_format: false
activation_quantize_type: moving_average_abs_max
weight_quantize_type: channel_wise_abs_max
not_quant_pattern:
- skip_quant
quantize_op_types:
- conv2d
weight_bits: 8
TrainConfig:
epochs: 1
eval_iter: 1000
logging_iter: 100
learning_rate:
type: CosineAnnealingDecay
learning_rate: 0.00001
optimizer_builder:
optimizer:
type: Adam
weight_decay: 5.0e-05
PostProcess:
name: CTCLabelDecode
Metric:
name: RecMetric
main_indicator: acc
ignore_space: False
Train:
dataset:
name: MultiScaleDataSet
ds_width: false
data_dir: datasets/real_data/
label_file_list:
- datasets/real_data/train_list.txt
ext_op_transform_idx: 1
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- RecConAug:
prob: 0.5
ext_data_num: 2
image_shape: [48, 320, 3]
max_text_length: *max_text_length
- RecAug:
- MultiLabelEncode:
gtc_encode: NRTRLabelEncode
- KeepKeys:
keep_keys:
- image
- label_ctc
- label_gtc
- length
- valid_ratio
sampler:
name: MultiScaleSampler
scales: [[320, 32], [320, 48], [320, 64]]
first_bs: &bs 64
fix_bs: false
divided_factor: [8, 16] # w, h
is_training: True
loader:
shuffle: true
batch_size_per_card: *bs
drop_last: true
num_workers: 8
Eval:
dataset:
name: SimpleDataSet
data_dir: datasets/real_data/
label_file_list:
- datasets/real_data/val_list.txt
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- MultiLabelEncode:
gtc_encode: NRTRLabelEncode
- RecResizeImg:
image_shape: [3, 48, 320]
- KeepKeys:
keep_keys:
- image
- label_ctc
- label_gtc
- length
- valid_ratio
loader:
shuffle: false
drop_last: false
batch_size_per_card: 1
num_workers: 4

View File

@@ -0,0 +1,113 @@
Global:
model_dir: ./models/ch_PP-OCRv4_rec_server_infer
model_filename: inference.pdmodel
params_filename: inference.pdiparams
model_type: rec
algorithm: SVTR
character_dict_path: ./ppocr_keys_v1.txt
max_text_length: &max_text_length 25
use_space_char: true
Distillation:
alpha: 1.0
loss: 'l2'
QuantAware:
use_pact: false
activation_bits: 8
is_full_quantize: false
onnx_format: false
activation_quantize_type: moving_average_abs_max
weight_quantize_type: channel_wise_abs_max
not_quant_pattern:
- skip_quant
quantize_op_types:
- conv2d
weight_bits: 8
TrainConfig:
epochs: 1
eval_iter: 1000
logging_iter: 100
learning_rate:
type: CosineAnnealingDecay
learning_rate: 0.00001
optimizer_builder:
optimizer:
type: Adam
weight_decay: 5.0e-05
PostProcess:
name: CTCLabelDecode
Metric:
name: RecMetric
main_indicator: acc
ignore_space: False
Train:
dataset:
name: MultiScaleDataSet
ds_width: false
data_dir: datasets/real_data/
ext_op_transform_idx: 1
label_file_list:
- datasets/real_data/train_list.txt
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- RecConAug:
prob: 0.5
ext_data_num: 2
image_shape: [48, 320, 3]
max_text_length: *max_text_length
- RecAug:
- MultiLabelEncode:
gtc_encode: NRTRLabelEncode
- KeepKeys:
keep_keys:
- image
- label_ctc
- label_gtc
- length
- valid_ratio
sampler:
name: MultiScaleSampler
scales: [[320, 32], [320, 48], [320, 64]]
first_bs: &bs 64
fix_bs: false
divided_factor: [8, 16] # w, h
is_training: True
loader:
shuffle: true
batch_size_per_card: *bs
drop_last: true
num_workers: 8
Eval:
dataset:
name: SimpleDataSet
data_dir: datasets/real_data/
label_file_list:
- datasets/real_data/val_list.txt
transforms:
- DecodeImage:
img_mode: BGR
channel_first: false
- MultiLabelEncode:
gtc_encode: NRTRLabelEncode
- RecResizeImg:
image_shape: [3, 48, 320]
- KeepKeys:
keep_keys:
- image
- label_ctc
- label_gtc
- length
- valid_ratio
loader:
shuffle: false
drop_last: false
batch_size_per_card: 1
num_workers: 4

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
import os
import cv2
dataset_path = "datasets/v4_4_test_dataset"
annotation_file = "datasets/v4_4_test_dataset/label.txt"
small_images_path = "datasets/v4_4_test_dataset_small"
new_annotation_file = "datasets/v4_4_test_dataset_small/label.txt"
os.makedirs(small_images_path, exist_ok=True)
with open(annotation_file, "r") as f:
lines = f.readlines()
for i, line in enumerate(lines):
image_name = line.split(" ")[0]
image_path = os.path.join(dataset_path, image_name)
try:
image = cv2.imread(image_path)
height, width, _ = image.shape
# 如果图像的宽度和高度都小于2000而且长宽比小于2将其复制到新的文件夹并保存其标注信息
if height < 2000 and width < 2000:
if max(height, width) / min(height, width) < 2:
print(i, height, width, image_path)
small_image_path = os.path.join(small_images_path, image_name)
cv2.imwrite(small_image_path, image)
with open(new_annotation_file, "a") as f:
f.write(f"{line}")
except:
continue

View File

@@ -0,0 +1,171 @@
# 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 logging
from tqdm import tqdm
import numpy as np
import argparse
import paddle
from paddleslim.common import load_config as load_slim_config
from paddleslim.common import get_logger
from paddleslim.auto_compression import AutoCompression
from paddleslim.common.dataloader import get_feed_vars
import sys
sys.path.append("../../../")
from ppocr.data import build_dataloader
from ppocr.postprocess import build_post_process
from ppocr.metrics import build_metric
logger = get_logger(__name__, level=logging.INFO)
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."
)
return parser
def reader_wrapper(reader, input_name):
if isinstance(input_name, list) and len(input_name) == 1:
input_name = input_name[0]
def gen(): # 形成一个字典输入
for i, batch in enumerate(reader()):
yield {input_name: batch[0]}
return gen
def eval_function(exe, compiled_test_program, test_feed_names, test_fetch_list):
post_process_class = build_post_process(all_config["PostProcess"], global_config)
eval_class = build_metric(all_config["Metric"])
model_type = global_config["model_type"]
with tqdm(
total=len(val_loader),
bar_format="Evaluation stage, Run batch:|{bar}| {n_fmt}/{total_fmt}",
ncols=80,
) as t:
for batch_id, batch in enumerate(val_loader):
images = batch[0]
try:
(preds,) = exe.run(
compiled_test_program,
feed={test_feed_names[0]: images},
fetch_list=test_fetch_list,
)
except:
preds, _ = exe.run(
compiled_test_program,
feed={test_feed_names[0]: images},
fetch_list=test_fetch_list,
)
batch_numpy = []
for item in batch:
batch_numpy.append(np.array(item))
if model_type == "det":
preds_map = {"maps": preds}
post_result = post_process_class(preds_map, batch_numpy[1])
eval_class(post_result, batch_numpy)
elif model_type == "rec":
post_result = post_process_class(preds, batch_numpy[1])
eval_class(post_result, batch_numpy)
t.update()
metric = eval_class.get_metric()
logger.info("metric eval ***************")
for k, v in metric.items():
logger.info("{}:{}".format(k, v))
if model_type == "det":
return metric["hmean"]
elif model_type == "rec":
return metric["acc"]
return metric
def main():
rank_id = paddle.distributed.get_rank()
if args.devices == "gpu":
place = paddle.CUDAPlace(rank_id)
paddle.set_device("gpu")
else:
place = paddle.CPUPlace()
paddle.set_device("cpu")
global all_config, global_config
all_config = load_slim_config(args.config_path)
if "Global" not in all_config:
raise KeyError(f"Key 'Global' not found in config file. \n{all_config}")
global_config = all_config["Global"]
gpu_num = paddle.distributed.get_world_size()
train_dataloader = build_dataloader(all_config, "Train", args.devices, logger)
global val_loader
val_loader = build_dataloader(all_config, "Eval", args.devices, logger)
if (
isinstance(all_config["TrainConfig"]["learning_rate"], dict)
and all_config["TrainConfig"]["learning_rate"]["type"] == "CosineAnnealingDecay"
):
steps = len(train_dataloader) * all_config["TrainConfig"]["epochs"]
all_config["TrainConfig"]["learning_rate"]["T_max"] = steps
print("total training steps:", steps)
global_config["input_name"] = get_feed_vars(
global_config["model_dir"],
global_config["model_filename"],
global_config["params_filename"],
)
ac = AutoCompression(
model_dir=global_config["model_dir"],
model_filename=global_config["model_filename"],
params_filename=global_config["params_filename"],
save_dir=args.save_dir,
config=all_config,
train_dataloader=reader_wrapper(train_dataloader, global_config["input_name"]),
eval_callback=eval_function if rank_id == 0 else None,
eval_dataloader=reader_wrapper(val_loader, global_config["input_name"]),
)
ac.compress()
if __name__ == "__main__":
paddle.enable_static()
parser = argsparser()
args = parser.parse_args()
main()

View File

@@ -0,0 +1,292 @@
# Copyright (c) 2023 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 argparse
import time
import os
import sys
import cv2
import numpy as np
import paddle
import logging
import numpy as np
import argparse
from tqdm import tqdm
import paddle
from paddleslim.common import load_config as load_slim_config
from paddleslim.common import get_logger
import sys
sys.path.append("../../../")
from ppocr.data import build_dataloader
from ppocr.postprocess import build_post_process
from ppocr.metrics import build_metric
from paddle.inference import create_predictor, PrecisionType
from paddle.inference import Config as PredictConfig
logger = get_logger(__name__, level=logging.INFO)
def find_images_with_bounding_size(dataset: paddle.io.Dataset):
max_length_index = -1
max_width_index = -1
min_length_index = -1
min_width_index = -1
max_length = float("-inf")
max_width = float("-inf")
min_length = float("inf")
min_width = float("inf")
for idx, data in enumerate(dataset):
image = np.array(data[0])
h, w = image.shape[-2:]
if h > max_length:
max_length = h
max_length_index = idx
if w > max_width:
max_width = w
max_width_index = idx
if h < min_length:
min_length = h
min_length_index = idx
if w < min_width:
min_width = w
min_width_index = idx
print(f"Found max image length: {max_length}, index: {max_length_index}")
print(f"Found max image width: {max_width}, index: {max_width_index}")
print(f"Found min image length: {min_length}, index: {min_length_index}")
print(f"Found min image width: {min_width}, index: {min_width_index}")
return paddle.io.Subset(
dataset, [max_width_index, max_length_index, min_width_index, min_length_index]
)
def load_predictor(args):
"""
load predictor func
"""
rerun_flag = False
model_file = os.path.join(args.model_path, args.model_filename)
params_file = os.path.join(args.model_path, args.params_filename)
pred_cfg = PredictConfig(model_file, params_file)
pred_cfg.enable_memory_optim()
pred_cfg.switch_ir_optim(True)
if args.device == "GPU":
pred_cfg.enable_use_gpu(100, 0)
else:
pred_cfg.disable_gpu()
pred_cfg.set_cpu_math_library_num_threads(args.cpu_threads)
if args.use_mkldnn:
pred_cfg.enable_mkldnn()
if args.precision == "int8":
pred_cfg.enable_mkldnn_int8({"conv2d"})
if global_config["model_type"] == "rec":
# delete pass which influence the accuracy, please refer to https://github.com/PaddlePaddle/Paddle/issues/55290
pred_cfg.delete_pass("fc_mkldnn_pass")
pred_cfg.delete_pass("fc_act_mkldnn_fuse_pass")
if args.use_trt:
# To collect the dynamic shapes of inputs for TensorRT engine
dynamic_shape_file = os.path.join(args.model_path, "dynamic_shape.txt")
if os.path.exists(dynamic_shape_file):
pred_cfg.enable_tuned_tensorrt_dynamic_shape(dynamic_shape_file, True)
print("trt set dynamic shape done!")
precision_map = {
"fp16": PrecisionType.Half,
"fp32": PrecisionType.Float32,
"int8": PrecisionType.Int8,
}
if (
args.precision == "int8"
and "ppocrv4_det_server_qat_dist.yaml" in args.config_path
):
# Use the following settings only when the hardware is a Tesla V100. If you are using
# a RTX 3090, use the settings in the else branch.
pred_cfg.enable_tensorrt_engine(
workspace_size=1 << 30,
max_batch_size=1,
min_subgraph_size=30,
precision_mode=precision_map[args.precision],
use_static=True,
use_calib_mode=False,
)
pred_cfg.exp_disable_tensorrt_ops(["elementwise_add"])
else:
pred_cfg.enable_tensorrt_engine(
workspace_size=1 << 30,
max_batch_size=1,
min_subgraph_size=4,
precision_mode=precision_map[args.precision],
use_static=True,
use_calib_mode=False,
)
else:
# pred_cfg.disable_gpu()
# pred_cfg.set_cpu_math_library_num_threads(24)
pred_cfg.collect_shape_range_info(dynamic_shape_file)
print("Start collect dynamic shape...")
rerun_flag = True
predictor = create_predictor(pred_cfg)
return predictor, rerun_flag
def eval(args):
"""
eval mIoU func
"""
# DataLoader need run on cpu
paddle.set_device("cpu")
devices = paddle.device.get_device().split(":")[0]
val_loader = build_dataloader(all_config, "Eval", devices, logger)
post_process_class = build_post_process(all_config["PostProcess"], global_config)
eval_class = build_metric(all_config["Metric"])
model_type = global_config["model_type"]
predictor, rerun_flag = load_predictor(args)
if rerun_flag:
eval_dataset = find_images_with_bounding_size(val_loader.dataset)
batch_sampler = paddle.io.BatchSampler(
eval_dataset, batch_size=1, shuffle=False, drop_last=False
)
val_loader = paddle.io.DataLoader(
eval_dataset, batch_sampler=batch_sampler, num_workers=4, return_list=True
)
input_names = predictor.get_input_names()
input_handle = predictor.get_input_handle(input_names[0])
output_names = predictor.get_output_names()
output_handle = predictor.get_output_handle(output_names[0])
sample_nums = len(val_loader)
predict_time = 0.0
time_min = float("inf")
time_max = float("-inf")
print("Start evaluating ( total_iters: {}).".format(sample_nums))
for batch_id, batch in enumerate(val_loader):
images = np.array(batch[0])
batch_numpy = []
for item in batch:
batch_numpy.append(np.array(item))
# ori_shape = np.array(batch_numpy).shape[-2:]
input_handle.reshape(images.shape)
input_handle.copy_from_cpu(images)
start_time = time.time()
predictor.run()
preds = output_handle.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
if model_type == "det":
preds_map = {"maps": preds}
post_result = post_process_class(preds_map, batch_numpy[1])
eval_class(post_result, batch_numpy)
elif model_type == "rec":
post_result = post_process_class(preds, batch_numpy[1])
eval_class(post_result, batch_numpy)
if rerun_flag:
if batch_id == 3:
print(
"***** Collect dynamic shape done, Please rerun the program to get correct results. *****"
)
return
if batch_id % 100 == 0:
print("Eval iter:", batch_id)
sys.stdout.flush()
metric = eval_class.get_metric()
time_avg = predict_time / sample_nums
print(
"[Benchmark] Inference time(ms): min={}, max={}, avg={}".format(
round(time_min * 1000, 2),
round(time_max * 1000, 1),
round(time_avg * 1000, 1),
)
)
for k, v in metric.items():
print("{}:{}".format(k, v))
sys.stdout.flush()
def main():
global all_config, global_config
all_config = load_slim_config(args.config_path)
global_config = all_config["Global"]
eval(args)
if __name__ == "__main__":
paddle.enable_static()
parser = argparse.ArgumentParser()
parser.add_argument("--model_path", type=str, help="inference model filepath")
parser.add_argument(
"--config_path",
type=str,
default="./configs/ppocrv3_det_qat_dist.yaml",
help="path of compression strategy config.",
)
parser.add_argument(
"--model_filename",
type=str,
default="inference.pdmodel",
help="model file name",
)
parser.add_argument(
"--params_filename",
type=str,
default="inference.pdiparams",
help="params file name",
)
parser.add_argument(
"--device",
type=str,
default="GPU",
choices=["CPU", "GPU"],
help="Choose the device you want to run, it can be: CPU/GPU, default is GPU",
)
parser.add_argument(
"--precision",
type=str,
default="fp32",
choices=["fp32", "fp16", "int8"],
help="The precision of inference. It can be 'fp32', 'fp16' or 'int8'. Default is 'fp16'.",
)
parser.add_argument(
"--use_trt",
type=bool,
default=False,
help="Whether to use tensorrt engine or not.",
)
parser.add_argument(
"--use_mkldnn", type=bool, default=False, help="Whether use mkldnn or not."
)
parser.add_argument(
"--cpu_threads", type=int, default=10, help="Num of cpu threads."
)
args = parser.parse_args()
main()

View File

@@ -0,0 +1,88 @@
#!/bin/bash
# 本脚本用于测试PPOCRV4_det系列模型的自动压缩功能
## 运行脚本前,请确保处于以下环境:
## CUDA11.7+TensorRT8.4.2.4+Paddle2.5.2
model_type="$1"
if [ "$model_type" = "mobile" ]; then
echo "test ppocrv4_det_mobile model......"
## 启动自动化压缩训练
CUDA_VISIBLE_DEVICES=0 python run.py --save_dir ./models/det_mobile_qat --config_path configs/ppocrv4/ppocrv4_det_qat_dist.yaml
## GPU指标测试
### 量化前预期指标hmean:72.71%;time:4.7ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_det_infer --config ./configs/ppocrv4/ppocrv4_det_qat_dist.yaml --precision fp32 --use_trt True
### 量化后预期指标hmean:71.38%;time:3.3ms
python test_ocr.py --model_path ./models/det_mobile_qat --config ./configs/ppocrv4/ppocrv4_det_qat_dist.yaml --precision int8 --use_trt True
## CPU指标测试
### 量化前预期指标hmean:72.71%;time:198.4ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_det_infer --config ./configs/ppocrv4/ppocrv4_det_qat_dist.yaml --precision fp32 --use_mkldnn True --device CPU --cpu_threads 12
### 量化后预期指标hmean:72.30%;time:205.2ms
python test_ocr.py --model_path ./models/det_mobile_qat --config ./configs/ppocrv4/ppocrv4_det_qat_dist.yaml --precision int8 --use_mkldnn True --device CPU --cpu_threads 12
# 量化前模型推理
# GPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_det_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision fp32
# CPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_det_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision fp32
# 量化后模型推理
# GPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/det_mobile_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision int8
# CPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/det_mobile_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision int8
elif [ "$model_type" = "server" ]; then
echo "test ppocrv4_det_server model......"
## 启动自动化压缩训练
CUDA_VISIBLE_DEVICES=0 python run.py --save_dir ./models/det_server_qat --config_path configs/ppocrv4/ppocrv4_det_server_qat_dist.yaml
## GPU指标测试
### 量化前预期指标hmean:79.77%;time:50.0ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_det_server_infer --config ./configs/ppocrv4/ppocrv4_det_server_qat_dist.yaml --precision fp32 --use_trt True
### 量化后预期指标hmean:79.81%;time:42.4ms
python test_ocr.py --model_path ./models/det_server_qat --config ./configs/ppocrv4/ppocrv4_det_server_qat_dist.yaml --precision int8 --use_trt True
## CPU指标测试
### 量化前预期指标hmean:79.77%;time:2159.4ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_det_server_infer --config ./configs/ppocrv4/ppocrv4_det_server_qat_dist.yaml --precision fp32 --use_mkldnn True --device CPU --cpu_threads 12
### 量化后预期指标hmean:79.69%;time:1834.8ms
python test_ocr.py --model_path ./models/det_server_qat --config ./configs/ppocrv4/ppocrv4_det_server_qat_dist.yaml --precision int8 --use_mkldnn True --device CPU --cpu_threads 12
## 量化前模型推理
### GPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_det_server_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision fp32
### CPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_det_server_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision fp32
## 量化后模型推理
### GPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/det_server_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision int8
### CPU
python tools/infer/predict_det.py --det_model_dir deploy/slim/auto_compression/models/det_server_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision int8
else
echo "unrecgnized model_type"
fi

View File

@@ -0,0 +1,88 @@
#!/bin/bash
# 本脚本用于测试PPOCRV4_rec系列模型的自动压缩功能
## 运行脚本前,请确保处于以下环境:
## CUDA11.2+TensorRT8.0.3.4+Paddle2.5.2
model_type="$1"
if [ "$model_type" = "mobile" ]; then
echo "test ppocrv4_rec_mobile model......"
## 启动自动化压缩训练
CUDA_VISIBLE_DEVICES=0 python run.py --save_dir ./models/rec_mobile_qat --config_path configs/ppocrv4/ppocrv4_rec_qat_dist.yaml
## GPU指标测试
### 量化前预期指标accuracy:78.92%;time:1.7ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_rec_infer --config ./configs/ppocrv4/ppocrv4_rec_qat_dist.yaml --precision fp32 --use_trt True
### 量化后预期指标accuracy:78.41%;time:1.4ms
python test_ocr.py --model_path ./models/rec_mobile_qat --config ./configs/ppocrv4/ppocrv4_rec_qat_dist.yaml --precision int8 --use_trt True
## CPU指标测试
### 量化前预期指标accuracy:78.92%;time:33.3ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_rec_infer --config ./configs/ppocrv4/ppocrv4_rec_qat_dist.yaml --precision fp32 --use_mkldnn True --device CPU --cpu_threads 12
### 量化后预期指标accuracy:78.44%;time:34.0ms
python test_ocr.py --model_path ./models/rec_mobile_qat --config ./configs/ppocrv4/ppocrv4_rec_qat_dist.yaml --precision int8 --use_mkldnn True --device CPU --cpu_threads 12
# 量化前模型推理
# GPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_rec_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision fp32
# CPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_rec_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision fp32
# 量化后模型推理
# GPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/rec_mobile_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision int8
# CPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/rec_mobile_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision int8
elif [ "$model_type" = "server" ]; then
echo "test ppocrv4_rec_server model......"
## 启动自动化压缩训练
CUDA_VISIBLE_DEVICES=0 python run.py --save_dir ./models/rec_server_qat --config_path configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml
## GPU指标测试
### 量化前预期指标accuracy:81.62%;time:4.0ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_rec_server_infer --config ./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml --precision fp32 --use_trt True
### 量化后预期指标accuracy:81.03%;time:2.0ms
python test_ocr.py --model_path ./models/rec_server_qat --config ./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml --precision int8 --use_trt True
## CPU指标测试
### 量化前预期指标accuracy:81.62%;time:62.5ms
python test_ocr.py --model_path ./models/ch_PP-OCRv4_rec_server_infer --config ./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml --precision fp32 --use_mkldnn True --device CPU --cpu_threads 12
### 量化后预期指标accuracy:81.00%;time:64.4ms
python test_ocr.py --model_path ./models/rec_server_qat --config ./configs/ppocrv4/ppocrv4_rec_server_qat_dist.yaml --precision int8 --use_mkldnn True --device CPU --cpu_threads 12
## 量化前模型推理
### GPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_rec_server_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision fp32
### CPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/ch_PP-OCRv4_rec_server_infer \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision fp32
## 量化后模型推理
### GPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/rec_server_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu True \
--use_tensorrt True --warmup True --precision int8
### CPU
python tools/infer/predict_det.py --rec_model_dir deploy/slim/auto_compression/models/rec_server_qat \
--benchmark True --image_dir deploy/slim/auto_compression/datasets/v4_4_test_dataset --use_gpu False \
--enable_mkldnn True --warmup True --precision int8
else
echo "unrecgnized model_type"
fi

View File

@@ -0,0 +1,67 @@
# PP-OCR模型裁剪
复杂的模型有利于提高模型的性能,但也导致模型中存在一定冗余,模型裁剪通过移出网络模型中的子模型来减少这种冗余,达到减少模型计算复杂度,提高模型推理性能的目的。
本教程将介绍如何使用飞桨模型压缩库PaddleSlim做PaddleOCR模型的压缩。
[PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim)集成了模型剪枝、量化(包括量化训练和离线量化)、蒸馏和神经网络搜索等多种业界常用且领先的模型压缩功能,如果您感兴趣,可以关注并了解。
在开始本教程之前,建议先了解:
1. [PaddleOCR模型的训练方法](../../../doc/doc_ch/training.md)
2. [模型裁剪教程](https://github.com/PaddlePaddle/PaddleSlim/blob/release%2F2.0.0/docs/zh_cn/tutorials/pruning/dygraph/filter_pruning.md)
## 快速开始
模型裁剪主要包括四个步骤:
1. 安装 PaddleSlim
2. 准备训练好的模型
3. 敏感度分析、裁剪训练
4. 导出模型、预测部署
### 1. 安装PaddleSlim
```bash
git clone https://github.com/PaddlePaddle/PaddleSlim.git
cd PaddleSlim
git checkout develop
python3 setup.py install
```
### 2. 获取预训练模型
模型裁剪需要加载事先训练好的模型PaddleOCR也提供了一系列[模型](../../../doc/doc_ch/models_list.md),开发者可根据需要自行选择模型或使用自己的模型。
### 3. 敏感度分析训练
加载预训练模型后通过对现有模型的每个网络层进行敏感度分析得到敏感度文件sen.pickle可以通过PaddleSlim提供的[接口](https://github.com/PaddlePaddle/PaddleSlim/blob/9b01b195f0c4bc34a1ab434751cb260e13d64d9e/paddleslim/dygraph/prune/filter_pruner.py#L75)加载文件,获得各网络层在不同裁剪比例下的精度损失。从而了解各网络层冗余度,决定每个网络层的裁剪比例。
敏感度文件内容格式:
```
sen.pickle(Dict){
'layer_weight_name_0': sens_of_each_ratio(Dict){'pruning_ratio_0': acc_loss, 'pruning_ratio_1': acc_loss}
'layer_weight_name_1': sens_of_each_ratio(Dict){'pruning_ratio_0': acc_loss, 'pruning_ratio_1': acc_loss}
}
例子:
{
'conv10_expand_weights': {0.1: 0.006509952684312718, 0.2: 0.01827734339798862, 0.3: 0.014528405644659832, 0.6: 0.06536008804270439, 0.8: 0.11798612250664964, 0.7: 0.12391408417493704, 0.4: 0.030615754498018757, 0.5: 0.047105205602406594}
'conv10_linear_weights': {0.1: 0.05113190831455035, 0.2: 0.07705573833558801, 0.3: 0.12096721757739311, 0.6: 0.5135061352930738, 0.8: 0.7908166677143281, 0.7: 0.7272187676899062, 0.4: 0.1819252083008504, 0.5: 0.3728054727792405}
}
```
加载敏感度文件后会返回一个字典字典中的keys为网络模型参数模型的名字values为一个字典里面保存了相应网络层的裁剪敏感度信息。例如在例子中conv10_expand_weights所对应的网络层在裁掉10%的卷积核后模型性能相较原模型会下降0.65%,详细信息可见[PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/algo/algo.md#2-%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%89%AA%E8%A3%81%E5%8E%9F%E7%90%86)
进入PaddleOCR根目录通过以下命令对模型进行敏感度分析训练
```bash
python3 deploy/slim/prune/sensitivity_anal.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model="your trained model" Global.save_model_dir=./output/prune_model/
```
### 4. 导出模型、预测部署
在得到裁剪训练保存的模型后我们可以将其导出为inference_model
```bash
pytho3 deploy/slim/prune/export_prune_model.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model=./output/det_db/best_accuracy Global.save_inference_dir=./prune/prune_inference_model
```
inference model的预测和部署参考
1. [inference model python端预测](../../../doc/doc_ch/inference.md)
2. [inference model C++预测](../../cpp_infer/readme.md)

View File

@@ -0,0 +1,73 @@
# PP-OCR Models Pruning
Generally, a more complex model would achieve better performance in the task, but it also leads to some redundancy in the model. Model Pruning is a technique that reduces this redundancy by removing the sub-models in the neural network model, so as to reduce model calculation complexity and improve model inference performance.
This example uses PaddleSlim provided[APIs of Pruning](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/docs/zh_cn/api_cn/dygraph/pruners) to compress the OCR model.
[PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim), an open source library which integrates model pruning, quantization (including quantization training and offline quantization), distillation, neural network architecture search, and many other commonly used and leading model compression technique in the industry.
It is recommended that you could understand following pages before reading this example
1. [PaddleOCR training methods](../../../doc/doc_ch/quickstart.md)
2. [The demo of prune](https://github.com/PaddlePaddle/PaddleSlim/blob/release%2F2.0.0/docs/zh_cn/tutorials/pruning/dygraph/filter_pruning.md)
## Quick start
Five steps for OCR model prune:
1. Install PaddleSlim
2. Prepare the trained model
3. Sensitivity analysis and tailoring training
4. Export model, predict deployment
### 1. Install PaddleSlim
```bash
git clone https://github.com/PaddlePaddle/PaddleSlim.git
cd PaddleSlim
git checkout develop
python3 setup.py install
```
### 2. Download Pre-trained Model
Model prune needs to load pre-trained models.
PaddleOCR also provides a series of [models](../../../doc/doc_en/models_list_en.md). Developers can choose their own models or use their own models according to their needs.
### 3. Pruning sensitivity analysis
After the pre-trained model is loaded, sensitivity analysis is performed on each network layer of the model to understand the redundancy of each network layer, and save a sensitivity file which named: sen.pickle. After that, user could load the sensitivity file via the [methods provided by PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/prune/sensitive.py#L221) and determining the pruning ratio of each network layer automatically. For specific details of sensitivity analysis, see[Sensitivity analysis](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/en/tutorials/image_classification_sensitivity_analysis_tutorial_en.md)
The data format of sensitivity file
```
sen.pickle(Dict){
'layer_weight_name_0': sens_of_each_ratio(Dict){'pruning_ratio_0': acc_loss, 'pruning_ratio_1': acc_loss}
'layer_weight_name_1': sens_of_each_ratio(Dict){'pruning_ratio_0': acc_loss, 'pruning_ratio_1': acc_loss}
}
example
{
'conv10_expand_weights': {0.1: 0.006509952684312718, 0.2: 0.01827734339798862, 0.3: 0.014528405644659832, 0.6: 0.06536008804270439, 0.8: 0.11798612250664964, 0.7: 0.12391408417493704, 0.4: 0.030615754498018757, 0.5: 0.047105205602406594}
'conv10_linear_weights': {0.1: 0.05113190831455035, 0.2: 0.07705573833558801, 0.3: 0.12096721757739311, 0.6: 0.5135061352930738, 0.8: 0.7908166677143281, 0.7: 0.7272187676899062, 0.4: 0.1819252083008504, 0.5: 0.3728054727792405}
}
The function would return a dict after loading the sensitivity file. The keys of the dict are name of parameters in each layer. And the value of key is the information about pruning sensitivity of corresponding layer. In example, pruning 10% filter of the layer corresponding to conv10_expand_weights would lead to 0.65% degradation of model performance. The details could be seen at: [Sensitivity analysis](https://github.com/PaddlePaddle/PaddleSlim/blob/release/2.0-alpha/docs/zh_cn/algo/algo.md)
```
The function would return a dict after loading the sensitivity file. The keys of the dict are name of parameters in each layer. And the value of key is the information about pruning sensitivity of corresponding layer. In example, pruning 10% filter of the layer corresponding to conv10_expand_weights would lead to 0.65% degradation of model performance. The details could be seen at: [Sensitivity analysis](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/algo/algo.md#2-%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%89%AA%E8%A3%81%E5%8E%9F%E7%90%86)
Enter the PaddleOCR root directoryperform sensitivity analysis on the model with the following command
```bash
python3 deploy/slim/prune/sensitivity_anal.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model="your trained model" Global.save_model_dir=./output/prune_model/
```
### 5. Export inference model and deploy it
We can export the pruned model as inference_model for deployment:
```bash
python deploy/slim/prune/export_prune_model.py -c configs/det/ch_ppocr_v2.0/ch_det_mv3_db_v2.0.yml -o Global.pretrained_model=./output/det_db/best_accuracy Global.save_inference_dir=./prune/prune_inference_model
```
Reference for prediction and deployment of inference model:
1. [inference model python prediction](../../../doc/doc_en/inference_en.md)
2. [inference model C++ prediction](../../cpp_infer/readme_en.md)

View File

@@ -0,0 +1,137 @@
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import sys
__dir__ = os.path.dirname(__file__)
sys.path.append(__dir__)
sys.path.append(os.path.join(__dir__, "..", "..", ".."))
sys.path.append(os.path.join(__dir__, "..", "..", "..", "tools"))
import paddle
from ppocr.data import build_dataloader, set_signal_handlers
from ppocr.modeling.architectures import build_model
from ppocr.postprocess import build_post_process
from ppocr.metrics import build_metric
from ppocr.utils.save_load import load_model
import tools.program as program
def main(config, device, logger, vdl_writer):
global_config = config["Global"]
# build dataloader
set_signal_handlers()
valid_dataloader = build_dataloader(config, "Eval", device, logger)
# build post process
post_process_class = build_post_process(config["PostProcess"], global_config)
# build model
# for rec algorithm
if hasattr(post_process_class, "character"):
char_num = len(getattr(post_process_class, "character"))
config["Architecture"]["Head"]["out_channels"] = char_num
model = build_model(config["Architecture"])
if config["Architecture"]["model_type"] == "det":
input_shape = [1, 3, 640, 640]
elif config["Architecture"]["model_type"] == "rec":
input_shape = [1, 3, 32, 320]
flops = paddle.flops(model, input_shape)
logger.info("FLOPs before pruning: {}".format(flops))
from paddleslim.dygraph import FPGMFilterPruner
model.train()
pruner = FPGMFilterPruner(model, input_shape)
# build metric
eval_class = build_metric(config["Metric"])
def eval_fn():
metric = program.eval(model, valid_dataloader, post_process_class, eval_class)
if config["Architecture"]["model_type"] == "det":
main_indicator = "hmean"
else:
main_indicator = "acc"
logger.info("metric[{}]: {}".format(main_indicator, metric[main_indicator]))
return metric[main_indicator]
params_sensitive = pruner.sensitive(
eval_func=eval_fn,
sen_file="./sen.pickle",
skip_vars=["conv2d_57.w_0", "conv2d_transpose_2.w_0", "conv2d_transpose_3.w_0"],
)
logger.info(
"The sensitivity analysis results of model parameters saved in sen.pickle"
)
# calculate pruned params's ratio
params_sensitive = pruner._get_ratios_by_loss(params_sensitive, loss=0.02)
for key in params_sensitive.keys():
logger.info("{}, {}".format(key, params_sensitive[key]))
plan = pruner.prune_vars(params_sensitive, [0])
flops = paddle.flops(model, input_shape)
logger.info("FLOPs after pruning: {}".format(flops))
# load pretrain model
load_model(config, model)
metric = program.eval(model, valid_dataloader, post_process_class, eval_class)
if config["Architecture"]["model_type"] == "det":
main_indicator = "hmean"
else:
main_indicator = "acc"
logger.info("metric['']: {}".format(main_indicator, metric[main_indicator]))
# start export model
from paddle.jit import to_static
infer_shape = [3, -1, -1]
if config["Architecture"]["model_type"] == "rec":
infer_shape = [3, 32, -1] # for rec model, H must be 32
if (
"Transform" in config["Architecture"]
and config["Architecture"]["Transform"] is not None
and config["Architecture"]["Transform"]["name"] == "TPS"
):
logger.info(
"When there is tps in the network, variable length input is not supported, and the input size needs to be the same as during training"
)
infer_shape[-1] = 100
model = to_static(
model,
input_spec=[
paddle.static.InputSpec(shape=[None] + infer_shape, dtype="float32")
],
)
save_path = "{}/inference".format(config["Global"]["save_inference_dir"])
paddle.jit.save(model, save_path)
logger.info("inference model is saved to {}".format(save_path))
if __name__ == "__main__":
config, device, logger, vdl_writer = program.preprocess(is_train=True)
main(config, device, logger, vdl_writer)

View File

@@ -0,0 +1,200 @@
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import sys
__dir__ = os.path.dirname(__file__)
sys.path.append(__dir__)
sys.path.append(os.path.join(__dir__, "..", "..", ".."))
sys.path.append(os.path.join(__dir__, "..", "..", "..", "tools"))
import paddle
import paddle.distributed as dist
from ppocr.data import build_dataloader, set_signal_handlers
from ppocr.modeling.architectures import build_model
from ppocr.losses import build_loss
from ppocr.optimizer import build_optimizer
from ppocr.postprocess import build_post_process
from ppocr.metrics import build_metric
from ppocr.utils.save_load import load_model
import tools.program as program
dist.get_world_size()
def get_pruned_params(parameters):
params = []
for param in parameters:
if (
len(param.shape) == 4
and "depthwise" not in param.name
and "transpose" not in param.name
and "conv2d_57" not in param.name
and "conv2d_56" not in param.name
):
params.append(param.name)
return params
def main(config, device, logger, vdl_writer):
# init dist environment
if config["Global"]["distributed"]:
dist.init_parallel_env()
global_config = config["Global"]
# build dataloader
set_signal_handlers()
train_dataloader = build_dataloader(config, "Train", device, logger)
if config["Eval"]:
valid_dataloader = build_dataloader(config, "Eval", device, logger)
else:
valid_dataloader = None
# build post process
post_process_class = build_post_process(config["PostProcess"], global_config)
# build model
# for rec algorithm
if hasattr(post_process_class, "character"):
char_num = len(getattr(post_process_class, "character"))
config["Architecture"]["Head"]["out_channels"] = char_num
model = build_model(config["Architecture"])
if config["Architecture"]["model_type"] == "det":
input_shape = [1, 3, 640, 640]
elif config["Architecture"]["model_type"] == "rec":
input_shape = [1, 3, 32, 320]
flops = paddle.flops(model, input_shape)
logger.info("FLOPs before pruning: {}".format(flops))
from paddleslim.dygraph import FPGMFilterPruner
model.train()
pruner = FPGMFilterPruner(model, input_shape)
# build loss
loss_class = build_loss(config["Loss"])
# build optim
optimizer, lr_scheduler = build_optimizer(
config["Optimizer"],
epochs=config["Global"]["epoch_num"],
step_each_epoch=len(train_dataloader),
model=model,
)
# build metric
eval_class = build_metric(config["Metric"])
# load pretrain model
pre_best_model_dict = load_model(config, model, optimizer)
logger.info(
"train dataloader has {} iters, valid dataloader has {} iters".format(
len(train_dataloader), len(valid_dataloader)
)
)
# build metric
eval_class = build_metric(config["Metric"])
logger.info(
"train dataloader has {} iters, valid dataloader has {} iters".format(
len(train_dataloader), len(valid_dataloader)
)
)
def eval_fn():
metric = program.eval(
model, valid_dataloader, post_process_class, eval_class, False
)
if config["Architecture"]["model_type"] == "det":
main_indicator = "hmean"
else:
main_indicator = "acc"
logger.info("metric[{}]: {}".format(main_indicator, metric[main_indicator]))
return metric[main_indicator]
run_sensitive_analysis = False
"""
run_sensitive_analysis=True:
Automatically compute the sensitivities of convolutions in a model.
The sensitivity of a convolution is the losses of accuracy on test dataset in
different pruned ratios. The sensitivities can be used to get a group of best
ratios with some condition.
run_sensitive_analysis=False:
Set prune trim ratio to a fixed value, such as 10%. The larger the value,
the more convolution weights will be cropped.
"""
if run_sensitive_analysis:
params_sensitive = pruner.sensitive(
eval_func=eval_fn,
sen_file="./deploy/slim/prune/sen.pickle",
skip_vars=[
"conv2d_57.w_0",
"conv2d_transpose_2.w_0",
"conv2d_transpose_3.w_0",
],
)
logger.info(
"The sensitivity analysis results of model parameters saved in sen.pickle"
)
# calculate pruned params's ratio
params_sensitive = pruner._get_ratios_by_loss(params_sensitive, loss=0.02)
for key in params_sensitive.keys():
logger.info("{}, {}".format(key, params_sensitive[key]))
else:
params_sensitive = {}
for param in model.parameters():
if "transpose" not in param.name and "linear" not in param.name:
# set prune ratio as 10%. The larger the value, the more convolution weights will be cropped
params_sensitive[param.name] = 0.1
plan = pruner.prune_vars(params_sensitive, [0])
flops = paddle.flops(model, input_shape)
logger.info("FLOPs after pruning: {}".format(flops))
# start train
program.train(
config,
train_dataloader,
valid_dataloader,
device,
model,
loss_class,
optimizer,
lr_scheduler,
post_process_class,
eval_class,
pre_best_model_dict,
logger,
vdl_writer,
)
if __name__ == "__main__":
config, device, logger, vdl_writer = program.preprocess(is_train=True)
main(config, device, logger, vdl_writer)

View File

@@ -0,0 +1,60 @@
# PP-OCR模型量化
复杂的模型有利于提高模型的性能,但也导致模型中存在一定冗余,模型量化将全精度缩减到定点数减少这种冗余,达到减少模型计算复杂度,提高模型推理性能的目的。
模型量化可以在基本不损失模型的精度的情况下将FP32精度的模型参数转换为Int8精度减小模型参数大小并加速计算使用量化后的模型在移动端等部署时更具备速度优势。
本教程将介绍如何使用飞桨模型压缩库PaddleSlim做PaddleOCR模型的压缩。
[PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim) 集成了模型剪枝、量化(包括量化训练和离线量化)、蒸馏和神经网络搜索等多种业界常用且领先的模型压缩功能,如果您感兴趣,可以关注并了解。
在开始本教程之前,建议先了解[PaddleOCR模型的训练方法](../../../doc/doc_ch/training.md)以及[PaddleSlim](https://paddleslim.readthedocs.io/zh_CN/latest/index.html)
## 快速开始
量化多适用于轻量模型在移动端的部署,当训练出一个模型后,如果希望进一步的压缩模型大小并加速预测,可使用量化的方法压缩模型。
模型量化主要包括五个步骤:
1. 安装 PaddleSlim
2. 准备训练好的模型
3. 量化训练
4. 导出量化推理模型
5. 量化模型预测部署
### 1. 安装PaddleSlim
```bash
pip3 install paddleslim==2.3.2
```
### 2. 准备训练好的模型
PaddleOCR提供了一系列训练好的[模型](../../../doc/doc_ch/models_list.md),如果待量化的模型不在列表中,需要按照[常规训练](../../../doc/doc_ch/quickstart.md)方法得到训练好的模型。
### 3. 量化训练
量化训练包括离线量化训练和在线量化训练,在线量化训练效果更好,需加载预训练模型,在定义好量化策略后即可对模型进行量化。
量化训练的代码位于slim/quantization/quant.py 中比如训练检测模型以PPOCRv3检测模型为例训练指令如下
```
# 下载检测预训练模型:
wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_distill_train.tar
tar xf ch_PP-OCRv3_det_distill_train.tar
python deploy/slim/quantization/quant.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model='./ch_PP-OCRv3_det_distill_train/best_accuracy' Global.save_model_dir=./output/quant_model_distill/
```
如果要训练识别模型的量化,修改配置文件和加载的模型参数即可。
### 4. 导出模型
在得到量化训练保存的模型后我们可以将其导出为inference_model用于预测部署
```bash
python deploy/slim/quantization/export_model.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.checkpoints=output/quant_model/best_accuracy Global.save_inference_dir=./output/quant_inference_model
```
### 5. 量化模型部署
上述步骤导出的量化模型参数精度仍然是FP32但是参数的数值范围是int8导出的模型可以通过PaddleLite的opt模型转换工具完成模型转换。
量化模型移动端部署的可参考 [移动端模型部署](../../lite/readme.md)
备注量化训练后的模型参数是float32类型转inference model预测时相对不量化无加速效果原因是量化后模型结构之间存在量化和反量化算子如果要使用量化模型部署建议使用TensorRT并设置precision为INT8加速量化模型的预测时间。

View File

@@ -0,0 +1,65 @@
# PP-OCR Models Quantization
Generally, a more complex model would achieve better performance in the task, but it also leads to some redundancy in the model.
Quantization is a technique that reduces this redundancy by reducing the full precision data to a fixed number,
so as to reduce model calculation complexity and improve model inference performance.
This example uses PaddleSlim provided [APIs of Quantization](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/dygraph/quanter/qat.rst) to compress the OCR model.
It is recommended that you could understand following pages before reading this example
- [The training strategy of OCR model](../../../doc/doc_en/quickstart_en.md)
- [PaddleSlim Document](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/dygraph/quanter/qat.rst)
## Quick Start
Quantization is mostly suitable for the deployment of lightweight models on mobile terminals.
After training, if you want to further compress the model size and accelerate the prediction, you can use quantization methods to compress the model according to the following steps.
1. Install PaddleSlim
2. Prepare trained model
3. Quantization-Aware Training
4. Export inference model
5. Deploy quantization inference model
### 1. Install PaddleSlim
```bash
pip3 install paddleslim==2.3.2
```
### 2. Download Pre-trained Model
PaddleOCR provides a series of pre-trained [models](../../../doc/doc_en/models_list_en.md).
If the model to be quantified is not in the list, you need to follow the [Regular Training](../../../doc/doc_en/quickstart_en.md) method to get the trained model.
### 3. Quant-Aware Training
Quantization training includes offline quantization training and online quantization training.
Online quantization training is more effective. It is necessary to load the pre-trained model.
After the quantization strategy is defined, the model can be quantified.
The code for quantization training is located in `slim/quantization/quant.py`. For example, the training instructions of slim PPOCRv3 detection model are as follows:
```
# download provided model
wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_distill_train.tar
tar xf ch_PP-OCRv3_det_distill_train.tar
python deploy/slim/quantization/quant.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model='./ch_PP-OCRv3_det_distill_train/best_accuracy' Global.save_model_dir=./output/quant_model_distill/
```
If you want to quantify the text recognition model, you can modify the configuration file and loaded model parameters.
### 4. Export inference model
Once we got the model after pruning and fine-tuning, we can export it as an inference model for the deployment of predictive tasks:
```bash
python deploy/slim/quantization/export_model.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.checkpoints=output/quant_model/best_accuracy Global.save_inference_dir=./output/quant_inference_model
```
### 5. Deploy
The numerical range of the quantized model parameters derived from the above steps is still FP32, but the numerical range of the parameters is int8.
The derived model can be converted through the `opt tool` of PaddleLite.
For quantitative model deployment, please refer to [Mobile terminal model deployment](../../lite/readme.md)

View File

@@ -0,0 +1,189 @@
# Copyright (c) 2020 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
__dir__ = os.path.dirname(os.path.abspath(__file__))
sys.path.append(__dir__)
sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "..", "..", "..")))
sys.path.insert(0, os.path.abspath(os.path.join(__dir__, "..", "..", "..", "tools")))
import argparse
import paddle
from paddle.jit import to_static
from ppocr.modeling.architectures import build_model
from ppocr.postprocess import build_post_process
from ppocr.utils.save_load import load_model
from ppocr.utils.logging import get_logger
from tools.program import load_config, merge_config, ArgsParser
from ppocr.metrics import build_metric
import tools.program as program
from paddleslim.dygraph.quant import QAT
from ppocr.data import build_dataloader, set_signal_handlers
from ppocr.utils.export_model import export_single_model
def main():
############################################################################################################
# 1. quantization configs
############################################################################################################
quant_config = {
# weight preprocess type, default is None and no preprocessing is performed.
"weight_preprocess_type": None,
# activation preprocess type, default is None and no preprocessing is performed.
"activation_preprocess_type": None,
# weight quantize type, default is 'channel_wise_abs_max'
"weight_quantize_type": "channel_wise_abs_max",
# activation quantize type, default is 'moving_average_abs_max'
"activation_quantize_type": "moving_average_abs_max",
# weight quantize bit num, default is 8
"weight_bits": 8,
# activation quantize bit num, default is 8
"activation_bits": 8,
# data type after quantization, such as 'uint8', 'int8', etc. default is 'int8'
"dtype": "int8",
# window size for 'range_abs_max' quantization. default is 10000
"window_size": 10000,
# The decay coefficient of moving average, default is 0.9
"moving_rate": 0.9,
# for dygraph quantization, layers of type in quantizable_layer_type will be quantized
"quantizable_layer_type": ["Conv2D", "Linear"],
}
FLAGS = ArgsParser().parse_args()
config = load_config(FLAGS.config)
config = merge_config(config, FLAGS.opt)
logger = get_logger()
# build post process
post_process_class = build_post_process(config["PostProcess"], config["Global"])
# build model
if hasattr(post_process_class, "character"):
char_num = len(getattr(post_process_class, "character"))
if config["Architecture"]["algorithm"] in [
"Distillation",
]: # distillation model
for key in config["Architecture"]["Models"]:
if (
config["Architecture"]["Models"][key]["Head"]["name"] == "MultiHead"
): # for multi head
if config["PostProcess"]["name"] == "DistillationSARLabelDecode":
char_num = char_num - 2
# update SARLoss params
assert (
list(config["Loss"]["loss_config_list"][-1].keys())[0]
== "DistillationSARLoss"
)
config["Loss"]["loss_config_list"][-1]["DistillationSARLoss"][
"ignore_index"
] = (char_num + 1)
out_channels_list = {}
out_channels_list["CTCLabelDecode"] = char_num
out_channels_list["SARLabelDecode"] = char_num + 2
config["Architecture"]["Models"][key]["Head"][
"out_channels_list"
] = out_channels_list
else:
config["Architecture"]["Models"][key]["Head"][
"out_channels"
] = char_num
elif config["Architecture"]["Head"]["name"] == "MultiHead": # for multi head
if config["PostProcess"]["name"] == "SARLabelDecode":
char_num = char_num - 2
# update SARLoss params
assert list(config["Loss"]["loss_config_list"][1].keys())[0] == "SARLoss"
if config["Loss"]["loss_config_list"][1]["SARLoss"] is None:
config["Loss"]["loss_config_list"][1]["SARLoss"] = {
"ignore_index": char_num + 1
}
else:
config["Loss"]["loss_config_list"][1]["SARLoss"]["ignore_index"] = (
char_num + 1
)
out_channels_list = {}
out_channels_list["CTCLabelDecode"] = char_num
out_channels_list["SARLabelDecode"] = char_num + 2
config["Architecture"]["Head"]["out_channels_list"] = out_channels_list
else: # base rec model
config["Architecture"]["Head"]["out_channels"] = char_num
if config["PostProcess"]["name"] == "SARLabelDecode": # for SAR model
config["Loss"]["ignore_index"] = char_num - 1
model = build_model(config["Architecture"])
# get QAT model
quanter = QAT(config=quant_config)
quanter.quantize(model)
load_model(config, model)
# build metric
eval_class = build_metric(config["Metric"])
# build dataloader
set_signal_handlers()
valid_dataloader = build_dataloader(config, "Eval", device, logger)
use_srn = config["Architecture"]["algorithm"] == "SRN"
model_type = config["Architecture"].get("model_type", None)
# start eval
metric = program.eval(
model, valid_dataloader, post_process_class, eval_class, model_type, use_srn
)
model.eval()
logger.info("metric eval ***************")
for k, v in metric.items():
logger.info("{}:{}".format(k, v))
save_path = config["Global"]["save_inference_dir"]
arch_config = config["Architecture"]
if (
arch_config["algorithm"] == "SVTR"
and arch_config["Head"]["name"] != "MultiHead"
):
input_shape = config["Eval"]["dataset"]["transforms"][-2]["SVTRRecResizeImg"][
"image_shape"
]
else:
input_shape = None
if arch_config["algorithm"] in [
"Distillation",
]: # distillation model
archs = list(arch_config["Models"].values())
for idx, name in enumerate(model.model_name_list):
sub_model_save_path = os.path.join(save_path, name, "inference")
export_single_model(
model.model_list[idx],
archs[idx],
sub_model_save_path,
logger,
input_shape,
quanter,
)
else:
save_path = os.path.join(save_path, "inference")
export_single_model(model, arch_config, save_path, logger, input_shape, quanter)
if __name__ == "__main__":
config, device, logger, vdl_writer = program.preprocess()
main()

226
deploy/slim/quantization/quant.py Executable file
View File

@@ -0,0 +1,226 @@
# Copyright (c) 2020 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.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import sys
__dir__ = os.path.dirname(os.path.abspath(__file__))
sys.path.append(__dir__)
sys.path.append(os.path.abspath(os.path.join(__dir__, "..", "..", "..")))
sys.path.append(os.path.abspath(os.path.join(__dir__, "..", "..", "..", "tools")))
import yaml
import paddle
import paddle.distributed as dist
paddle.seed(2)
from ppocr.data import build_dataloader, set_signal_handlers
from ppocr.modeling.architectures import build_model
from ppocr.losses import build_loss
from ppocr.optimizer import build_optimizer
from ppocr.postprocess import build_post_process
from ppocr.metrics import build_metric
from ppocr.utils.save_load import load_model
import tools.program as program
from paddleslim.dygraph.quant import QAT
dist.get_world_size()
class PACT(paddle.nn.Layer):
def __init__(self):
super(PACT, self).__init__()
alpha_attr = paddle.ParamAttr(
name=self.full_name() + ".pact",
initializer=paddle.nn.initializer.Constant(value=20),
learning_rate=1.0,
regularizer=paddle.regularizer.L2Decay(2e-5),
)
self.alpha = self.create_parameter(shape=[1], attr=alpha_attr, dtype="float32")
def forward(self, x):
out_left = paddle.nn.functional.relu(x - self.alpha)
out_right = paddle.nn.functional.relu(-self.alpha - x)
x = x - out_left + out_right
return x
quant_config = {
# weight preprocess type, default is None and no preprocessing is performed.
"weight_preprocess_type": None,
# activation preprocess type, default is None and no preprocessing is performed.
"activation_preprocess_type": None,
# weight quantize type, default is 'channel_wise_abs_max'
"weight_quantize_type": "channel_wise_abs_max",
# activation quantize type, default is 'moving_average_abs_max'
"activation_quantize_type": "moving_average_abs_max",
# weight quantize bit num, default is 8
"weight_bits": 8,
# activation quantize bit num, default is 8
"activation_bits": 8,
# data type after quantization, such as 'uint8', 'int8', etc. default is 'int8'
"dtype": "int8",
# window size for 'range_abs_max' quantization. default is 10000
"window_size": 10000,
# The decay coefficient of moving average, default is 0.9
"moving_rate": 0.9,
# for dygraph quantization, layers of type in quantizable_layer_type will be quantized
"quantizable_layer_type": ["Conv2D", "Linear"],
}
def main(config, device, logger, vdl_writer):
# init dist environment
if config["Global"]["distributed"]:
dist.init_parallel_env()
global_config = config["Global"]
# build dataloader
set_signal_handlers()
train_dataloader = build_dataloader(config, "Train", device, logger)
if config["Eval"]:
valid_dataloader = build_dataloader(config, "Eval", device, logger)
else:
valid_dataloader = None
# build post process
post_process_class = build_post_process(config["PostProcess"], global_config)
# build model
# for rec algorithm
if hasattr(post_process_class, "character"):
char_num = len(getattr(post_process_class, "character"))
if config["Architecture"]["algorithm"] in [
"Distillation",
]: # distillation model
for key in config["Architecture"]["Models"]:
if (
config["Architecture"]["Models"][key]["Head"]["name"] == "MultiHead"
): # for multi head
if config["PostProcess"]["name"] == "DistillationSARLabelDecode":
char_num = char_num - 2
# update SARLoss params
assert (
list(config["Loss"]["loss_config_list"][-1].keys())[0]
== "DistillationSARLoss"
)
config["Loss"]["loss_config_list"][-1]["DistillationSARLoss"][
"ignore_index"
] = (char_num + 1)
out_channels_list = {}
out_channels_list["CTCLabelDecode"] = char_num
out_channels_list["SARLabelDecode"] = char_num + 2
config["Architecture"]["Models"][key]["Head"][
"out_channels_list"
] = out_channels_list
else:
config["Architecture"]["Models"][key]["Head"][
"out_channels"
] = char_num
elif config["Architecture"]["Head"]["name"] == "MultiHead": # for multi head
if config["PostProcess"]["name"] == "SARLabelDecode":
char_num = char_num - 2
# update SARLoss params
assert list(config["Loss"]["loss_config_list"][1].keys())[0] == "SARLoss"
if config["Loss"]["loss_config_list"][1]["SARLoss"] is None:
config["Loss"]["loss_config_list"][1]["SARLoss"] = {
"ignore_index": char_num + 1
}
else:
config["Loss"]["loss_config_list"][1]["SARLoss"]["ignore_index"] = (
char_num + 1
)
out_channels_list = {}
out_channels_list["CTCLabelDecode"] = char_num
out_channels_list["SARLabelDecode"] = char_num + 2
config["Architecture"]["Head"]["out_channels_list"] = out_channels_list
else: # base rec model
config["Architecture"]["Head"]["out_channels"] = char_num
if config["PostProcess"]["name"] == "SARLabelDecode": # for SAR model
config["Loss"]["ignore_index"] = char_num - 1
model = build_model(config["Architecture"])
pre_best_model_dict = dict()
# load fp32 model to begin quantization
pre_best_model_dict = load_model(
config, model, None, config["Architecture"]["model_type"]
)
freeze_params = False
if config["Architecture"]["algorithm"] in ["Distillation"]:
for key in config["Architecture"]["Models"]:
freeze_params = freeze_params or config["Architecture"]["Models"][key].get(
"freeze_params", False
)
act = None if freeze_params else PACT
quanter = QAT(config=quant_config, act_preprocess=act)
quanter.quantize(model)
if config["Global"]["distributed"]:
model = paddle.DataParallel(model)
# build loss
loss_class = build_loss(config["Loss"])
# build optim
optimizer, lr_scheduler = build_optimizer(
config["Optimizer"],
epochs=config["Global"]["epoch_num"],
step_each_epoch=len(train_dataloader),
model=model,
)
# resume PACT training process
pre_best_model_dict = load_model(
config, model, optimizer, config["Architecture"]["model_type"]
)
# build metric
eval_class = build_metric(config["Metric"])
logger.info(
"train dataloader has {} iters, valid dataloader has {} iters".format(
len(train_dataloader), len(valid_dataloader)
)
)
# start train
program.train(
config,
train_dataloader,
valid_dataloader,
device,
model,
loss_class,
optimizer,
lr_scheduler,
post_process_class,
eval_class,
pre_best_model_dict,
logger,
vdl_writer,
)
if __name__ == "__main__":
config, device, logger, vdl_writer = program.preprocess(is_train=True)
main(config, device, logger, vdl_writer)

View File

@@ -0,0 +1,172 @@
# Copyright (c) 2020 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.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import sys
__dir__ = os.path.dirname(os.path.abspath(__file__))
sys.path.append(__dir__)
sys.path.append(os.path.abspath(os.path.join(__dir__, "..", "..", "..")))
sys.path.append(os.path.abspath(os.path.join(__dir__, "..", "..", "..", "tools")))
import yaml
import paddle
import paddle.distributed as dist
paddle.seed(2)
from ppocr.data import build_dataloader, set_signal_handlers
from ppocr.modeling.architectures import build_model
from ppocr.losses import build_loss
from ppocr.optimizer import build_optimizer
from ppocr.postprocess import build_post_process
from ppocr.metrics import build_metric
from ppocr.utils.save_load import load_model
import tools.program as program
import paddleslim
from paddleslim.dygraph.quant import QAT
import numpy as np
dist.get_world_size()
class PACT(paddle.nn.Layer):
def __init__(self):
super(PACT, self).__init__()
alpha_attr = paddle.ParamAttr(
name=self.full_name() + ".pact",
initializer=paddle.nn.initializer.Constant(value=20),
learning_rate=1.0,
regularizer=paddle.regularizer.L2Decay(2e-5),
)
self.alpha = self.create_parameter(shape=[1], attr=alpha_attr, dtype="float32")
def forward(self, x):
out_left = paddle.nn.functional.relu(x - self.alpha)
out_right = paddle.nn.functional.relu(-self.alpha - x)
x = x - out_left + out_right
return x
quant_config = {
# weight preprocess type, default is None and no preprocessing is performed.
"weight_preprocess_type": None,
# activation preprocess type, default is None and no preprocessing is performed.
"activation_preprocess_type": None,
# weight quantize type, default is 'channel_wise_abs_max'
"weight_quantize_type": "channel_wise_abs_max",
# activation quantize type, default is 'moving_average_abs_max'
"activation_quantize_type": "moving_average_abs_max",
# weight quantize bit num, default is 8
"weight_bits": 8,
# activation quantize bit num, default is 8
"activation_bits": 8,
# data type after quantization, such as 'uint8', 'int8', etc. default is 'int8'
"dtype": "int8",
# window size for 'range_abs_max' quantization. default is 10000
"window_size": 10000,
# The decay coefficient of moving average, default is 0.9
"moving_rate": 0.9,
# for dygraph quantization, layers of type in quantizable_layer_type will be quantized
"quantizable_layer_type": ["Conv2D", "Linear"],
}
def sample_generator(loader):
def __reader__():
for indx, data in enumerate(loader):
images = np.array(data[0])
yield images
return __reader__
def sample_generator_layoutxlm_ser(loader):
def __reader__():
for indx, data in enumerate(loader):
input_ids = np.array(data[0])
bbox = np.array(data[1])
attention_mask = np.array(data[2])
token_type_ids = np.array(data[3])
images = np.array(data[4])
yield [input_ids, bbox, attention_mask, token_type_ids, images]
return __reader__
def main(config, device, logger, vdl_writer):
# init dist environment
if config["Global"]["distributed"]:
dist.init_parallel_env()
global_config = config["Global"]
# build dataloader
set_signal_handlers()
config["Train"]["loader"]["num_workers"] = 0
is_layoutxlm_ser = (
config["Architecture"]["model_type"] == "kie"
and config["Architecture"]["Backbone"]["name"] == "LayoutXLMForSer"
)
train_dataloader = build_dataloader(config, "Train", device, logger)
if config["Eval"]:
config["Eval"]["loader"]["num_workers"] = 0
valid_dataloader = build_dataloader(config, "Eval", device, logger)
if is_layoutxlm_ser:
train_dataloader = valid_dataloader
else:
valid_dataloader = None
paddle.enable_static()
exe = paddle.static.Executor(device)
if "inference_model" in global_config.keys(): # , 'inference_model'):
inference_model_dir = global_config["inference_model"]
else:
inference_model_dir = os.path.dirname(global_config["pretrained_model"])
if not (
os.path.exists(os.path.join(inference_model_dir, "inference.pdmodel"))
and os.path.exists(os.path.join(inference_model_dir, "inference.pdiparams"))
):
raise ValueError(
"Please set inference model dir in Global.inference_model or Global.pretrained_model for post-quantization"
)
if is_layoutxlm_ser:
generator = sample_generator_layoutxlm_ser(train_dataloader)
else:
generator = sample_generator(train_dataloader)
paddleslim.quant.quant_post_static(
executor=exe,
model_dir=inference_model_dir,
model_filename="inference.pdmodel",
params_filename="inference.pdiparams",
quantize_model_path=global_config["save_inference_dir"],
sample_generator=generator,
save_model_filename="inference.pdmodel",
save_params_filename="inference.pdiparams",
batch_size=1,
batch_nums=None,
)
if __name__ == "__main__":
config, device, logger, vdl_writer = program.preprocess(is_train=True)
main(config, device, logger, vdl_writer)