opencv的DNN推理模块是不直接支持pytorch训练出来的pth文件。一般来说,可以将pth文件转成onnx格式,再用opencv调用onnx文件,即可实现推理。
pth文件转onnx文件时,因为onnx对于pth中某些定义的层是不支持的,难免会碰到一些问题。近期,对一些分割网络进行了部署,碰到的一些问题记录在下。
-
RuntimeError: ONNX export failed: Couldn’t export operator aten::adaptive_avg_pool2d
onnx不支持pytorch中的nn.AdaptiveAvgPool2d(),需要改成普通的nn.AvgPool2d()。修改之后不会对网络精度造成很大的影响。据了解,nn.AdaptiveAvgPool2d()的kernel大小是自适应变化的,stride也是自适应的,转成普通的nn.AvgPool2d就要自己计算kernel大小和stride,计算公式如下:stride = floor ( (input_size / (output_size) ) kernel_size = input_size − (output_size−1) * stride
-
RuntimeError: Failed to export an ONNX attribute, since it’s not constant, please try to make things (e.g., kernel size) static if possible
说明pytorch模型中有些层的参数是变量,onnx识别不了,需要改成常量。def pool(self, x, size): th, tw = size, size xh, xw = x.size()[2:] stride_h = xh // th stride_w = xw // tw kernel_h = xh - (th - 1) * stride_h kernel_w = xw - (tw - 1) * stride_w avgpool = nn.AvgPool2d((kernel_h, kernel_w), stride=(stride_h, stride_w)) # avgpool = nn.AdaptiveAvgPool2d(size) return avgpool(x)
通过调试xxxxxxxxxx/python3.6/site-packages/torch/onnx/symbolic_helper.py发现,我的池化层kernel_h、kernel_w是变量,onnx不认可。因此,将其改成固定的阿拉伯数字。
-
RuntimeError: ONNX export failed: Couldn’t export operator aten::upsample_bilinear2d
onnx不支持aten::upsample_bilinear2d。
解决办法:- pytorch版本升级至1.3+,转onnx的时候设置opset_version=11
2.如果第一种不行,就修改上采样层的模式为nearest,不过这样会对模型的效果打一些折扣。torch_out = torch.onnx._export(net, inputs, output_onnx, export_params=True, verbose=True, input_names=input_names, output_names=output_names, opset_version=11)
x = F.interpolate(x, size, mode='bilinear', align_corners=True) # 修改为model=‘nearest’ x = F.interpolate(x, size, mode='nearest')
- pytorch版本升级至1.3+,转onnx的时候设置opset_version=11
-
opencv读取onnx失败
net = cv2.dnn.readNetFromONNX('./fastscnn_citys.onnx')
上述代码读取onnx文件,出现以下报错
cv2.error: OpenCV(4.4.0) /tmp/pip-req-build-qacpj5ci/opencv/modules/dnn/src/onnx/onnx_importer.cpp:1410: error: (-2:Unspecified error) in function 'void cv::dnn::dnn4_v20200609::ONNXImporter::populateNet(cv::dnn::dnn4_v20200609::Net)' > (expected: 'shapes.depth() == CV_32S'), where > 'shapes.depth()' is 5 (CV_32FC1) > must be equal to > 'CV_32S' is 4 (CV_32SC1)
暂不知道是什么原因造成的,据说是Upsampling层造成的;将opencv版本升级至4.5+,bug消失。
-
Can’t create layer “122” of type “ReduceL2” in function 'getLayerInstance’
例如我的网络中其中有一层是对张量进行归一化:dn = torch.norm(desc, p=2, dim=1) # 在dim=1维计算二范数 [1, 256, 30, 60]-->[1, 30, 60] desc = desc.div(torch.unsqueeze(dn, 1))
转成onnx之后用opencv进行推理,出现对ReduceL2未识别的错误;对层进行修改,如下即可:
dn = torch.norm(desc, p=2, dim=1, keepdim=True) #在dim=1维计算二范数 [1, 256, 30, 60]-->[1, 1, 30, 60] desc = desc.div(dn) # Divide by norm to normalize. [1, 256, 30, 60]