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

 

 

 

 

 

 

 

 

 

 

 

 

  





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM