yolov5 v7.0转ncnn时问题解决

yolov5 v7.0转ncnn时遇到很多问题,ncnn版本20231027以下仅做记录:

1.通过官方代码,export.py 转onnx,指定–dynamic --simplify参数

2.编译并安装ncnn,通过onnx2ncnn将onnx转化为ncnn.bin和ncnn.param

3.加载ncnn模型(该部分坑很多)

直接加载,构造输入,获取输出,直接挂掉了,经过排查发现问题,如下图:

yolov5 v7.0转ncnn时问题解决_第1张图片

  通过节点输出,发现当获取到Slice算子时,输出错误,因此初步判定Slice算子有问题,通过文档查看,发现ncnn中Slice的参数1=3(这里的1是axis),由于ncnn最多支持4维度(忽略掉batch,就只剩3维),因此这里的维度越界了,改为1=2就可以正常运行了。

  修改后接着往下运行,又挂掉了,定位到发现是后边的Concate算子报错了,同样也是维度问题,将0=3改为0=2。

  通过netron观察onnx模型,发现yolov5s 7.0中有三个这样的结构,全部按照上述两个操作更改,如下:

yolov5 v7.0转ncnn时问题解决_第2张图片

  运行成功,不会报错了

  然而,ncnn模型输出与onnxruntime推理的模型输出不一致,不仅仅是不一致,里边全是-nan,也就是中间某些层的参数一定是出了问题。

  最开始怀疑是推理模型是的问题,可能是fp32使用int8或fp16推理等,通过文档查看,ncnn推理时默认是使用fp32,而且转模型是默认也是fp32,排除这一想法。

  由于最终模型结构没有问题,那么问题肯定是出在模型参数上了,再次通过各个节点的部分结果与onnxruntime对比,在同样输入时,第一层(Convelution)的结果就不对,肯定是权重或偏置不对。

  于是在ncnn/src/lays/convelution.cpp的load_model中添加输出信息,重新编译安装ncnn,运行代码发现卷积层的weight不对,通过查看ncnn.param文件,发现convelution算子的前边还有6个MemoryData,如下图:

yolov5 v7.0转ncnn时问题解决_第3张图片

  查阅文档发现ncnn中参数读取是按照顺序,果然卷积层的错误的权重来自391节点,也就是说MemoryData定义有问题,文档中MemoryData的参数有三个,whc,默认值都是0:
yolov5 v7.0转ncnn时问题解决_第4张图片

因此也就是说,ncnn读取权重的时候,MemoryData的大小被误认为是0了,所以就没有读取,直接到Convelution了,所以这里将MemoryData的参数补齐(需要对照Metron的参数维度以及这些节点的使用位置),如下图:

yolov5 v7.0转ncnn时问题解决_第5张图片

然后重新运行程序,运行成功,贴一张图展示下:

yolov5 v7.0转ncnn时问题解决_第6张图片
  总结一下:

  通过这一波操作下来,发现onnx2ncnn这个工具目前还有很多操作需要完善,单个算子可能没问题,但是多个算子的组合可能就有问题(主要原因是不支持更多维度)。

如果模型转换后遇到模型推理失败:

1.获取节点返回值或输出节点维度,定位到出错节点,然后根据出错节点的参数排查问题

如果推理结果和预期不符:

1.由于ncnn输出的结果都是以ncnn::Mat,因此可以根据各个节点输出与onnxruntime对比,从最开始不一致的节点分析,如果ncnn.param参数没问题,那肯定就是权重问题。

2.切记,不要随意删除有权重信息的中间节点,因为ncnn权重加载是按顺序的,如果含有权重信息的参数被删除,后续的算子参数就是错乱。

如果是算子不支持:

1.通过自定义算子,将代码放在ncnn/src/lays中,重新编译

2.通过自定义算子,在代码运行时注册进去

你可能感兴趣的:(YOLO,ncnn,onnx2ncnn)