ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

海思开发:Nanodet : onnx -> caffe

2021-03-02 21:01:24  阅读:737  来源: 互联网

标签:node layer name onnx shape caffe reshape 海思 out


一、前言

应网友所托,帮忙转换 nanodet 模型(注:原版 nanodet 上采样是线性插值法,海思中不支持,换成反卷积了),花了一天多时间思考与尝试,终于搞定它,过程如下。

二、主要过程

仔细想了下,其实 nanodet 并没有什么生僻的 op,全部是很普通的、常见的那种,但是它们的组合方式以及数据的shape太令人难受了,下面我截图说明一下。

1. split 不支持

在这里插入图片描述
这里说一下,onnxsplitcaffe 里面是 sliceslice 相关博客。相关转换代码如下:

# onnx2caffe/_operators.py 文件中
def _convert_Split(node, graph, err):
    node_name = node.name
    input_name = str(node.inputs[0])
    #print(node.attrs)
    #print(node.outputs)
    layer = myf("Slice", node_name, [input_name], node.outputs,
                axis=node.attrs['axis'],
                slice_point=node.attrs['split'][0]
                )
    #print(graph.channel_dims)
    graph.channel_dims[node.outputs[0]] = node.attrs['split'][0]
    graph.channel_dims[node.outputs[1]] = node.attrs['split'][1]
    #print(graph.channel_dims)
    return layer

2. transpose 的问题

在这里插入图片描述
在之前博客里提过,海思 caffepermute 局限很大,即其支持的 order 只能为 (0,2,3, 1)。这就让人很难受了,而且从上图可以看出transpose输入的数据是三维的,难上加难了。
仔细查看输入数据shapetranspose上面那个reshape的输入是四维,满足transpose需要,那我们是否可以调换一下顺序,下面试着写一下。

#                      reshape                transpose        
原始: (1, 1, 52, 52) ---------> (1, 1, 2704) ------------> (1, 2704, 1)
# 现在我们试着这样
#                   transpose (0, 2, 3, 1)       reshape
现在: (1, 1, 52, 52) ---------> (1, 52, 52, 1) ----------> (1, 2704, 1)

这样一来,海思caffepermute 的要求输入shape 满足了,order 也满足了。
在这里插入图片描述
另一边分支的 transpose 也同样处理。相关转换代码如下:

# onnx2caffe/_operators.py 文件中
def _convert_transpose_reshape(node, input_name_shape):
    node_name = node.name
    input_name = input_name_shape[0]
    output_name = str(node.outputs[0])
    layers = []
    transpose_out_name = node_name + "_tran_out"
    layer = myf("Permute", node_name, [input_name], [transpose_out_name], permute_param=dict(order=[0, 2, 3, 1]))
    layers.append(layer)

    #reshape_out_name = node_name + "reshape_out"
    layer = myf("Reshape", node_name + "_reshape", [transpose_out_name], [output_name],
                reshape_param=dict(shape=dict(dim=[1, -1, input_name_shape[1]])))
    layers.append(layer)

    return tuple(layers)

3. MatMul 的问题

这个还是先看图吧,直接点。
在这里插入图片描述
输入是三维数据,权值是二维的,虽然在 pc 上写了段代码,试验了下,发现这样的数据做矩阵运算是可以的,但是模型推理时数据还是不对。针对这样的情况,想着干脆加两个 reshape ,将其 shape 换过来后再做打算,流程如下:

#                     matmul
原始:(1, 10816, 11) ---------> (1, 10816, 1) 
#                     reshape                matmul               reshape
现在:(1, 10816, 11) ---------> (10816, 11) ---------> (10816, 1) ---------> (1, 10816, 1)

相关转换代码如下:

# onnx2caffe/_operators.py 文件中
def _convert_matmul_nanodet(node, graph, err):  # 建立网络结构图
    layers = []
    node_name = node.name
    output_name = str(node.outputs[0])  # 输出节点名
    input_name = str(node.inputs[0])  # 上层节点名

    reshape_1_out = node_name + "_reshape1_out"
    layer = myf("Reshape", node_name + "_reshape1", [input_name], [reshape_1_out],
                reshape_param=dict(shape=dict(dim=[-1, 11])))
    layers.append(layer)

    matmul_out = node_name + "_matmul_out"
    weight_name = node.inputs[1]  # 本层参数名
    if weight_name in node.input_tensors:  # 判断参数数组是否真的存在
        W = node.input_tensors[weight_name]  # 获得参数数组
    else:  # 没有的话也就没意义继续了
        err.missing_initializer(node,
                                "MatMul weight tensor: {} not found in the graph initializer".format(weight_name, ))
        return

    b = None
    bias_flag = False
    if len(node.inputs) > 2:  # 如果只有上层节点名和 W 权值,则为 2
        b = node.input_tensors[node.inputs[2]]
    # 权值 shape 不对,也没意义继续了
    if len(W.shape) != 2 or (b is not None and len(b.shape) != 1):
        return err.unsupported_op_configuration(node, "MatMul is supported only for inner_product layer")
    if b is not None:
        bias_flag = True
        if W.shape[1] != b.shape[0]:  # FC 中,二者 shape[0] 是输出通道数, 一定相等,shape[1] 是输入通道数。
            return err.unsupported_op_configuration(node,
                                                    "MatMul is supported only for inner_product layer")
    # 不同于 gemm ,matmul 不做转置操作,w = (A, B), A 是输入通道数, B 是输出通道数
    layer = myf("InnerProduct", node_name, [reshape_1_out], [matmul_out], num_output=W.shape[1], bias_term=bias_flag)
    graph.channel_dims[output_name] = W.shape[1]  # 获得输出通道数
    layers.append(layer)

    #reshape_2_out = node_name + "_reshape2_out"
    layer = myf("Reshape", node_name + "_reshape2", [matmul_out], [output_name],
                reshape_param=dict(shape=dict(dim=[1, -1, 1])))
    layers.append(layer)

    return tuple(layers)

主要是这三个问题吧,其余都是些小问题,就不写出来干扰大家视线了。

三、验证与后语

出于谨慎,对比转换前后模型差异是必不可少的,代码如下:

# 1. onnx 推理 demo
import onnxruntime
import numpy as np

session = onnxruntime.InferenceSession(r"nanodet_weight\m\model_deconv_796_bs16.onnx")
img = np.ones((1, 3, 416, 416)).astype(np.float32)

result = session.run(["236"], {"input.1": img})
print(result)

# 2. caffe 推理 demo
import caffe
import numpy as np

def caffe_test():
    weight_file = r"nanodet_weight\m\nanodet.caffemodel"
    model_file = r"nanodet_weight\m\nanodet.prototxt"
    net = caffe.Net(model_file, weight_file, caffe.TEST)
    img = np.ones((1, 3, 416, 416))

    net.blobs['input.1'].data[...] = img

    out = net.forward()

    print(out["236"].shape)
    print(out["236"])

结果表明,二者除了精度差异外,其余一模一样,所以各位放心使用!
最后,附上完整转换demo链接,仓促之下写成难免有误,还请各位多多包涵,谢谢。本篇博客是今年第一篇,一段时间没写,确实手生不少,难怪说 ”学如逆水行舟,不进则退“,以后要多总结,多分享。

标签:node,layer,name,onnx,shape,caffe,reshape,海思,out
来源: https://blog.csdn.net/tangshopping/article/details/114292738

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有