pytorch转onnx再转ncnn过程及问题记录(主要包括pytorch转onnx:Relu6不支持,onnx转ncnn:ResizeNearest不支持)
背景:pytorch版本:0.3.0,一个pytorch需要C++接口调用,同时也想试试ncnn模型是不快点,所以有 pytorch->onnx->ncnn.
1、pytorch2onnx
代码:
import io
import torch
import torch.onnx
from torch.autograd import Variable
from models.ssd_new_mobilenet_FFA import build_ssd
import torchvision
# device = torch.device("cpu")
def test():
# model = SSD(300,2)
num_classes =2
# args.trained_model = 'weights/mobilenet_v1_1.0_224.pth'
net = build_ssd('test', 300, num_classes=num_classes)
pthfile = r'./weights/ssd_new_mobilenet_FFA.pth'
net.load_state_dict(torch.load(pthfile, map_location=lambda storage, loc: storage))
net.eval()
dummy_input = Variable(torch.randn(1, 3, 300, 300)).cpu()
model = net.cpu()
torch.onnx.export(model, dummy_input, "ssd_new_mobilenet_FFA.onnx")
if __name__ == "__main__":
test()
运行,出现问题:RuntimeError: Attempted to trace Detect, but tracing of legacy functions is not supported
查看网上答案,修改,网络输出,如下(注释的是原始代码,新的是修改的):
# output = self.detect(
# loc.view(loc.size(0), -1, 10), # loc preds
# self.softmax(conf.view(-1, self.num_classes)), # conf preds
# # self.priors.type(type(x.data)).cuda() # default boxes
# self.priors.type(type(x.data)).cpu()
# )
output = (
loc.view(loc.size(0), -1, 10),
conf.view(conf.size(0), -1, self.num_classes),
x
)
再次运行,出现问题:RuntimeError: ONNX export failed: Couldn't export operator hardtanh
跟进代码查看,其实就是不支持Relu6,查看网上资料修改,可以通过Relu+clamp来实现Relu6修改
relu = ReLU(inplace=True)(input)
replace_relu6 = relu.clamp(max=6)
但还是不行,不支持clamp,于是我想了一下,就是一个激活函数(没有权重参数),而且我最后要用的也不是onnx模型,而是ncnn,ncnn支持relu6就可以了。
于是在pytorch网络结构模型中先用Relu代替了Relu6(因为我的网络结构中激活函数只用了Relu6,所以用Relu代替不会混淆,如果本来就有Relu,那就要考虑其他激活函数代 替),
改了Relu6之后就可以成功转成功了生成了转换后的onnx模型。
然后,在onnx转ncnn时,针对Relu转的时候就转Relu6.
2、onnx2ncnn
首先,直接用onnx2ncnn.exe试了下,不行,出现错误:ResizeNearest not supported yet! # height_scale=2 # width_scale=2刚好,我需要改Relu转Relu6,所以直接备份ncnn- master,在ncnn-master目录下新建build文件夹,然后通过cmake-gui创建新的工程(提前准备好protobuf相关文件和路径)一起解决这两个问题。打开工程:(1)修改Relu转 ReLu6,查看caffe2ncnn.cpp,发现Relu6在ncnn中表示为Clip,所以修改Relu在ncnn中也表示为Clip,图示如下:
同时,Relu6在ncnn中用Clip表示还需要参数,对照caffe2ncnn增添参数图示如下:
至此,从pytorch2onnx中Relu6不支持问题解决。
(2)ResizeNearest不支持问题
首先,ncnn是支持最近邻插值的,只不过在onnx转ncnn时,只匹配Resize,Nearest作为一个属性参数附带在里面,所以匹配不到ResizeNearst。
如下图所示,增添层名ResizeNearest,都转为ncnn的插值层Interp
再添加Interp的属性参数w_scale,h_scale,可以查看onnx网络结构看到,图示如下:
最后在属性中添加命令参数运行onnx2ncnn工程,成功生成param和bin文件。