赞
踩
1、下载PP-OCRv3
https://github.com/PaddlePaddle/PaddleOCR
2、将paddle模型转换成onnx模型
(创建虚拟环境):paddle
base环境(使用onnx-sim进行简化)
(1) 转onnx命令(paddle环境):
- paddle2onnx --model_dir ./paddle_infere/
- --model_filename inference.pdmodel
- --params_filename inference.pdiparams
- --save_file ./rec_v3.onnx
- --opset_version 11
(2)利用onnx-sim对onnx模型进行简化和优化(base环境)
命令:
- #简化
- python3 -m onnxsim ./rec_v3.onnx ./rec_v3-sim.onnx --overwrite-input-shape 1,3,48,320
-
- # 优化
- python3 -m onnxoptimizer rec_v3-sim.onnx rec_v3-sim-opt.onnx
3、将onnx模型转为ncnn模型
a、下载ncnn https://github.com/Tencent/ncnn
本例中下载的版本为:ncnn-20221128-windows-vs2015
b、将onnx转为ncnn模型
命令:
- # 转换为ncnn
- onnx2ncnn.exe ./rec_v3-sim-opt.onnx ./rec_v3-sim-opt.param ./rec_v3-sim-opt.bin
-
- # 对ncnn进行优化
- ncnnoptimize.exe rec_v3-sim-opt.param rec_v3-sim-opt.bin rec_v3-sim-new-opt.param rec_v3-sim-new-opt.bin 0 (其中 0=float32, 1=float16)
onnx转ncnn模型运行结果:(部分op操作不支持)两个attention模块有个5d的reshpe导致出现了问题
ncnn进行优化运行结果(不是paddleocrv3 官方识别模型的截图)
4、对模型进行修改不支持的操作
(如需下述中的g步骤,请先进行g步骤在修改其他操作)
(1)打开.param文件
(2)修改不支持的操作
a、两个reshape操作, ncnn不支持5d转换 (共2个),ncnn的reshape和permute目前已经支持4d操作
- 操作 层名 输入数量 输出数量 输入变量名 输出变量名
-
- # 改之前
- Reshape p2o.Reshape.87 1 1 p2o.Add.89 reshape2_0.tmp_0 0=120 1=3 2=-1
- Permute p2o.Transpose.1 1 1 reshape2_0.tmp_0 transpose_1.tmp_0
-
- Reshape p2o.Reshape.93 1 1 p2o.Add.109 reshape2_2.tmp_0 0=120 1=3 2=-1
- Permute p2o.Transpose.4 1 1 reshape2_2.tmp_0 transpose_4.tmp_0
-
- # 改之后
- Reshape p2o.Reshape.87 1 1 p2o.Add.89 reshape2_0.tmp_0 0=15 1=8 2=-1 11=3
- Permute p2o.Transpose.1 1 1 reshape2_0.tmp_0 transpose_1.tmp_0 0=8
-
- Reshape p2o.Reshape.93 1 1 p2o.Add.109 reshape2_2.tmp_0 0=15 1=8 2=-1 11=3
- Permute p2o.Transpose.4 1 1 reshape2_2.tmp_0 transpose_4.tmp_0 0=8
-
- 注解: 其中reshape: 0:宽(w) 1:高(h) 2:通道(c) 11:depth (ncnn框架中)

b、ncnn的squeeze不支持4d操作, 将squeeze 改为reshape即可。(总共6个)
- # 修改之前
- Squeeze p2o.Squeeze.0 1 1 p2o.Slice.3 transpose_1.tmp_0_slice_0 -23303=1,0
- Squeeze p2o.Squeeze.1 1 1 p2o.Slice.5 transpose_1.tmp_0_slice_1 -23303=1,0
- Squeeze p2o.Squeeze.2 1 1 p2o.Slice.7 transpose_1.tmp_0_slice_2 -23303=1,0
-
- # 修改之后
- Reshape p2o.Squeeze.0 1 1 p2o.Slice.3 transpose_1.tmp_0_slice_0 0=15 1=-1 2=8
- Reshape p2o.Squeeze.1 1 1 p2o.Slice.5 transpose_1.tmp_0_slice_1 0=15 1=-1 2=8
- Reshape p2o.Squeeze.2 1 1 p2o.Slice.7 transpose_1.tmp_0_slice_2 0=15 1=-1 2=8
-
-
- # 修改之前
- Squeeze p2o.Squeeze.3 1 1 p2o.Slice.11 transpose_4.tmp_0_slice_0 -23303=1,0
- Squeeze p2o.Squeeze.4 1 1 p2o.Slice.13 transpose_4.tmp_0_slice_1 -23303=1,0
- Squeeze p2o.Squeeze.5 1 1 p2o.Slice.15 transpose_4.tmp_0_slice_2 -23303=1,0
-
- # 修改之后
- Reshape p2o.Squeeze.3 1 1 p2o.Slice.11 transpose_4.tmp_0_slice_0 0=15 1=-1 2=8
- Reshape p2o.Squeeze.4 1 1 p2o.Slice.13 transpose_4.tmp_0_slice_1 0=15 1=-1 2=8
- Reshape p2o.Squeeze.5 1 1 p2o.Slice.15 transpose_4.tmp_0_slice_2 0=15 1=-1 2=8

