onnx模型解析

image.png
ir_version: 3
producer_name: "onnx.utils.extract_model"
graph {
  node {
    input: "Input3"
    input: "Constant339"
    output: "Minus340_Output_0"
    name: "Minus340"
    op_type: "Sub"
    doc_string: ""
    domain: ""
  }
  node {
    input: "Minus340_Output_0"
    input: "Constant343"
    output: "Block352_Output_0"
    name: "Block352"
    op_type: "Div"
    doc_string: ""
    domain: ""
  }
  name: "Extracted from {CNTKGraph}"
  initializer {
    data_type: 1
    float_data: 127.5
    name: "Constant339"
  }
  initializer {
    data_type: 1
    float_data: 255.0
    name: "Constant343"
  }
  input {
    name: "Input3"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 64
          }
          dim {
            dim_value: 64
          }
        }
      }
    }
  }
  output {
    name: "Block352_Output_0"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 64
          }
          dim {
            dim_value: 64
          }
        }
      }
    }
  }
  value_info {
    name: "Minus340_Output_0"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 64
          }
          dim {
            dim_value: 64
          }
        }
      }
    }
  }
  value_info {
    name: "Block352_Output_0"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 1
          }
          dim {
            dim_value: 64
          }
          dim {
            dim_value: 64
          }
        }
      }
    }
  }
}
opset_import {
  domain: ""
  version: 7
}

# 了解了onnx的结构后,我们可以根据它的结构将其拆分成多个单节点的onnx模型,以便于对整体模型的单个节点进行测试和分析。
import onnx
from onnx import helper,numpy_helper




def show_weight(weight):
    print("="*10, "details of weight: ", weight.name, "="*10)
    print("data type: ", weight.data_type)
    print("shape: ", weight.dims)
    data_numpy = numpy_helper.to_array(weight)
    # data_numpy = np.frombuffer(weight.raw_data, dtype=xxx)
    # print("detail data:", data_numpy)
    print("="*40)



# onnx.utils.extract_model("emotion-ferplus-7.onnx","mini_model.onnx",["Input3"],["Block352_Output_0"])

model = onnx.load("emotion-ferplus-7.onnx")
# print(model.ir_version)   # IR的版本
# print(model.producer_name)   #
# print(model.opset_import)   # opset 版本信息


# graph   
# graph中有node(NodeProto类型),input(ValueInfoProto类型),output(ValueInfoProto类型)和initializer(TensorProto类型)
# 其中node中存放着模型中的所有计算节点,input中存放着模型所有的输入节点,output存放着模型所有的输出节点,initializer存放着模型所有的权重常量 tensor;
#value_info存放了各个 动态tensor 的信息

#在ONNX模型中,initializer和value_info都是用于描述模型中的张量(tensor)的,但是它们的用途有所不同。

# initializer是一个包含模型中所有预先初始化的张量的列表。这些张量通常是模型的权重和偏差,它们在模型训练过程中被学习,并在模型推理过程中被使用。initializer中的每个元素都是一个TensorProto对象,包含了张量的数据类型、形状和值。

# value_info则是用于描述模型中的输入、输出和中间结果的张量的。它包含了张量的名称、数据类型和形状,但不包含张量的值。value_info主要用于在模型的图形定义中,描述那些不是模型输入也不是模型输出,但在模型计算过程中会被使用的张量。

# 总的来说,initializer和value_info都是用于描述模型中的张量的,但initializer更关注于张量的值,而value_info更关注于张量的元数据。
# node 通过input和output的指向关系,描绘出一个深度学习模型的拓扑图
# for node in model.graph.node:
#     print(node)


# print(model.graph.input)
# print(model.graph.output)


# #获取节点数量
# print(len(model.graph.node))


# # with open("model.txt","w") as f:
# #     f.write(str(model))
# # print(model.graph)


# print(getNodeNameList(model))

# print("-----------------------------------------------")
# #如何修改一个initializer  的值??
# # 移除旧的initializer  添加一个新的initializer 
# # 直接修改当前的initializer
# for initializer in  model.graph.initializer:
#     print(initializer)

# # model.graph.initializer.remove(next(init for init in model.graph.initializer if init.name == 'Constant343'))
# model.graph.initializer.remove(model.graph.initializer[0])

# # 创建一个新的TensorProto对象作为新的initializer
# new_initializer = helper.make_tensor(name = 'Constant339', data_type = onnx.TensorProto.FLOAT,
#                                      dims = [1], vals = [255.0], raw=False)

# model.graph.initializer.append(new_initializer)


# print("-----------------------------------------------")
# for initializer in  model.graph.initializer:
#     print(initializer)
    

# print("-----------------------------------------------")
# init = model.graph.initializer[0]
# print(init)
# init.name = "Constant343_new"
# #data是一个类似列表的对象,按照列表的方式去操作
# # init.float_data.pop()
# # init.float_data[:] = []
# # init.float_data.extend([123.0])

