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文件。

 

  




相关