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)
手工优化ncnn模型结构 - 知乎 (zhihu.com)
FeiGeChuanShu/ncnn_paddleocr: Android paddleocr demo infer by ncnn (github.com)
ncnn框架量化工具过程记录笔记 - 知乎 (zhihu.com)文章来源:https://www.toymoban.com/news/detail-663907.html
转载请注明出处文章来源地址https://www.toymoban.com/news/detail-663907.html
到了这里,关于PP-OCRv3 文本识别模型转ncnn模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!