c、将把attention中的Gemm修改为MatMul即可(总共4个)
- # 修改前
- Gemm p2o.MatMul.2 2 1 p2o.Mul.9 transpose_2.tmp_0 p2o.MatMul.3
- Gemm p2o.MatMul.4 2 1 softmax_0.tmp_0 transpose_1.tmp_0_slice_2 p2o.MatMul.5
-
- # 修改后
- MatMul p2o.MatMul.2 2 1 p2o.Mul.9 transpose_2.tmp_0 p2o.MatMul.3
- MatMul p2o.MatMul.4 2 1 softmax_0.tmp_0 transpose_1.tmp_0_slice_2 p2o.MatMul.5
-
-
- # 修改前
- Gemm p2o.MatMul.14 2 1 p2o.Mul.18 transpose_5.tmp_0 p2o.MatMul.15
- Gemm p2o.MatMul.16 2 1 softmax_1.tmp_0 transpose_4.tmp_0_slice_2 p2o.MatMul.17
-
- # 修改后
- MatMul p2o.MatMul.14 2 1 p2o.Mul.18 transpose_5.tmp_0 p2o.MatMul.15
- MatMul p2o.MatMul.16 2 1 softmax_1.tmp_0 transpose_4.tmp_0_slice_2 p2o.MatMul.17

d、修改slice操作 (共6个)
- # 修改前
- Crop p2o.Slice.2 1 1 transpose_1.tmp_0_splitncnn_2 p2o.Slice.3 -23309=0 -23310=0
- Crop p2o.Slice.4 1 1 transpose_1.tmp_0_splitncnn_1 p2o.Slice.5 -23309=0 -23310=0
- Crop p2o.Slice.6 1 1 transpose_1.tmp_0_splitncnn_0 p2o.Slice.7 -23309=0 -23310=0
-
- # 修改之后
- Crop p2o.Slice.2 1 1 transpose_1.tmp_0_splitncnn_2 p2o.Slice.3 -23309=1,0 -23310=1,1 -23311=1,0
- Crop p2o.Slice.4 1 1 transpose_1.tmp_0_splitncnn_1 p2o.Slice.5 -23309=1,1 -23310=1,2 -23311=1,0
- Crop p2o.Slice.6 1 1 transpose_1.tmp_0_splitncnn_0 p2o.Slice.7 -23309=1,2 -23310=1,3 -23311=1,0
-
- # 修改之前
- Crop p2o.Slice.10 1 1 transpose_4.tmp_0_splitncnn_2 p2o.Slice.11 -23309=0 -23310=0
- Crop p2o.Slice.12 1 1 transpose_4.tmp_0_splitncnn_1 p2o.Slice.13 -23309=0 -23310=0
- Crop p2o.Slice.14 1 1 transpose_4.tmp_0_splitncnn_0 p2o.Slice.15 -23309=0 -23310=0
-
- # 修改之后
- Crop p2o.Slice.10 1 1 transpose_4.tmp_0_splitncnn_2 p2o.Slice.11 -23309=1,0 -23310=1,1 -23311=1,0
- Crop p2o.Slice.12 1 1 transpose_4.tmp_0_splitncnn_1 p2o.Slice.13 -23309=1,1 -23310=1,2 -23311=1,0
- Crop p2o.Slice.14 1 1 transpose_4.tmp_0_splitncnn_0 p2o.Slice.15 -23309=1,2 -23310=1,3 -23311=1,0
-
-
- # 注释: 09:satrts 10:ends 11:axis (onnx中的satrts ends axis steps)