# # print(dir(init.float_data))

# init.float_data[0] = 123.50
# print("-----------------------------------------------")
# print(model.graph.initializer)


#####################################################################################################################

for info in  model.graph.value_info:
    # info.type.tensor_type.shape.dim[:] = [1,2,3]
    from onnx import TensorShapeProto, TensorShapeProto

    # 添加一个维度
    dim = TensorShapeProto.Dimension()
    dim.dim_value = 666  # 设置新维度的大小
    info.type.tensor_type.shape.dim.append(dim)

    info.type.tensor_type.shape.dim[0].dim_value = 333
   
    # 删除一个维度
    del info.type.tensor_type.shape.dim[2]
    
    print(info.type.tensor_type.shape.dim)
    # print(dir(info.type.tensor_type.shape.dim))
    
# tensorname_to_info = {info.name:info for  info in  model.graph.value_info}
# print(tensorname_to_info)





init_maper = {init.name:init for init in model.graph.initializer}
valueinfo_maper = {value_info.name:value_info for value_info in model.graph.value_info}
node_mapper = {node.name:node for init in  model.graph.node} 





def get_parent_node(graph, node):
    parents = []
    for input_name in node.input:
        for n in graph.node:
            if input_name in n.output:
                parents.append(n)
    return parents



def get_children_node(graph, node):
    children = []
    for output_name in node.output:
        for n in graph.node:
            if output_name in n.input:
                children.append(n)
    return children


node = node_mapper["noode_name"]
parents = get_parent_node(model.graph, node)
children = get_children_node(model.graph, node)



def remove_node_by_name(graph, node_name):
    # 标记需要移除的node
    node_to_remove = None
    for node in graph.node:
        if node.name == node_name:
            node_to_remove = node
            break
    if node_to_remove is None:
        return False
    # 删除该node的所有输入和输出edge
    # 把即将删除节点输出的tensor重定向,改接向即将删除节点输入的tensor
    for output in node_to_remove.output:
        for node in graph.node:
            for index, input in enumerate(node.input):
                if input == output:
                    node.input[index] = node_to_remove.input[0]
    # 从graph中移除这个node
    graph.node.remove(node_to_remove)
    return True




##在后面插入一个新的node
def insert_node_after(graph, existing_node_name, new_node):

    # 添加新节点到图中,并更新输入输出连接
    nodes = graph.node
    for i, node in enumerate(nodes):
        if node.name == existing_node_name:
            # 假设现有节点的输出仅链接到另外一个节点
            existing_output_name = node.output[0]

            # 修改现有节点的输出名称
            new_output_name = new_node.output[0]
            node.output[0] = new_output_name

            # 更新所有引用原先输出名称的节点的对应输入
            for later_node in nodes[i+1:]:
                for j, input_name in enumerate(later_node.input):
                    if input_name == existing_output_name:
                        later_node.input[j] = new_output_name

            # 插入新节点到现有节点后面
            nodes.insert(i + 1, new_node)
            break




def insert_node_before(graph, existing_node_name, new_node):
    # 查找现有节点以及它的输入
    existing_node_index = None
    existing_node_input = None
    for index, node in enumerate(graph.node):
        if node.name == existing_node_name:
            existing_node_index = index
            existing_node_input = node.input
            break
    
    if existing_node_index is None:
        raise ValueError(f"No node with name {existing_node_name} found in the graph.")

    # 新节点将采用现有节点的输入,并将其输出设置为现有节点的新输入
    # 假设新节点有一个输出
    new_node_output = new_node.output[0]
    
    # 所有原来指向现有节点输入的连接现在应该指向新节点的输出
    for node in graph.node:
        for i, input_name in enumerate(node.input):
            if input_name in existing_node_input:
                node.input[i] = new_node_output

    # 现有节点的新输入将是新节点的输出
    graph.node[existing_node_index].input[:] = [new_node_output]

    # 将新节点插入到图中现有节点之前的位置
    graph.node.insert(existing_node_index, new_node)
        
        
# NodeNameList = []
# for i in range(len(model.graph.node)):
#     node = model.graph.node[i]
#     print(node.input)
#     print(node.output)
#     print(node.attribute)
#     NodeNameList.append(model.graph.node[i].name)
    


#  out_tvi = [inner_output for inner_output in model.graph.value_info if inner_output.name == name]

https://github.com/ZhangGe6/onnx-modifier/tree/master
https://github.com/bindog/onnx-surgery/blob/master/surgery.py
https://bindog.github.io/blog/2020/03/13/deep-learning-model-convert-and-depoly/
https://www.zhihu.com/question/386526462
https://blog.csdn.net/ChuiGeDaQiQiu/article/details/123794387

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容