使用 ONNX GraphSurgeon 从零构建 ONNX 模型以 GlobalLpPool 单算子模型为例【免费下载链接】TensorRTNVIDIA® TensorRT™ is an SDK for high-performance deep learning inference on NVIDIA GPUs. This repository contains the open source components of TensorRT.项目地址: https://gitcode.com/GitHub_Trending/tens/TensorRT本篇指南围绕 NVIDIA TensorRT 仓库中 onnx-graphsurgeon 组件的首个官方示例展开讲解如何不依赖任何已有模型仅通过 ONNX GraphSurgeon 的 IR中间表示API 手工搭建计算图并导出为合法的 ONNX 文件。读完本文你将掌握gs.Variable、gs.Node、gs.Graph三件套的核心用法理解 Variable/Constant 与 Tensor/Node/Graph 的拓扑关系并能够独立创建包含任意标准 ONNX 算子的最小模型为后续子图隔离、常量折叠、节点替换等进阶操作打下基础。ONNX GraphSurgeon 与从零建图的适用场景ONNX GraphSurgeon 是 NVIDIA TensorRT 生态中的 Python 库官方定位为创建和修改 ONNX 模型的库。绝大多数情况下我们用它导入已有的 ONNX 模型gs.import_onnx(onnx.load(model.onnx))再实施手术式修改但它的 IR 本身是完备的计算图抽象项目 README 明确指出The IR exposes a simple API so that it is possible to build graphs manually.IR 暴露了简单 API使得手工构建计算图成为可能。从零建图的实际价值体现在三类场景生成测试夹具为验证某个算子、某个 TensorRT 插件或自定义层的行为需要一个只包含该算子的最小模型隔离一切无关噪声程序化合成模型按规则批量生成不同 shape、不同属性组合的模型用于自动化测试理解 ONNX 图结构通过亲手组装 Tensor、Node、Graph直观理解 ONNX 模型中数据流、拓扑与属性的对应关系。本示例示例目录构建的正是这样一颗最小种子——一个仅含单个GlobalLpPool节点的 ONNX 模型。完整代码逐行拆解示例的核心实现位于 example.py全文仅 10 行有效代码却完整覆盖了定义输入 → 定义输出 → 定义算子节点 → 组装图 → 导出 ONNX → 落盘的全部流程import onnx_graphsurgeon as gs import numpy as np import onnx X gs.Variable(nameX, dtypenp.float32, shape(1, 3, 5, 5)) Y gs.Variable(nameY, dtypenp.float32, shape(1, 3, 1, 1)) node gs.Node(opGlobalLpPool, attrs{p: 2}, inputs[X], outputs[Y]) graph gs.Graph(nodes[node], inputs[X], outputs[Y], ir_version10) onnx.save(gs.export_onnx(graph), test_globallppool.onnx)运行方式同样简单在示例目录下执行python3 example.py即可在当前目录生成test_globallppool.onnx。第 1 步用 Variable 声明输入与输出张量X gs.Variable(nameX, dtypenp.float32, shape(1, 3, 5, 5)) Y gs.Variable(nameY, dtypenp.float32, shape(1, 3, 1, 1))Variable是 IR 中值在推理时才确定的张量携带名称、数据类型与形状信息。对应到源码 tensor.py其构造签名支持参数含义本示例取值name张量名称在图内必须唯一X/Ydtype数据类型NumPy dtype 或 ONNX TensorProto.DataTypenp.float32shape形状Sequence[int]维度可为字符串维度参数或 -1动态维度(1, 3, 5, 5)type张量类型默认tensor_type省略从源码可以看到Tensor基类定义了DYNAMIC -1常量tensor.py因此形状中的动态维度可以直接写作-1这与 ONNX 的 dynamic dim 语义一致。Variable内部通过misc.SynchronizedList维护inputs/outputs两个链表分别指向产生该张量的节点和消费该张量的节点。这里输入X的形状为(1, 3, 5, 5)即 batch1、channel3、空间尺寸 5×5输出Y的形状为(1, 3, 1, 1)空间维度被池化压缩为 1×1这正是全局池化的典型输出形态。第 2 步用 Node 定义 GlobalLpPool 算子node gs.Node(opGlobalLpPool, attrs{p: 2}, inputs[X], outputs[Y])Node表示图中的一个操作其构造签名见 node.py参数含义本示例取值op算子类型须为 ONNX 标准算子名或自定义域算子名GlobalLpPoolname节点名称可选默认省略attrs属性字典值可为 Python 基本类型、Graph或Tensor{p: 2}inputs输入张量列表[X]outputs输出张量列表[Y]domain节点所属域默认标准 ONNX 域省略注意attrs默认以OrderedDict存储源码self.attrs misc.default_value(attrs, OrderedDict())这保证了属性在导出时的顺序稳定性。第 3 步组装 Graph 并导出 ONNXgraph gs.Graph(nodes[node], inputs[X], outputs[Y], ir_version10) onnx.save(gs.export_onnx(graph), test_globallppool.onnx)Graph是计算图的容器其构造签名见 graph.py本示例使用到的关键参数nodes图中全部节点列表inputs/outputs图的输入/输出张量列表ir_version导出时写入 ONNX 模型的 IR 版本号示例指定为 10。值得强调的默认值与高级参数opset默认取Graph.DEFAULT_OPSET 11graph.pyname默认为onnx_graphsurgeon_graph此外还支持doc_string、producer_name、producer_version、import_domains、functions局部函数对应示例 11等元信息字段。导出链路是gs.export_onnx(graph)将 IR 图序列化为 ONNXModelProto再由官方onnx.save写入磁盘。底层实现在 onnx_exporter.py其中dtype_to_onnxL41-L73负责把 NumPy dtype 或ml_dtypes中的 bfloat16、float8、int4/uint4 等扩展类型映射为 ONNX 的 dtype 枚举导出器还会自动扫描图中节点含子图使用的 domain把自定义域追加到import_domainsupdate_import_domains并对重复节点名给出告警check_duplicate_node_names。生成的模型长什么样用 Netron 打开生成的test_globallppool.onnx可以看到下图所示的单节点图输入X1×3×5×5float32经GlobalLpPool节点属性p2输出Y1×3×1×1float32。项目 README 也说明examples 中的可视化图均使用 Netron 生成。深度理解GlobalLpPool 算子与 p 属性GlobalLpPool是 ONNX 标准算子集中的全局 Lp 池化算子会对输入张量除 batch 与 channel 之外的所有空间维度做池化输出形状恒为(N, C, 1, 1, ...)。其核心属性p控制范数阶数p 2时等价于GlobalAveragePoolL2 范数池化示例即采用此配置p 1时对应 L1 范数池化p取float(inf)时对应 MaxPool 行为L∞ 范数。示例中输入(1, 3, 5, 5)与输出(1, 3, 1, 1)的形状组合正好构成一个语义自洽的全局池化样例5×5 的空间尺寸被压缩成 1×1。这也是该示例被选为首个教程的原因——单输入、单输出、单节点、零初始值是理解 IR 三要素Tensor/Node/Graph之间关系的最小完备模型。由浅入深从零建图的完整知识地图本示例只是 onnx-graphsurgeon examples 系列的起点理解它之后可以沿着 examples 目录 的编号顺序继续深入每一例都对应一个从零建图或改图的实战场景示例主题与本例的关系02_creating_a_model_with_initializer带权重Constant/Initializer的模型创建在 Variable 基础上引入gs.Constant对应卷积等带权算子03_isolating_a_subgraph子图隔离反向应用建图逻辑从大模型中裁剪出子图04_modifying_a_model模型修改建图能力的逆过程——在已有图上增删节点07_creating_a_model_with_the_layer_api使用 layer API 建图graph.layer(op...)自动管理中间张量命名比手工拼装 Node 更省事11_creating_a_local_function创建局部函数在 Graph 之上引入Function对应graph.functions参数IR 核心概念速览为了让从零建图不流于抄代码这里把 IR 的三个核心概念详见 README 的 Understanding The Basics 章节对照源码做一个速览Tensor张量图数据的载体分为两个子类tensor.pyVariable值在推理时才确定携带dtype与shape本示例的 X、Y 均属此类Constant值已知以 NumPy 数组形式存储可通过values属性读取和修改。值得注意的实现细节是Constant的取值采用惰性加载LazyValues只有访问values属性时才真正把底层数据加载为 NumPy 数组tensor.py L397-L402这为大模型导入节省了大量不必要的内存拷贝。Node节点定义图中的一个操作op指明算子类型attrs存放算子属性inputs/outputs连接张量。节点输入输出始终是 Tensor。Graph图包含零个或多个 Node 以及输入/输出 Tensor 的容器。中间张量不显式跟踪而是通过遍历图内节点自动推导。Graph还提供cleanup()清除未使用的节点与张量、toposort()拓扑排序、tensors()返回Dict[str, Tensor]名称到张量的映射O(N) 复杂度等实用方法。关键机制inputs/outputs 的同步联动从零建图时最容易被忽视的一点是Tensor 与 Node 的inputs/outputs是双向同步的。在 tensor.py 的__setattr__与SynchronizedList机制下修改任一侧都会自动更新另一侧。例如del tensor.outputs[0]移除某个消费节点后该节点自身的inputs也会同步变为空反之把节点接到张量的outputs列表时节点侧会自动登记该输入。因此构建图时只需把inputs[X]、outputs[Y]传给 Node再交给Graph汇总即可无需手工维护双向引用这也是示例代码能如此精简的底层原因。验证与进阶自查生成模型后可以在 Python 交互环境中快速验证其结构与合法性import onnx import onnx_graphsurgeon as gs # 重新加载并导入生成的模型 model onnx.load(test_globallppool.onnx) graph gs.import_onnx(model) print(graph.inputs) # [Variable (X): (shape[1, 3, 5, 5], dtypefloat32)] print(graph.outputs) # [Variable (Y): (shape[1, 3, 1, 1], dtypefloat32)] print(graph.nodes) # [(GlobalLpPool). Inputs: [Tensor (X)] Outputs: [Tensor (Y)]] onnx.checker.check_model(model) # 官方模型结构校验onnx.checker.check_model通过即代表导出的模型结构合法可直接用于后续的 TensorRT 推理或其他下游消费。这也是每次从零建图后推荐的收尾动作。小结本示例用 10 行代码完整演示了 ONNX GraphSurgeon 从零建图的四步范式Variable 声明张量 → Node 声明算子 → Graph 组装 → export_onnx onnx.save 落盘。其背后是 IR 中 TensorVariable/Constant、Node、Graph 三类对象的清晰分工以及inputs/outputs双向同步这一巧妙设计。掌握了这个最小范式无论是生成单算子测试夹具、程序化合成模型还是理解后续子图隔离与图改写示例都将变得水到渠成。【免费下载链接】TensorRTNVIDIA® TensorRT™ is an SDK for high-performance deep learning inference on NVIDIA GPUs. This repository contains the open source components of TensorRT.项目地址: https://gitcode.com/GitHub_Trending/tens/TensorRT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考