e、修改识别头CTC
交换Squeeze 和 Transpose(onnx:图)
.param(图)
交换前:
交换后:
- # 交换前
- Squeeze p2o.Squeeze.6 1 1 swish_20.tmp_0 squeeze_0.tmp_0 -23300=1,1
- Permute p2o.Transpose.8 1 1 squeeze_0.tmp_0 transpose_8.tmp_0 0=1
-
-
- # 交换后
- Permute p2o.Transpose.8 1 1 swish_20.tmp_0 squeeze_0.tmp_0 0=3
- Squeeze p2o.Squeeze.6 1 1 squeeze_0.tmp_0 transpose_8.tmp_0 -23303=1,0
f、修改最后一层softmax层
- # 修改前
- Softmax p2o.Softmax.2 1 1 p2o.Add.129 softmax_2.tmp_0 0=1 1=1
-
- # 修改后
- Softmax p2o.Softmax.2 1 1 p2o.Add.129 softmax_2.tmp_0 0=-1 1=1
-
至此修改完成后,对模型进行测试(模型精度有损失)
g、将split、BinaryOp、sigmoid替换成Swish操作(共7处)(经测试:不替换也没问题,不影响精度)
(上图206、229 不是paddleocrv3官方模型转换出来,已经进行了网络修改,需要根据自己转换后得到的layer count为准)
替换后:
- (1)
- # 替换前
- Split splitncnn_3 1 2 conv2d_309.tmp_0 conv2d_309.tmp_0_splitncnn_0 conv2d_309.tmp_0_splitncnn_1
- BinaryOp p2o.Mul.0 2 1 conv2d_309.tmp_0_splitncnn_1 p2o.helper.constant.42_splitncnn_6 p2o.Mul.1 0=2
- Sigmoid p2o.Sigmoid.0 1 1 p2o.Mul.1 p2o.Sigmoid.1
- BinaryOp p2o.Mul.2 2 1 conv2d_309.tmp_0_splitncnn_0 p2o.Sigmoid.1 swish_14.tmp_0 0=2
-
- # 替换后
- Swish p2o.Mul.0 1 1 conv2d_309.tmp_0 swish_14.tmp_0
-
- 注意: 最初时 layer count 为206层, 我们将4个操作换成了1个操作,减少了(4-1)3个操作,因此将param文件中的layer count 改为 203层
-
- (2)
- # 替换前
- Split splitncnn_4 1 2 conv2d_310.tmp_0 conv2d_310.tmp_0_splitncnn_0 conv2d_310.tmp_0_splitncnn_1
- BinaryOp p2o.Mul.3 2 1 conv2d_310.tmp_0_splitncnn_1 p2o.helper.constant.42_splitncnn_5 p2o.Mul.4 0=2
- Sigmoid p2o.Sigmoid.2 1 1 p2o.Mul.4 p2o.Sigmoid.3
- BinaryOp p2o.Mul.5 2 1 conv2d_310.tmp_0_splitncnn_0 p2o.Sigmoid.3 swish_15.tmp_0 0=2
-
- # 替换后
- Swish p2o.Mul.1 1 1 conv2d_310.tmp_0 swish_15.tmp_0
-
- 注意:将4个操作换成了一个操作,减少了(4-1)3个操作,因此将 layer count 203 改为layer count 200
-
-
- 将7处替换完成后,运行
- ncnnoptimize.exe rec_v3-sim-opt.param rec_v3-sim-opt.bin rec_v3-sim-new-opt.param rec_v3-sim-new-opt.bin 0
-
- ncnnoptimize 工具,自动将无用的 MemoryData 删除,并且会自动帮你将最终的 blob count 设置为合适的数量, 所以前面步骤中不需要你自己改 blob count,也不用担心多出来的 MemoryData,都会帮你优化掉

参考链接:
ncnn部署PP-OCRv3之onnx篇 - 知乎 (zhihu.com)
FeiGeChuanShu/ncnn_paddleocr: Android paddleocr demo infer by ncnn (github.com)
ncnn框架量化工具过程记录笔记 - 知乎 (zhihu.com)
转载请注明出处
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。