PP-OCRv3 文本识别模型转ncnn模型

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)

转载请注明出处

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值