import src.OPs as op from src.c2oObject import * from onnx import helper import copy import numpy as np from src.op_layer_info import * import random import sys from typing import * import onnx class Caffe2Onnx(): def __init__(self, net, model, onnxname): # 初始化一个c2oGraph对象 self.onnxmodel = c2oGraph(onnxname) # 网络和参数 self.netLayerCaffe = self.GetNetLayerCaffe(net) self.netModelCaffe = self.GetNetModelCaffe(model) # 模型的输入名和输入维度 self.model_input_name = [] self.model_input_shape = [] # 节点列表 self.onnxNodeList = [] # 获取层列表 LayerList = self.AddInputsTVIAndGetLayerList(net) self.GenerateOnnxNodeList(LayerList) self.AddOutputsTVIAndValueInfo() # 获取网络层 def GetNetLayerCaffe(self, net): if len(net.layer) == 0 and len(net.layers) != 0: return net.layers elif len(net.layer) != 0 and len(net.layers) == 0: return net.layer else: print("prototxt layer error") return -1 # 获取参数层 def GetNetModelCaffe(self, model): if len(model.layer) == 0 and len(model.layers) != 0: return model.layers elif len(model.layer) != 0 and len(model.layers) == 0: return model.layer else: print("caffemodel layer error") return -1 # 将模型输入信息添加到Inputs中并获取后续层列表 def AddInputsTVIAndGetLayerList(self, net): # 如果第一个layer的类型为Input,且没有net.input存在 if net.input == [] and self.netLayerCaffe[0].type == "Input": layer_list = [] # 考虑到整个网络会有多输入情况 for lay in self.netLayerCaffe: if lay.type == "Input": if len(lay.top) == 1 and lay.top[0] != lay.name: input_layer_name = lay.top[0] else: input_layer_name = lay.name in_tvi = helper.make_tensor_value_info( input_layer_name + "_input", TensorProto.FLOAT, lay.input_param.shape[0].dim) self.model_input_name.append(input_layer_name + "_input") self.model_input_shape.append(lay.input_param.shape[0].dim) self.onnxmodel.addInputsTVI(in_tvi) else: layer_list.append(lay) return layer_list # 如果存在net.input elif net.input != []: if bool(net.input_dim): input_dim = net.input_dim elif bool(net.input_shape): input_dim = net.input_shape[0].dim else: raise RuntimeError("Input shape missing!") in_tvi = helper.make_tensor_value_info("input", TensorProto.FLOAT, input_dim) self.model_input_name.append("input") self.model_input_shape.append(input_dim) self.onnxmodel.addInputsTVI(in_tvi) return self.netLayerCaffe # 以上情况都不是,则该caffe模型没有输入,存在问题 else: raise ValueError("the caffe model has no input") # 得到layer的参数shape def GetParamsShapeAndData(self, layer): ParamShape = [] ParamData = [] # 根据这个layer名找出对应的caffemodel中的参数 for model_layer in self.netModelCaffe: if layer.name == model_layer.name: Params = copy.deepcopy(model_layer.blobs) ParamShape = [p.shape.dim for p in Params] ParamData = [p.data for p in Params] if layer.type == "BatchNorm" or layer.type == "BN": if len(ParamShape) == 3: # 如果是bn层,则不用最后一层的滑动系数 ParamShape = ParamShape[:-1] ParamData = ParamData[:-1] elif len(ParamShape) == 2 and len(ParamShape[0]) != 1: ParamShape = [[ParamShape[0][1]], [ParamShape[1][1]]] ParamData = ParamData return ParamShape, ParamData def get_param_shape(self, params): shapes = [] for p in params: if p.shape.dim != []: shape = p.shape.dim shapes.append(shape) else: shape = [p.num, p.channels, p.height, p.width] shapes.append(shape) return shapes # 将参数添加到Inputs中,并生成tensor存储数据 def AddInputsTVIFromParams(self, layer, ParamName, ParamType): ParamShape = [] ParamData = [] # 根据这个layer名找出对应的caffemodel中的参数 for model_layer in self.netModelCaffe: if layer.name == model_layer.name: Params = copy.deepcopy(model_layer.blobs) #ParamShape = [p.shape.dim for p in Params] ParamShape = self.get_param_shape(Params) ParamData = [p.data for p in Params] if layer.type == "BatchNorm" or layer.type == "BN": if len(ParamShape) == 3: # 如果是bn层,params为[mean, var, s],则需要把mean和var除以滑动系数s ParamShape = ParamShape[:-1] ParamData = [ [q / (Params[-1].data[0]) for q in p.data] if i == 0 else [q / (Params[-1].data[0] + 1e-5) for q in p.data] for i, p in enumerate(Params[:-1]) ] # with s elif len(ParamShape) == 2 and len(ParamShape[0]) == 4: ParamShape = [[ParamShape[0][1]], [ParamShape[1][1]]] ParamData = [[q / 1. for q in p.data] if i == 0 else [q / (1. + 1e-5) for q in p.data] for i, p in enumerate(Params)] if layer.type == "Reshape": ParamShape = [[len(model_layer.reshape_param.shape.dim)]] ParamData = [model_layer.reshape_param.shape.dim] if layer.type == "Convolution" or layer.type == "ConvolutionDepthwise": if len(ParamShape) == 2: ParamShape[1] = [ParamShape[0][0]] if layer.type == "InnerProduct": if len(ParamShape[0]) > 2: ParamShape[0] = [ParamShape[0][2], ParamShape[0][3]] if len(ParamShape) == 2: if len(ParamShape[1]) > 2: ParamShape[1] = [ParamShape[1][2], ParamShape[1][3]] if layer.type == "Normalize": if len(ParamShape) == 1: ParamShape[0] = [1, ParamShape[0][0], 1, 1] # comment it for tvm because tvm use broadcast at prelu layer # 个人感觉如果不用 tvm,就不需要使用 Prelu # if layer.type == 'PReLU': # ParamShape = [[ParamShape[0][0], 1, 1]] break # 判断是否有Param if ParamShape != []: ParamName = ParamName[0:len(ParamShape)] ParamType = ParamType[0:len(ParamShape)] for i in range(len(ParamShape)): ParamName[i] = layer.name + ParamName[i] p_tvi = helper.make_tensor_value_info(ParamName[i], ParamType[i], ParamShape[i]) p_t = helper.make_tensor(ParamName[i], ParamType[i], ParamShape[i], ParamData[i]) self.onnxmodel.addInputsTVI(p_tvi) self.onnxmodel.addInitTensor(p_t) # print("添加参数" + ParamName[i] + "输入信息和tensor数据") if layer.type == "BatchNorm" or layer.type == "BN" or layer.type == "Scale": return ParamName, ParamShape return ParamName # 手动将参数添加到输入信息中,并生成tensor存储数据 def AddInputsTVIMannul(self, layer, param_names, param_types, param_shapes, param_data): node_names = copy.deepcopy(param_names) for i in range(len(param_shapes)): node_names[i] = layer.name + param_names[i] p_tvi = helper.make_tensor_value_info(node_names[i], param_types[i], param_shapes[i]) p_t = helper.make_tensor(node_names[i], param_types[i], param_shapes[i], param_data[i]) self.onnxmodel.addInputsTVI(p_tvi) self.onnxmodel.addInitTensor(p_t) return node_names # # 由于 Slice 的 input 情况特殊,所以需要特殊处理 # if layer.type == 'Slice': # for i in range(len(ParamShape)): # p_tvi = helper.make_tensor_value_info(Param_Name[i], ParamType[i], ParamShape[i]) # p_t = helper.make_tensor(Param_Name[i], ParamType[i], ParamShape[i], ParamData[i]) # self.onnxmodel.addInputsTVI(p_tvi) # self.onnxmodel.addInitTensor(p_t) # return Param_Name # else: # for i in range(len(ParamShape)): # Param_Name[i] = layer.name + ParamName[i] # p_tvi = helper.make_tensor_value_info(Param_Name[i], ParamType[i], ParamShape[i]) # p_t = helper.make_tensor(Param_Name[i], ParamType[i], ParamShape[i], ParamData[i]) # self.onnxmodel.addInputsTVI(p_tvi) # self.onnxmodel.addInitTensor(p_t) # return Param_Name # 获取上一层的输出名(即当前层的输入) def GetLastLayerOutNameAndShape(self, layer): output_name = [] outshape = [] # flag is True: 模型的输入没有被覆盖 # flag is False: 模型的输入已经被覆盖 flag = True # 如果结点列表为空,或者当前层的bottom在input_name中,那么上一层输入一定是 Input if self.onnxNodeList == []: output_name += self.model_input_name outshape += self.model_input_shape else: for i in range(len(layer.bottom)): # 因为prototxt中存在top和bottom同名的情况,但是layer.bottom只能对应一个node,所以对每个layer.bottom,找到最末的那个同名节点作为上一层节点 name = None shape = None for node in self.onnxNodeList: for j in range(len(node.top) if node.node.op_type != "MaxPool" else 1): if layer.bottom[i] == node.top[j]: name = node.outputs_name[j] shape = node.outputs_shape[j] for k in range(len(node.bottom)): if node.top[j] == node.bottom[k]: for w in range(len(self.model_input_name)): if node.top[j] + '_input' == self.model_input_name[w]: flag = False for j in range(len(self.model_input_name)): if layer.bottom[i] + '_input' == self.model_input_name[j] and flag: output_name.append(self.model_input_name[j]) outshape.append(self.model_input_shape[j]) if name: output_name.append(name) outshape.append(shape) try: assert output_name, "Failed at layer %s, layer's bottom not detected ..." % (layer.name) except: print("Failed at layer %s, layer's bottom not detected ..." % (layer.name)) exit(-1) return output_name, outshape # 获取当前层的输出名,即layername def GetCurrentLayerOutName(self, layer): # return [layer.name] # 考虑有多个输出的情况 # # TODO: 为什么要使用 layer.name 进行替代呢? if layer.top == layer.bottom and len(layer.top) == 1: return [layer.name] return [out for out in layer.top] def GenerateOnnxNodeList(self, Layers): for i in range(len(Layers)): print("convert layer: " + Layers[i].name) # Convolution if Layers[i].type == "Convolution" or Layers[i]. type == Layer_CONVOLUTION: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 conv_pname = self.AddInputsTVIFromParams(Layers[i], op_pname["Conv"], op_ptype["Conv"]) input_name.extend(conv_pname) # 3.构建conv_node conv_node = op.createConv(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(conv_node) elif Layers[i].type == "ConvolutionDepthwise" or Layers[i].type == Layer_CONVOLUTION: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 conv_pname = self.AddInputsTVIFromParams(Layers[i], op_pname["Conv"], op_ptype["Conv"]) input_name.extend(conv_pname) # 3.构建conv_node conv_node = op.createConv(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(conv_node) # BatchNorm+Scale elif Layers[i].type == "BatchNorm" or Layers[i].type == "BN": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 if i < len(Layers) - 1 and Layers[i + 1].type == "Scale": scale_pname, scale_pshape = self.AddInputsTVIFromParams(Layers[i + 1], op_pname["Scale"], op_ptype["Scale"]) bn_pname, bn_pshape = self.AddInputsTVIFromParams(Layers[i], op_pname["BatchNorm"], op_ptype["BatchNorm"]) assert bn_pshape == scale_pshape, "BatchNorm and Scale params should share the same shape" input_name.extend(scale_pname) input_name.extend(bn_pname) else: bn_pshape, _ = self.GetParamsShapeAndData(Layers[i]) custom_params = [np.ones(shape=bn_pshape[0], dtype=np.float), 0.001 + np.zeros(shape=bn_pshape[1], dtype=np.float)] scale_pname = self.AddInputsTVIMannul(Layers[i], op_pname["Scale"], op_ptype["Scale"], bn_pshape, custom_params) bn_pname, bn_pshape = self.AddInputsTVIFromParams(Layers[i], op_pname["BatchNorm"], op_ptype["BatchNorm"]) input_name.extend(scale_pname) input_name.extend(bn_pname) # 3.构建bn_node bn_node = op.createBN(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(bn_node) elif Layers[i].type == "Scale": if i > 0 and (Layers[i - 1].type == "BatchNorm" or Layers[i - 1].type == "BN"): # bn + scale continue # signal scale input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 # node_name = Layers[i].name + random.choice('1234567890abcdefghijklmnopqrst') node_name = Layers[i].name has_two_input: bool = False if len(input_name) > 1: has_two_input = True if has_two_input and op.need_add_reshape(input_shape): reshape_layer = copy.deepcopy(Layers[i]) # add reshape layer reshape_node_name = input_name[1] + '_reshap_' + random.choice('1234567890abcdefghijklmnopqrst') reshape_input_name = input_name[1] reshape_input_shape = input_shape[1] reshape_shape_data = op.get_param_shape(input_shape) reshape_shape_shape = np.shape(reshape_shape_data) reshape_params = self.AddInputsTVIMannul(Layers[i], [reshape_node_name + 'shape'], [TensorProto.INT64], [reshape_shape_shape], [reshape_shape_data]) reshape_output_name = [reshape_input_name + '_output_name'] reshape_node = op.createReshape(reshape_layer, reshape_node_name, [reshape_input_name, reshape_params[0]], reshape_output_name, reshape_input_shape, output_shape=[reshape_shape_data]) self.onnxNodeList.append(reshape_node) # add mul node input_name[1] = reshape_output_name[0] input_shape[1] = reshape_shape_data mul_node = op.create_mul_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(mul_node) else: param_shape, param_data = self.GetParamsShapeAndData(Layers[i]) # Scale = Mul + Add if len(param_shape) == 2: # create mul param_scale_shape = [1, param_shape[0][0], 1, 1] param_scale_data = param_data[0] param_scale_name = self.AddInputsTVIMannul(Layers[i], ["_scale"], [TensorProto.FLOAT], [param_scale_shape], [param_scale_data]) mul_node_name = node_name + "_mul" mul_input_name = [input_name[0], param_scale_name[0]] mul_output_name = [output_name[0] + "_mul"] mul_input_shape = [input_shape[0], param_scale_shape] mul_node = op.create_mul_node(Layers[i], mul_node_name, mul_input_name, mul_output_name, mul_input_shape) self.onnxNodeList.append(mul_node) param_bias_shape = [1, param_shape[1][0], 1, 1] param_bias_data = param_data[1] param_bias_name = self.AddInputsTVIMannul(Layers[i], ["_bias"], [TensorProto.FLOAT], [param_bias_shape], [param_bias_data]) add_node_name = node_name + "_add" add_input_name = [mul_output_name[0], param_bias_name[0]] add_output_name = output_name add_input_shape = [input_shape[0], param_bias_shape] add_node = op.create_add_node(Layers[i], add_node_name, add_input_name, add_output_name, add_input_shape) self.onnxNodeList.append(add_node) # Scale = Mul if len(param_shape) == 1: # create mul param_scale_shape = [1, param_shape[0][0], 1, 1] param_scale_data = param_data[0] param_scale_name = self.AddInputsTVIMannul( Layers[i], ["_scale"], [TensorProto.FLOAT], [param_scale_shape], [param_scale_data]) mul_input_name = [input_name[0], param_scale_name[0]] mul_input_shape = [input_shape[0], param_scale_shape] mul_node = op.create_mul_node(Layers[i], node_name, mul_input_name, output_name, mul_input_shape) self.onnxNodeList.append(mul_node) # Pooling elif Layers[i].type == "Pooling" or Layers[i].type == Layer_POOLING: # TODO: # Pooling <= Pad + Pool # NOTE: 由于 Caffe 和 ONNX 对 AveragePool 的处理的方式的不同,所以需要在pool node 之前添加 Pad node # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # create pad node pads = op.get_pool_pads(Layers[i]) pads_shape = [np.shape(pads)] pads_name = node_name + "_output" pads_output_name = [node_name + "_output"] pad_output_shape = op.calculate_pad_output_shape(input_shape, pads) pads_param = self.AddInputsTVIMannul(Layers[i], ["_pad"], [TensorProto.INT64], pads_shape, [pads]) input_name.extend(pads_param) pool_type = op.pooling_type(Layers[i]) if pool_type == "GlobalMaxPool" or pool_type == "MaxPool": constant_value = [-sys.float_info.max] constant_shape = [np.shape(constant_value)] constant_value_param = self.AddInputsTVIMannul(Layers[i], ["_constant_value"], [TensorProto.FLOAT], constant_shape, [constant_value]) input_name.extend(constant_value_param) pad_node = op.create_pad_node(Layers[i], pads_name, input_name, pads_output_name, input_shape) self.onnxNodeList.append(pad_node) # 2.构建pool_node pool_node = op.create_pooling_node(Layers[i], node_name, pads_output_name, output_name, pad_output_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(pool_node) # MaxUnPool elif Layers[i].type == "MaxUnpool": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # 2.构建unpool_node unpool_node = op.createUnPooling(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(unpool_node) # Eltwise elif Layers[i].type == "Eltwise" or Layers[i].type == Layer_ELTWISE: # 1.获取节点输入名、输入维度、输出名、节点名 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 node_name = Layers[i].name # 2.构建eltwise_node eltwise_node = op.createEltwise(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(eltwise_node) # Softmax elif Layers[i].type == "Softmax" or Layers[i].type == Layer_SOFTMAX: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # 2.构建softmax_node softmax_node = op.createSoftmax(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(softmax_node) # Relu elif Layers[i].type == "ReLU" or Layers[i].type == Layer_RELU: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # letters = '1234567890abcdefghijklmnopqrst' # length = random.randrange(5, 16) # randstr = ''.join(random.choice(letters) for _ in range(length)) # node_name = node_name # for i in range(len(output_name)): # output_name[i] = output_name[i] + random.choice('1234567890abcdef') #print(output_name) # 2.构建relu_node relu_node = op.createRelu(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(relu_node) # PRelu elif Layers[i].type == "PReLU": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 pname = self.AddInputsTVIFromParams(Layers[i], op_pname["PRelu"], op_ptype["PRelu"]) input_name.extend(pname) # 3.构建PRelu_node PRelu_node = op.createPRelu(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(PRelu_node) # relu6 elif Layers[i].type == 'ReLU6': # relu6 = clip(0, 6) # add relu node input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name min_value = np.float(0) max_value = np.float(6) shape = np.shape([min_value]) min_param = self.AddInputsTVIMannul(Layers[i], ["_min"], [TensorProto.FLOAT], [shape], [[min_value]]) input_name.extend(min_param) max_param = self.AddInputsTVIMannul(Layers[i], ['_max'], [TensorProto.FLOAT], [shape], [[max_value]]) input_name.extend(max_param) relu6_node = op.create_clip_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(relu6_node) elif Layers[i].type == "Sigmoid": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # 2.构建relu_node sigmoid_node = op.createSigmoid(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(sigmoid_node) elif Layers[i].type == 'Log': input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name log_node = op.create_log_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(log_node) # LRN elif Layers[i].type == "LRN" or Layers[i].type == Layer_LRN: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.构建LRN_node LRN_node = op.createLRN(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(LRN_node) # Dropout elif Layers[i].type == "Dropout" or Layers[i].type == Layer_DROPOUT: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.构建Dropout_node Dropout_node = op.createDropout(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(Dropout_node) # Upsample elif Layers[i].type == "Upsample" or Layers[i].type == Layer_UPSAMPLE: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 # add roi input # add scales input paramshape = [[8, 1], [4, 1]] paramdata = [[1, 1, 1, 1, 2, 2, 2, 2], [1.0, 1.0, Layers[i].upsample_param.scale, Layers[i].upsample_param.scale]] pname = self.AddInputsTVIMannul(Layers[i], op_pname["Upsample"], op_ptype["Upsample"], paramshape, paramdata) input_name.extend(pname) # 3.构建Upsample_node Upsample_node = op.create_resize_node(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(Upsample_node) elif Layers[i].type == 'Interp': input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name interp_node = op.create_interp_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(interp_node) # Concat elif Layers[i].type == "Concat" or Layers[i].type == Layer_CONCAT: # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.构建Concat_node Concat_node = op.createConcat(Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(Concat_node) elif Layers[i].type == 'Slice': # 1. 获取节点书输入名,输入维度,输出名,节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name_list = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name starts, ends, axes = op.analyzeLayer(Layers[i], input_shape) SliceLayer = copy.deepcopy(Layers[i]) for i in range(len(output_name_list)): # 放在这里的原因是 slice_name = copy.deepcopy(input_name) # starts ends axes 的 shape 是相同的 shape = [np.shape([1])] starts_param = self.AddInputsTVIMannul(SliceLayer, ['_starts' + str(i)], [TensorProto.INT64], shape, [[starts[i]]]) ends_param = self.AddInputsTVIMannul(SliceLayer, ['_ends' + str(i)], [TensorProto.INT64], shape, [[ends[i]]]) axes_param = self.AddInputsTVIMannul(SliceLayer, ['_axes' + str(i)], [TensorProto.INT64], shape, [[axes[i]]]) slice_name.extend(starts_param) slice_name.extend(ends_param) slice_name.extend(axes_param) Slice_node = op.createSlice(SliceLayer, output_name_list[i], slice_name, [output_name_list[i]], input_shape, starts[i], ends[i]) # 3. 添加节点到节点列表 self.onnxNodeList.append(Slice_node) # Reshape elif Layers[i].type == "Reshape": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 reshape_param = op.get_reshape_param(Layers[i], input_shape) reshape_param_shape = [np.shape(reshape_param)] pname = self.AddInputsTVIMannul(Layers[i], op_pname["Reshape"], op_ptype["Reshape"], reshape_param_shape, [reshape_param]) input_name.extend(pname) # 3.构建reshape节点 reshape_node = op.createReshape(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加点到节点列表 self.onnxNodeList.append(reshape_node) # InnerProduct # 由于onnx中没有全连接层,因此需要拆分,拆分有两种方法(Reshape+Gemm,Reshape+MatMul+Add) elif Layers[i].type == "InnerProduct" or Layers[i].type == Layer_INNER_PRODUCT: node_layer = copy.deepcopy(Layers[i]) # 深拷贝 node_input_name, node_input_shape = self.GetLastLayerOutNameAndShape(node_layer) # 获取输入名列表和输入形状 reshape_outname = "" reshape_output_shape = op.getReshapeOutShape(Layers[i], node_input_shape) need_reshape = 0 if reshape_output_shape[0] == node_input_shape[0] else 1 if need_reshape: ####一、reshape # 1.获取节点输入名、输入维度、输出名、节点名 reshape_outname = [node_layer.name + "_Reshape"] reshape_nodename = node_layer.name + "_Reshape" # 2.生成节点参数tensor value info,并获取节点参数名, 将参数名加入节点输入名列表 paramshape = [[2]] reshape_pname = self.AddInputsTVIMannul(node_layer, op_pname["Reshape"], op_ptype["Reshape"], paramshape, reshape_output_shape) node_input_name.extend(reshape_pname) # 3.构建reshape_node reshape_node = op.createReshape(node_layer, reshape_nodename, node_input_name, reshape_outname, node_input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(reshape_node) # import ipdb; ipdb.set_trace() ####二、Gemm 最后一个node输出保持原名称 gemm_layer = copy.deepcopy(Layers[i]) # 深拷贝 # 1.获取节点输入名、输入维度、输出名、节点名 gemm_inname = reshape_outname if need_reshape == 1 else node_input_name gemm_input_shape = reshape_output_shape if need_reshape == 1 else node_input_shape gemm_outname = [gemm_layer.name] gemm_nodename = gemm_layer.name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 gemm_pname = self.AddInputsTVIFromParams(gemm_layer, op_pname["InnerProduct"], op_ptype[ "InnerProduct"]) # 获取输入参数,对于add来说blobs[1]里存放的是bias不需要,所以直接获取blobs[0] gemm_inname.extend(gemm_pname) # 3.构建gemm_node matmul_node = op.createGemm(gemm_layer, gemm_nodename, gemm_inname, gemm_outname, gemm_input_shape, gemm_layer.inner_product_param.num_output) # 4.添加节点到节点列表 self.onnxNodeList.append(matmul_node) elif Layers[i].type == 'ShuffleChannel': # TODO support ShuffleChannel # reshape [N, C, H, W] tensor to [N, G, C', H, W] node_layer = copy.deepcopy(Layers[i]) # 深拷贝 node_input_name, node_input_shape = self.GetLastLayerOutNameAndShape(node_layer) # 获取输入名列表和输入形状 reshape_outname = "" reshape_output_shape = op.getReshapeOutShape(Layers[i], node_input_shape) need_reshape = 0 if reshape_output_shape[0] == node_input_shape[0] else 1 if need_reshape: # 一. reshape [N, C, H, W] tensor to [N, G, C', H, W] # 1.获取节点输入名、输入维度、输出名、节点名 reshape_outname = [node_layer.name + "_Reshape"] reshape_nodename = node_layer.name + "_Reshape" # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 param_data = op.getReshapeOutShape(node_layer, node_input_shape) param_shape = np.array([1, 2, 3, 4, 5], np.int).shape reshape_pname = self.AddInputsTVIMannul(node_layer, op_pname["Reshape"], op_ptype["Reshape"], [param_shape], param_data) node_input_name.extend(reshape_pname) # 这里不用对输入进行拓展,因为输入没有增加 # node_input_name.extend(reshape_pname) # 3.构建reshape_node reshape_node = op.createReshape(node_layer, reshape_nodename, node_input_name, reshape_outname, node_input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(reshape_node) # 2. transpose [N, C', G, H, W] transpose_layer = copy.deepcopy(Layers[i]) # 深拷贝 # 1.获取节点输入名、输入维度、输出名、节点名 transpose_input_name = reshape_outname if need_reshape == 1 else node_input_name transpose_input_shape = reshape_output_shape if need_reshape == 1 else node_input_shape transpose_output_name = [node_layer.name + "_Transpose"] transpose_node_name = node_layer.name + "_Transpose" # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 # 获取输入参数,对于add来说blobs[1]里存放的是bias不需要,所以直接获取blobs[0] # TODO 这地方为什么要选择使用AddInputsTVIMannul?取决于什么? # ANSWER: 取决于要转换的 onnx 的类型 # TODO param_date 是什么?为什么要设置这个变量 param_data = [[2]] # transpose_pname = self.AddInputsTVIMannul(transpose_layer, # op_pname["Transpose"], # op_ptype['Transpose'], # param_data, # transpose_input_shape) # transpose_input_name.extend(transpose_pname) # 3. transpose_node = op.createTranspose(transpose_layer, transpose_node_name, transpose_input_name, transpose_output_name, transpose_input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(transpose_node) # 三、 Reshape [N, C', G, H, W] tensor to [N, C, H, W] # end_layer = copy.deepcopy(Layers[i]) end_layer.type = "DeReshape" # 最后的输出的节点要保持原名称,这是为了生成该节点,保持链路畅通 end_output_name = [end_layer.name] end_node_name = end_layer.name # 上一层的输出是这一层的输入 end_input_name = transpose_node.outputs_name end_input_shape = transpose_node.outputs_shape # 最后保持输出和输入的形状是一致的 end_output_shape = [[node_input_shape[0][0], -1, node_input_shape[0][2], node_input_shape[0][3]]] param_shape = [np.array([1, 2, 3, 4], dtype=np.int).shape] end_pname = self.AddInputsTVIMannul(node_layer, op_pname["DouReshape"], op_ptype["DouReshape"], param_shape, end_output_shape) end_input_name.extend(end_pname) # 构建 end_node = op.createReshape(end_layer, end_node_name, end_input_name, end_output_name, end_input_shape) self.onnxNodeList.append(end_node) # Deconvolution elif Layers[i].type == "Deconvolution": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 conv_pname = self.AddInputsTVIFromParams(Layers[i], op_pname["ConvTranspose"], op_ptype["ConvTranspose"]) input_name.extend(conv_pname) # 3.构建conv_node conv_node = op.createConvTranspose(Layers[i], node_name, input_name, output_name, input_shape) # if True: # self.__print_debug_info(node_name, input_name, output_name, input_shape, conv_node.outputs_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(conv_node) # Flatten elif Layers[i].type == "Flatten": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # 由于后面 Flatten 的优化有问题,所以目前先将 Flatten -> reshape # flatten_node = op.create_flatten_node(layers[i], node_name, input_name, # output_name, input_shape) # self.onnxnodelist.append(flatten_nodelatten_node) # continue # Flatten -> Reshape # import ipdb; ipdb.set_trace() # # 2.生成节点参数tensor value info,并获取节点参数名,将参数名加入节点输入名列表 paramshape = [[2]] paramdata = op.getReshapeOutShape(Layers[i], input_shape) reshape_pname = self.AddInputsTVIMannul(Layers[i], op_pname["Reshape"], op_ptype["Reshape"], paramshape, paramdata) input_name.extend(reshape_pname) # 3.构建reshape_node reshape_node = op.createReshape(Layers[i], node_name, input_name, output_name, input_shape) # 4.添加节点到节点列表 self.onnxNodeList.append(reshape_node) elif Layers[i].type == "Permute": # Permute -> Transpose input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name transpose_node = op.createTranspose(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(transpose_node) elif Layers[i].type == "PriorBox": input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name priorbox_node = op.create_priorbox_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(priorbox_node) elif Layers[i].type == "DetectionOutput": input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name detection_output_node = op.create_detection_output(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(detection_output_node) elif Layers[i].type == "Axpy": # axpy = mul + add # top = bottom[0] * bottom[1] + bottom[2] input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name # create mul node mul_node = op.create_axpy_mul_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(mul_node) # create add node add_node = op.create_axpy_add_node(Layers[i], node_name, input_name, output_name, input_shape) self.onnxNodeList.append(add_node) elif Layers[i].type == "Normalize": input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name lp_normalization_output_name = [output_name[0] + "_lp"] lp_normalization_node = op.create_Lp_Normalization(Layers[i], node_name, input_name, lp_normalization_output_name, input_shape) self.onnxNodeList.append(lp_normalization_node) # get Normalize scale_shape, scale_data = self.GetParamsShapeAndData(Layers[i]) scale_shape = [1, scale_shape[0][0], 1, 1] scale_input = self.AddInputsTVIFromParams(Layers[i], ["_scale"], [TensorProto.FLOAT]) mul_input_name = [lp_normalization_output_name[0], node_name + "_scale"] mul_input_shape = [input_shape[0], scale_shape] mul_node = op.create_mul_node(Layers[i], node_name + "_mul", mul_input_name, output_name, mul_input_shape) self.onnxNodeList.append(mul_node) elif Layers[i].type == "Power": # Power: Mul + Add + Pow # create mul node input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name power, scale, shift = op.get_power_param(Layers[i]) scale_node_name = self.AddInputsTVIMannul(Layers[i], ["_scale"], [TensorProto.FLOAT], [np.shape(scale)], [scale]) mul_input_name = [input_name[0], scale_node_name[0]] mul_node = op.create_mul_node(Layers[i], node_name + "_mul", mul_input_name, [output_name[0] + "_mul"], [input_shape[0], np.shape(power)]) self.onnxNodeList.append(mul_node) # create Add node shift_param_name = self.AddInputsTVIMannul(Layers[i], ["_shift"], [TensorProto.FLOAT], [np.shape(scale)], [shift]) add_input_name = [output_name[0] + "_mul", shift_param_name[0]] add_node = op.create_add_node(Layers[i], node_name + "_add", add_input_name, [output_name[0] + "_add"], [input_shape[0], np.shape(shift)]) self.onnxNodeList.append(add_node) # create Pow power_param_name = self.AddInputsTVIMannul(Layers[i], ["_param_power"], [TensorProto.FLOAT], [np.shape(power)],[power]) power_input_name = [output_name[0] + "_add", power_param_name[0]] power_node = op.create_power_node(Layers[i], node_name + "_power", power_input_name, output_name, [input_shape[0], np.shape(power)]) self.onnxNodeList.append(power_node) elif Layers[i].type == "TanH": # 1.获取节点输入名、输入维度、输出名、节点名 input_name, input_shape = self.GetLastLayerOutNameAndShape( Layers[i]) # 获取输入名列表和输入形状 output_name = self.GetCurrentLayerOutName(Layers[i]) # 获取输出名列表 node_name = Layers[i].name # 2.构建tanh_node tanh_node = op.createTanh( Layers[i], node_name, input_name, output_name, input_shape) # 3.添加节点到节点列表 self.onnxNodeList.append(tanh_node) elif Layers[i].type == "Crop": # Crop: Slice # create Slice node input_name, input_shape = self.GetLastLayerOutNameAndShape(Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name starts, ends, axes = op.get_crop_param(Layers[i],input_shape) Crop_name=[] Crop_name.append(input_name[0]) starts_param = self.AddInputsTVIMannul(Layers[i], ['_starts' + str(i)], [TensorProto.INT64], [np.shape(starts)], [starts]) ends_param = self.AddInputsTVIMannul(Layers[i], ['_ends' + str(i)], [TensorProto.INT64], [np.shape(ends)], [ends]) axes_param = self.AddInputsTVIMannul(Layers[i], ['_axes' + str(i)], [TensorProto.INT64], [np.shape(axes)], [axes]) Crop_name.extend(starts_param) Crop_name.extend(ends_param) Crop_name.extend(axes_param) crop_node = op.create_crop_node(Layers[i], node_name, Crop_name, output_name, input_shape) self.onnxNodeList.append(crop_node) # MVN elif Layers[i].type == "MVN": # MVN: InstanceNormalization # create InstanceNormalization if Layers[i].mvn_param.normalize_variance == False or Layers[i].mvn_param.across_channels == True: print("Failed type not support: " + Layers[i].type) exit(-1) input_name, input_shape = self.GetLastLayerOutNameAndShape( Layers[i]) output_name = self.GetCurrentLayerOutName(Layers[i]) node_name = Layers[i].name MVN_name = [] MVN_name.append(input_name[0]) scale, bias = op.get_InstanceNorm_param(Layers[i],input_shape) scale_param = self.AddInputsTVIMannul(Layers[i], ['_scale' + str(i)], [TensorProto.FLOAT], [np.shape(scale)], [scale]) bias_param = self.AddInputsTVIMannul(Layers[i], ['_bias' + str(i)], [TensorProto.FLOAT], [np.shape(bias)], [bias]) MVN_name.extend(scale_param) MVN_name.extend(bias_param) MVN_node = op.create_InstanceNorm_op(Layers[i], node_name, MVN_name, output_name, input_shape) self.onnxNodeList.append(MVN_node) else: print("Failed type not support: " + Layers[i].type) exit(-1) # 判断当前节点是否是输出节点 def JudgeOutput(self, current_node, nodelist): for output_name in current_node.outputs_name: for node in nodelist: if output_name in node.inputs_name: return False return True # 添加模型输出信息和中间节点信息 def AddOutputsTVIAndValueInfo(self): for i in range(len(self.onnxNodeList)): if self.JudgeOutput(self.onnxNodeList[i], self.onnxNodeList): # 构建输出节点信息 lastnode = self.onnxNodeList[i] for j in range(len(lastnode.outputs_shape)): output_tvi = helper.make_tensor_value_info(lastnode.outputs_name[j], TensorProto.FLOAT, lastnode.outputs_shape[j]) self.onnxmodel.addOutputsTVI(output_tvi) else: # 构建中间节点信息 innernode = self.onnxNodeList[i] for k in range(len(innernode.outputs_shape)): hid_out_tvi = helper.make_tensor_value_info(innernode.outputs_name[k], TensorProto.FLOAT, innernode.outputs_shape[k]) self.onnxmodel.addValueInfoTVI(hid_out_tvi) # 创建模型 def createOnnxModel(self): node_def = [Node.node for Node in self.onnxNodeList] graph_def = helper.make_graph( node_def, self.onnxmodel.name, self.onnxmodel.in_tvi, self.onnxmodel.out_tvi, self.onnxmodel.init_t, value_info=self.onnxmodel.hidden_out_tvi ) model_def = helper.make_model(graph_def, producer_name='Tencent YouTu') print("2.onnx模型转换完成") return model_def