PPOCRV3文本识别模型精度损失问题解决
- 1. 得到可用的ncnn模型
- 2. 先看问题
- 3. 快速解决
- 4. 问题分析
- 5. 最终效果
- 6. 结语
1. 得到可用的ncnn模型
paddleocr文本识别模型(ch_PPOCRv3_rec_infer)转ncnn模型,我参考了这位大神的博客,基本包括了我遇到的所有问题,是一篇很好的文章:https://blog.csdn.net/qq_29543997/article/details/128923330,博客中提到转换的模型存在精度损失,也是基于该问题引出这篇博客。
2. 先看问题
在得到ncnn模型后,使用同一张图像,使用onnx模型和ncnn模型分别在paddleocr工程和ncnn工程上运行看识别结果, 发现精度损失在10-4,按理说onnx转ncnn仅是数据架构的转换,误差不会这么大:
3. 快速解决
尝试更换ncnn编译库,重新测试模型精度损失。下面内容是个人排查问题的方法,有兴趣的可以看看。
4. 问题分析
分析方法:分析onnx模型和ncnn模型各层的输入结果,比对损失(过程还是蛮痛苦的)。
- onnx模型各层输出打印:https://blog.csdn.net/xefei/article/details/126489861
- ncnn模型各层输出打印:这个挺简单的,改一下输出blob名称就好。
最终发现ncnn模型中将paddle推理模型中的AdaptiveAvgPool层(onnx不支持)和其之后的卷积层替换为了GlogalAvgPool和InnerProduct层,对ncnn的.param文件修改后,该部分的精度损失由[10-4,10-3],降到[0,10-6]。
- paddle推理模型对应源码:PaddleOCR/ppocr/modeling/backbones/det_mobilenet_v3.py中的"class SEModule(nn.Layer):"
- param文件文件介绍:https://blog.csdn.net/m0_37264397/article/details/124184227#t1
- ncnn官网各层参数介绍:https://github.com/Tencent/ncnn/wiki/operators
param文件修改内容如下:
# 前两行
# 修改前
7767517
251 289
# 修改后
7767517
253 291
# 修改GlogalAvgPool和InnerProduct层(2处)
# 修改前
Pooling p2o.GlobalAveragePool.0 1 1 batch_norm_50.tmp_4_splitncnn_1 p2o.GlobalAveragePool.1 0=1 4=1
InnerProduct p2o.Conv.24 1 1 p2o.GlobalAveragePool.1 relu_2.tmp_0 0=64 1=1 2=16384 9=1
InnerProduct p2o.Conv.25 1 1 relu_2.tmp_0 conv2d_110.tmp_0 0=256 1=1 2=16384
# 修改后
Pooling p2o.GlobalAveragePool.0 1 1 batch_norm_50.tmp_4_splitncnn_1 p2o.GlobalAveragePool.1 0=1 7=1 8=1 5=1
Convolution p2o.Conv.24 1 1 p2o.GlobalAveragePool.1 conv2d_120.tmp_0 0=64 1=1 5=1 6=16384 9=1
ReLU p20.ReLU.1 1 1 conv2d_120.tmp_0 relu_2.tmp_0
Convolution p2o.Conv.25 1 1 relu_2.tmp_0 conv2d_110.tmp_0 0=256 1=1 5=1 6=16384
# 修改前
Pooling p2o.GlobalAveragePool.2 1 1 batch_norm_52.tmp_4_splitncnn_1 p2o.GlobalAveragePool.3 0=1 4=1
InnerProduct p2o.Conv.28 1 1 p2o.GlobalAveragePool.3 relu_3.tmp_0 0=128 1=1 2=65536 9=1
InnerProduct p2o.Conv.29 1 1 relu_3.tmp_0 conv2d_113.tmp_0 0=512 1=1 2=65536
# 修改后
Pooling p2o.GlobalAveragePool.2 1 1 batch_norm_52.tmp_4_splitncnn_1 p2o.GlobalAveragePool.3 0=1 7=1 8=1 5=1
Convolution p2o.Conv.28 1 1 p2o.GlobalAveragePool.3 conv2d_121.tmp_0 0=128 1=1 5=1 6=65536 9=1
ReLU p20.ReLU.2 1 1 conv2d_121.tmp_0 relu_3.tmp_0
Convolution p2o.Conv.29 1 1 relu_3.tmp_0 conv2d_113.tmp_0 0=512 1=1 5=1 6=65536
修改前后param的网络结构如下:
经过以上修改得到的模型仍有误差,接着往下逐层分析,最终确认注意力机制中的InnerProduct层(全连接层)导致的精度损失,经过分析,甚至手动计算ncnn模型该层的输出,得到的输出结果与onnx结果精度损失很小。直接怀疑ncnn编译库内部计算存在问题,实在没办法了,尝试更换一版ncnn的编译库,没想到精度损失直接降到0~10-5。至于到底是ncnn版本问题,还是编译过程中出了问题就不得而知。
- 获取ncnn模型各层权重:https://github.com/Tencent/ncnn/issues/4666#issuecomment-1521638902
- 获取onnx模型各层权重: 直接用netron打开,点击某一层,右侧可以看到其weights和bias, 且能够保存下来。
5. 最终效果
上面是对短文本的识别效果,误差还是有10-5,不过对于长文本误差则是在0到10-7,可能是因为短文本图像补边造成的差异,需要进一步确认。
6. 结语
至此来看,自己前面改AdaptiveAvgPool层和InnerProduct层好像没用,白折腾了,直接换ncnn编译库可能就好了。不过经过这么折腾学了不多东西,记录下来作为后备之需。