目录
报错At least one output should be requested:
import onnxfrom onnx import helperfrom onnx import TensorProtoimport numpy as np weight = np.random.randn(36)X = helper.make_tensor_value_info('input', TensorProto.FLOAT, [1, 2, 4, 4])W = helper.make_tensor('W', TensorProto.FLOAT, [2, 2, 3, 3], weight)B = helper.make_tensor('B', TensorProto.FLOAT, [2], [1.0, 2.0])output = helper.make_tensor_value_info('output', TensorProto.FLOAT, [1, 2, 2, 2])node_def = helper.make_node('Conv', # node name ['input', 'W', 'B'], ['output'], # outputs # attributes strides=[2, 2], )graph_def = helper.make_graph([node_def], 'test_conv_mode', [X], # graph inputs [output], # graph outputs initializer=[W, B], )mode_def = helper.make_model(graph_def, producer_name='onnx-example')onnx.checker.check_model(mode_def)onnx.save(mode_def, "./Conv.onnx")有output:

删除output:

import os import cv2 import onnxruntimeimport timeos.environ['TF_CPP_MIN_LOG_LEVEL']='3'os.environ['CUDA_VISIBLE_DEVICES'] = '0' import numpy as np if __name__ == '__main__': dummy_input = np.ones([1, 4, 4, 2], dtype=np.float32) # img_o = cv2.resize(img, (192, 192)) # img = img_o.astype(np.float32) # img/=255.0 model_path = r'Conv2.onnx' # model_path = r'C:\Users\Administrator\Pictures\mm\resources.tar\resources\face_landmark_with_attention_192x192/model_float32.onnx' model_file_name = model_path.split(".")[0] session_option = onnxruntime.SessionOptions() session_option.optimized_model_filepath = f"{model_file_name}_cudaopt.onnx" session_option.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_EXTENDED session = onnxruntime.InferenceSession( model_path, session_option, providers=['CUDAExecutionProvider'] ) input_name = session.get_inputs()[0].name output_names = [o.name for o in session.get_outputs()] input_shape = session.get_inputs()[0].shape # Warmup output = session.run( output_names, # {input_name: dummy_input} {input_name: dummy_input.transpose((0,3,1,2))} ) # Inference for i in range(1): start = time.time() output = session.run(output_names,{input_name: dummy_input.transpose((0,3,1,2))}) print(output) # cv2.imshow("result",img_o) # cv2.waitKey(0) # print(f'elapsed_time : {(time.time()-start)*1000} ms')onnxruntime.capi.onnxruntime_pybind11_state.InvalidArgument: [ONNXRuntimeError] : 2 : INVALID_ARGUMENT : At least one output should be requested.原因:make_graph时没有指定output:
graph_def = helper.make_graph([node_def], 'test_conv_mode', [X], # graph inputs [], # graph outputs # [output], # graph outputs initializer=[W, B], )mode_def = helper.make_model(graph_def, producer_name='onnx-example')解决方法:
加上output即可:
graph_def = helper.make_graph([node_def], 'test_conv_mode', [X], # graph inputs [output], # graph outputs # [output], # graph outputs initializer=[W, B], )mode_def = helper.make_model(graph_def, producer_name='onnx-example')import onnxonnx_model = onnx.load(r"model.onnx")graph = onnx_model.graphnodes = graph.node input = graph.inputprint('input',input[0].name)output = graph.outputprint('output',output[0].name)# for i in range(len(nodes)):# if i<6:# print(nodes[i].name) # modify inputconv0_node = nodes[5]conv0_node.input[0] = input[0].namefor i in [4,3,2,1,0]: print(i,nodes[i].name) graph.node.remove(nodes[i]) # onnx.checker.check_model(onnx_model)onnx.save(onnx_model, 'modify.onnx')输出该模型的节点个数,还有节点中的属性信息,当然也包含静态图的链路形状。
import onnx onnx_model = onnx.load("test.onnx")graph = onnx_model.graphnode = graph.node for i in range(len(node)): print(node[i])
1.2 搜索目标节点
for i in range(len(node)): if node[i].op_type == 'Constant': node_rise = node[i] if node_rise.output[0] == '449': print(i) # 157
1.3 修改目标节点
就像链表的插入操作一样,即是删除、新建、插入。如下列代码所示:
old_scale_node = node[157]new_scale_node = onnx.helper.make_node( "Constant", inputs=[], outputs=['449'], value=onnx.helper.make_tensor('value', onnx.TensorProto.FLOAT, [4], [1, 1, 1.81, 1.81])) # 新建新节点graph.node.remove(old_scale_node) # 删除旧节点graph.node.insert(157, new_scale_node) # 插入新节点
具体onnx.helper.make_node的使用方法,可以去github上查找doc,然后就可以愉快地随意修改ONNX模型了。
1.5 完整代码
import onnx onnx_model = onnx.load("test.onnx")graph = onnx_model.graphnode = graph.node old_scale_node = node[157]new_scale_node = onnx.helper.make_node( "Constant", inputs=[], outputs=['449'], value=onnx.helper.make_tensor('value', onnx.TensorProto.FLOAT, [4], [1, 1, 1.81, 1.81]))graph.node.remove(old_scale_node) graph.node.insert(157, new_scale_node) onnx.checker.check_model(onnx_model)onnx.save(onnx_model, 'out.onnx')import onnximport math input_size =(1080,1920)model = onnx.load_model("centerface.onnx")d = model.graph.input[0].type.tensor_type.shape.dimprint(d)rate = (int(math.ceil(input_size[0]/d[2].dim_value)),int(math.ceil(input_size[1]/d[3].dim_value)))print("rare",rate)d[0].dim_value = 1d[2].dim_value *= rate[0]d[3].dim_value *= rate[1]for output in model.graph.output: d = output.type.tensor_type.shape.dim print(d) d[0].dim_value = 1 d[2].dim_value *= rate[0] d[3].dim_value *= rate[1] onnx.save_model(model,"centerface_1088_1920.onnx" )思路很简单,我们录入的模型是一个DAG,裁剪后也要保持网络为一个DAG。如原始的预处理的数据流为data—>Identity—>Sub—>Mul—>Conv0,我们在进行Op裁剪以后,数据流则变成:data—>Conv0。核心代码也非常简单,我们只取从Conv0往后的nodes,然后再修改Conv0的input为data即可。
# remove preprocessing nodenew_nodes = old_nodes[3:]del model.graph.node[:]model.graph.node.extend(new_nodes) conv0_node = model.graph.node[0]conv0_node.input[0] = "data"graph = model.graph
PRelu的参数为一维的,我们在onnxruntime中进行inference的时候可能无法正常进行broadcast(graph optimization阶段也无法进行),所以我们的思路很直接:直接修改slope参数的shape信息,若原来是1x64的向量,则将其shape信息改成(64, 1, 1)。修改后可以正常进行broadcast。核心代码如下,我们需要构建input maps(节点信息)和initializer maps(权值信息),然后遍历所有node去找出prelu的节点进行修改。
input_maps = {}init_maps = {}keys = [] for inp in model.graph.input: input_maps[inp.name] = inp keys.append(inp.name) for init in model.graph.initializer: init_maps[init.name] = init for key in keys: if "relu" in key: ## revise input inp = input_maps[key] dim_value = inp.type.tensor_type.shape.dim[0].dim_value new_shape = [dim_value, 1, 1] graph.input.remove(inp) new_inp = onnx.helper.make_tensor_value_info(inp.name, onnx.TensorProto.FLOAT, new_shape) graph.input.extend([new_inp]) ## revise init init = init_maps[key] new_init = onnx.helper.make_tensor(init.name, onnx.TensorProto.FLOAT, new_shape, init.float_data) graph.initializer.remove(init) graph.initializer.extend([new_init])
5. BatchNormalization参数修改
迭代遍历所有节点找到BN层,然后对其spatial参数进行修改就好。下面为核心代码部分:
## revise batch norm op typefor node in model.graph.node: if (node.op_type == "BatchNormalization"): for attr in node.attribute: if (attr.name == "spatial"): attr.i = 16. 模型导出验证
最后在进行模型导出的时候,可以借助onnx的工具进行graph的有效性验证,调用如下的 api 即可:onnx.checker.check_model。此外进行裁剪和修改的模型,需要与原有模型进行数值对比,我采用的方案为计算原始graph和修改后graph的cos similarity,一般如果流程没有出错的话,cos similarity会非常接近1.0。如下为核心代码片:
import cv2import numpy as npfrom numpy import linalg as LA orig_session = onnxruntime.InferenceSession("./r18_orig.onnx", None)revs_session = onnxruntime.InferenceSession("./r18_revised.onnx", None) image = cv2.imread("./Tom_Hanks_574745.png")image = cv2.resize(image, (112, 112))image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) orig_data = np.array(image).transpose(2, 0, 1)orig_data = orig_data.reshape(1, 1, 112, 112).astype("float32")input_name = orig_session.get_inputs()[0].nameorig_result = orig_session.run([], {input_name: orig_data}) revs_data = (iamge - 127.5) / 128.0revs_data = np.array(revs_data).transpose(2, 0, 1)revs_data = revs_data.reshape(1, 3, 112, 112).astype("float32")revs_result = revs_session.run([], {input_name: revs_data}) # calculate cos similarityresult_dot = np.dot(orig_result[0], revs_result[0])result_norm = LA.norm(orig_result[0], 2) * LA.norm(revs_result[0], 2)cos_similarity = result_dot / result_normprint(cos_similarity)
原文链接:https://blog.csdn.net/weixin_41521681/article/details/112724867
如何修改已有的ONNX模型 - jingsupo - 博客园
for input_node in onnx_model.graph.input: if 'input_xxx' == input_node.name: print("change input data name") input_node.name = 'data'这一步稍微复杂一点,我们首先要生成一个node或者tensor,然后将这个node或者tensor加入graph中,然后选择性的增加一个node来操作刚刚加入graph的node或者tensor。
首先我们生成一个tensor,就是需要减去的均值
sub_const_node = onnx.helper.make_tensor(name='const_sub', data_type=onnx.TensorProto.FLOAT, dims=[1], vals=[-127.5])然后我们将刚刚生成的tensor插入graph中
graph.initializer.append(sub_const_node)然后我们再增加一个减均值的node
sub_node = onnx.helper.make_node( 'Add', name='pre_sub', inputs=['data', 'const_sub'], outputs=['pre_sub'])然后将node加入graph中
graph.node.insert(0, sub_node)仿造这样的流程我们继续加入除以方差的操作
# 插入mulmul_const_node = onnx.helper.make_tensor(name='const_mul', data_type=onnx.TensorProto.FLOAT, dims=[1], vals=[1.0 / 127.5]) graph.initializer.append(mul_const_node) sub_node = onnx.helper.make_node( 'Mul', name='pre_mul', inputs=['pre_sub', 'const_mul'], outputs=['pre_mul'])graph.node.insert(1, sub_node)这样操作之后,我们还需要一步,就是将第一个卷积层的输入改动一下:
# 第一层卷积的输入修改 for id, node in enumerate(graph.node): for i, input_node in enumerate(node.input): if 'data' == input_node: node.input[i] = 'pre_mul'这样能我们加入node或者tensor的过程基本就结束了
这一步我们就可以简单的重组一下graph,然后save模型就行了
graph = onnx.helper.make_graph(graph.node, graph.name, graph.input, graph.output, graph.initializer)info_model = onnx.helper.make_model(graph)onnx_model = onnx.shape_inference.infer_shapes(info_model) onnx.checker.check_model(onnx_model)onnx.save(onnx_model, onnx_path.replace('nopre', 'fix'))
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删