ONNX最核心的语法结构介绍

📚 ONNX核心语法全景图

ONNX模型本质上是一个由Protocol Buffers定义的嵌套数据结构。我们可以把它想象成一个俄罗斯套娃,最外层是模型,打开后是计算图,再打开是节点和张量。

📖 语法详解:逐层拆解

1. 模型层(ModelProto)—— 身份证

每个ONNX文件最外层都是一个ModelProto,它定义了模型的"身份证信息"。

文本语法示例

<
  ir_version: 8,                      // ONNX中间表示版本
  opset_import: [ "" : 18 ],           // 导入的算子集(默认域为空字符串)
  producer_name: "PyTorch",
  producer_version: "2.1.0",
  model_version: 1,
  doc_string: "这是一个简单的线性模型"
>
agraph ...                                    // 后面是计算图内容

关键字段详解

字段 类型 含义 实践意义
ir_version int64 ONNX IR规范版本 决定了模型结构的解析方式
opset_import repeated 导入的算子集列表 每个算子集由(domain, version)唯一标识
producer_name string 生成工具名称 调试时追溯来源
graph GraphProto 核心计算图 模型的主体内容

算子集(opset_import)的深层理解

  • 默认算子集(domain="")是所有模型必须导入的

  • 可以导入多个自定义算子集(如com.microsoftcom.nvidia

  • 每个算子集版本号单调递增,新版本可能新增、修改或弃用算子

  • 运行时必须支持所有导入算子集,否则拒绝执行

2. 计算图层(GraphProto)—— 施工图

GraphProto是整个模型的核心,它描述了计算的具体流程。

文本语法示例

agraph (float[N, 128] X, float[128, 10] W, float[10] B) => (float[N, 10] C)
{
    T = MatMul(X, W)
    S = Add(T, B)
    C = Softmax(S)
}

等价Python API构造

from onnx import helper, TensorProto

# 定义输入输出张量
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, ['N', 128])
W = helper.make_tensor_value_info('W', TensorProto.FLOAT, [128, 10])
B = helper.make_tensor_value_info('B', TensorProto.FLOAT, [10])
C = helper.make_tensor_value_info('C', TensorProto.FLOAT, ['N', 10])

# 定义节点
node1 = helper.make_node('MatMul', ['X', 'W'], ['T'])
node2 = helper.make_node('Add', ['T', 'B'], ['S'])
node3 = helper.make_node('Softmax', ['S'], ['C'])

# 构建图
graph = helper.make_graph(
    [node1, node2, node3],  # 节点列表,**必须按拓扑序**[citation:2]
    'linear_graph',
    [X, W, B],  # 输入
    [C]         # 输出
)

3. 节点层(NodeProto)—— 施工工序

每个NodeProto代表计算图中的一个算子,是实际执行计算的基本单元。

文本语法中的节点

T = MatMul(X, W)  // 输出 = 算子名(输入1, 输入2, ...)

节点构成要素

要素 含义 示例
算子类型 执行的操作 MatMulAddConv
输入 输入张量名列表 ['X', 'W']
输出 输出张量名列表 ['T']
属性 算子参数(常量 kernel_shapepadsstrides

重要概念:输入vs属性

输入表示动态计算得到的值,属性是图中固定的常量。这种区分对硬件实现很重要,因为属性可能在编译期就确定。

4. 张量层(ValueInfoProto/TensorProto)—— 材料清单

ONNX中有两种"材料":一种是流动的中间结果(ValueInfoProto),一种是固定的权重(TensorProto)。

ValueInfoProto(中间结果声明)

# 定义形状为[10,10]的浮点张量
tensor_info = helper.make_tensor_value_info(
    'a', 
    TensorProto.FLOAT,  # 数据类型
    [10, 10]            # 形状(可用维度符号,如'N'、'batch')
)

TensorProto(常量数据)

import numpy as np
from onnx import numpy_helper

# 将numpy数组转为ONNX常量
weights = np.random.randn(128, 10).astype(np.float32)
weights_tensor = numpy_helper.from_array(weights, name='W')

# 将常量添加到图的initializer列表中
graph.initializer.append(weights_tensor)

🔍 实战:用"语法眼"看一个真实模型

让我们用刚才学的语法知识,解读一个真实ONNX模型的文本输出。下面这个例子来自MMDeploy教程:

text

ir_version: 8
graph {
  node {
    input: "a"
    input: "x"
    output: "c"
    op_type: "Mul"
  }
  node {
    input: "c"
    input: "b"
    output: "output"
    op_type: "Add"
  }
  name: "linear_func"
  input {            # 输入a的声明
    name: "a"
    type {
      tensor_type {
        elem_type: 1  # FLOAT类型
        shape {
          dim {dim_value: 10}
          dim {dim_value: 10}
        }
      }
    }
  }
  input { name: "x" ... }  # 输入x
  input { name: "b" ... }  # 输入b
  output { name: "output" ... }  # 输出
}
opset_import {version: 15}

语法分析

  • 模型层ir_version: 8表示遵循ONNX IR v8规范;opset_import {version: 15}表示使用默认算子集v15

  • 图层graph包含2个节点、3个输入、1个输出

  • 节点层:先MulAdd,符合拓扑序(因为Add依赖Mul的输出c

  • 张量层:所有输入输出都是形状[10,10]的float32

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