onnx-graphsurgeon Tensor 完全指南:深入理解 Variable 与 Constant 中间表示

发布时间:2026/9/15 19:42:14
onnx-graphsurgeon Tensor 完全指南:深入理解 Variable 与 Constant 中间表示
onnx-graphsurgeon Tensor 完全指南深入理解 Variable 与 Constant 中间表示【免费下载链接】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本篇技术指南聚焦 onnx-graphsurgeon 的 Tensor API。onnx-graphsurgeon 是 TensorRT 仓库中随附的 ONNX 图编辑库其Tensor及其子类Variable、Constant是整个中间表示IR中连接 Node 的边是进行模型修改、子图隔离、常量折叠等一切图变换操作的基础。阅读本文后你将掌握 Tensor 类族的设计原理、全部构造参数与常用方法并能结合仓库源码在真实工程中正确使用。从 API 参考页到源码Tensor 文档的组织方式关联文档 tensor.rst 是 Sphinx 风格的 autodoc 入口正文只有一行指令.. autoclass:: onnx_graphsurgeon.Tensor这意味着该页面的完整内容由 Sphinx 在构建时从onnx_graphsurgeon.Tensor的源码 docstring 自动展开生成并与其兄弟页面 variable.rst对应onnx_graphsurgeon.Variable和 constant.rst对应onnx_graphsurgeon.Constant共同构成 ir/tensor/toc.rst 这一文档小节隶属于 ir/toc.rst 的 Intermediate Representation 章节。因此理解 Tensor 的最佳途径是直接阅读其实现文件 tensor.py其中Tensor、Variable、Constant、LazyValues、SparseValues五个类共约 440 行docstring 即文档正文。这三个类均在包入口init.py第 7 行导出可直接通过onnx_graphsurgeon.Tensor、onnx_graphsurgeon.Variable、onnx_graphsurgeon.Constant使用本文后续简称gs.Tensor等。Tensor图结构中的抽象基类Tensor在 tensor.py 第 25 行被定义为一个抽象基类其 docstring 明确指出Abstract base class for tensors in a graph图中张量的抽象基类。在 onnx-graphsurgeon 的 IR 中Node 是图的点Tensor 是连接这些点的边——每个 Tensor 通过inputs关联其生产者 Node通过outputs关联其消费者 Node。关键设计点如下不可直接构造Tensor.__init__直接抛出NotImplementedError(Tensor is an abstract class)第 30-34 行。实际使用中只能创建其子类Variable或Constant。DYNAMIC -1哨兵值第 28 行表示动态维度/大小的标志常量在需要表达未知形状或维度时使用。inputs/outputs的同步赋值第 36-50 行__setattr__被重载当对inputs或outputs赋值时会先清空旧列表再 extend 新列表。这样做的目的是保证列表同步机制底层是SynchronizedList见 util/misc.py 第 139 行在每次修改时自动把 Node 与 Tensor 之间的双向引用关系维护一致同时注释说明这类原地操作触发的__iadd__后再赋值不会造成重复清理问题。is_empty()可选输入/输出的占位符def is_empty(self): return self.name 方法第 52-62 行判断该 Tensor 是否空。docstring 特别强调这里的空指的是张量名称为空字符串用于表示节点中省略的可选输入/输出而非张量的形状为空。这是 ONNX 图中省略可选输入时的标准表示方式。便捷导航i() 与 o()Tensor 提供了两个在图上行走的便捷方法第 115-152 行tensor.i(tensor_idx0, producer_idx0)返回该张量的某个生产者输入节点的第tensor_idx个输入张量。docstring 中的断言示例为tensor.i() tensor.inputs[0].inputs[0]tensor.i(1, 2) tensor.inputs[2].inputs[1]。注意参数顺序与o()相反这是因为张量通常只有一个生产者。tensor.o(consumer_idx0, tensor_idx0)返回该张量的第consumer_idx个消费者输出节点的第tensor_idx个输出张量示例断言为tensor.o() tensor.outputs[0].outputs[0]。这两个方法在链式追踪计算图数据流时非常实用例如x.i().i()可以向上回溯两层。类型互转to_constant() 与 to_variable()Tensor基类定义了两个原地类型转换方法这是图变换中高频使用的核心能力to_constant(values, data_locationNone, export_dtypeNone)第 64-89 行将张量原地转换为Constant。实现上是直接修改self.__class__ Constant再设置_values、data_location、export_dtype。docstring 强调由于是原地修改该张量的所有消费者/生产者都会看到这一更新。to_variable(dtypeNone, shape[])第 91-113 行将张量原地转换为Variable同样通过修改__class__实现并写入shape与dtype若dtype未指定则沿用原张量的export_dtype。相等性语义按名称比较第 162-168 行的__eq__定义两个 Tensor 相等当且仅当它们名称相同。docstring 补充了一条重要不变量一个 Graph 中不得包含重名张量。因此在对图做修改时必须谨慎命名避免意外合并或冲突。此外__str__第 154-157 行输出格式为TypeName (name): (shape..., dtype...)例如Variable (X): (shape(1, 3, 5, 5), dtypefloat32)__repr__直接复用__str__以保证日志输出整洁Constant的__repr__还会额外追加一行数值内容。Variable值在推理期才确定的张量Variable类第 171 行起docstring 定义为Represents a Tensor whose value is not known until inference-time值直到推理期才已知的张量对应 ONNX 图中的普通中间张量、模型输入与输出。构造函数签名第 180-201 行Variable( name: str, dtype: Union[np.dtype, onnx.TensorProto.DataType] None, shape: Sequence[Union[int, str]] None, type: str tensor_type, )四个参数的含义namestr张量名称图内必须唯一dtype数据类型接受 NumPy dtype 或onnx.TensorProto.DataType枚举shape形状允许包含字符串——docstring 明确说明 This may contain strings if the model uses dimension parameters即 ONNX 动态维度参数如batch、unk__符号可以直接以字符串出现在 shape 中type张量类型字符串默认tensor_type。此外Variable提供两个常用成员静态工厂方法Variable.empty()第 172-178 行创建名称为的 Variable用于表示被省略的可选输入。copy()第 213-219 行返回一个浅拷贝不含 inputs/outputs 连接信息的Variable(self.name, self.dtype, self.shape)。docstring 提醒通常你只需要拷贝整个 Graph而非单个张量。Variable.to_constant()第 203-211 行在转换前会先del self.dtype与del self.shape因为Constant的 shape/dtype 是从 values 派生的属性见下文再调用基类转换逻辑。Constant值已知的张量Constant类第 333 行起docstring 定义为Represents a Tensor whose value is known值已知的张量对应 ONNX 图中的常量/初始权重initializer例如卷积核权重、BN 均值方差等。构造函数签名第 334-374 行Constant( name: str, values: Union[np.ndarray, LazyValues], data_location: int None, export_dtype: Union[np.dtype, onnx.TensorProto.DataType] None, )name张量名称values张量数值必须是np.ndarray、LazyValues或SparseValues之一。若传入其他类型构造函数会调用G_LOGGER.critical直接报错第 360-371 行提示错误地提供了values参数类型data_location一个枚举值指示张量数据存储位置通常取自onnx.TensorProto.DataLocationexport_dtype导出到 ONNX 时使用的数据类型若未指定则使用 values 本身的 dtype见export_dtype属性第 416-421 行。属性推导values / shape / dtype / export_dtype与 Variable 直接存 shape/dtype 不同Constant的 shape 与 dtype完全由 values 推导values第 397-406 行getter 在首次访问时若内部是LazyValues会触发load()加载真正的 NumPy 数组懒加载语义setter 直接替换内部_values。shape第 408-410 行return self._values.shapedtype第 412-414 行return self._values.dtypeexport_dtypesetter第 423-437 行当设置非 None 的 export_dtype典型如 bfloat16时会检查当前 ONNX 版本是否支持onnx.helper.float32_to_bfloat16否则G_LOGGER.critical提示要么用ml_dtypes构造正确数据类型的 Constant要么降级 ONNX 到 1.19.1 或更早版本。懒加载机制LazyValues 与 SparseValues为了在导入大模型时避免立刻把所有权重读进内存导入器会把常量包装成LazyValues第 222-271 行。它保存底层onnx.TensorProto或SparseTensorProto预先计算shape、dtype、nbytes直到.load()被调用时才通过onnx.numpy_helper.to_array转为 NumPy 数组第 243-265 行。其中还有一处重要告警逻辑若该 dtype 无法转换为 NumPy 类型get_numpy_type返回 Noneload()会发出 warning——强制 cast 意味着重新导出时权重类型已与原模型不同这正是export_dtype参数存在的意义。SparseValues第 274-330 行继承自LazyValues用于从 ONNX 稀疏张量结构恢复稠密 NumPy 数组支持两种索引布局一维索引[NNZ]布局第 i 个值对应稀疏张量线性化后的第 i 个下标先np.zeros再按索引填入并 reshape 回tensor.dims二维索引[NNZ, rank]布局[i, j]位置的值是第 i 个非零值的第 j 维坐标逐行写入。若索引数据类型不是 INT64或索引维度既非 1 维也非 2 维都会触发G_LOGGER.critical报错。Constant.to_variable()第 376-387 行是to_constant的逆操作先取当前export_dtype删除_export_dtype与_values再调用基类转换若显式传入dtype则优先使用传入值。图结构中的双向连接SynchronizedList无论是Variable还是Constant构造函数中都会执行self.inputs misc.SynchronizedList(self, field_nameoutputs, initial[]) self.outputs misc.SynchronizedList(self, field_nameinputs, initial[])SynchronizedList定义于 util/misc.py 第 139 行是一个特殊的 list 子类当 Tensor 的inputs列表被 append 一个 Node 时它会自动把该 Tensor 追加到 Node 的outputs列表中field_name参数指明反向字段从而自动维护 Node-Tensor 之间的双向引用。这也是上文Tensor.__setattr__对inputs/outputs赋值做特殊处理的原因——任何赋值路径都必须经过同步逻辑保证图结构始终一致。理解这一点对调试节点连接异常类问题至关重要你几乎不需要手动维护反向引用。实战从仓库示例看 Tensor 的三种典型用法仓库 examples 目录提供了大量可直接运行的最小示例这里选取与 Tensor 最直接相关的三个1. 创建模型Variable 声明输入输出examples/01_creating_a_model/example.py 第 23-24 行展示了最基础的 Variable 创建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(opRelu, inputs[X], outputs[Y])最终graph gs.Graph(nodes[node], inputs[X], outputs[Y])即可通过gs.export_onnx(graph)导出 ONNX。可见 Variable 同时承担了图输入/输出声明和节点间数据流双重角色。2. 带权重模型Constant 充当 initializerexamples/02_creating_a_model_with_initializer/example.py 第 23-27 行X gs.Variable(nameX, dtypenp.float32, shape(1, 3, 224, 224)) W gs.Constant(nameW, valuesnp.ones(shape(5, 3, 3, 3), dtypenp.float32)) Y gs.Variable(nameY, dtypenp.float32, shape(1, 5, 222, 222))卷积权重W直接用np.ones构造为Constant作为 Conv 节点的第二个输入权重这正是 ONNX initializer 在 gs IR 中的表示方式——值已知导出时成为模型的初始权重。3. 子图隔离与常量折叠to_constant / to_variable 的用武之地examples/03_isolating_a_subgraph/generate.py 第 27-37 行构造了一个含两个常量a、b的乘法加法子图其配套教程演示了如何用Constant.to_variable()把常量变量化后做子图抽取。在更复杂的图优化场景中Variable.to_constant()则用于把可静态求值的节点输出固化为常量常量折叠——由于二者都是原地修改__class__图中所有关联节点立即感知到类型变化无需手工重建连接这是该设计的最大便利。小结与源码索引Tensor 类族是 onnx-graphsurgeon 图 IR 的基石Variable表示运行时才确定值的张量Constant表示值已知的常量/权重两者共享Tensor基类的名称相等语义、双向连接同步与便捷导航方法并可通过to_constant()/to_variable()原地互转。理解这些设计后你便能读懂仓库中几乎所有图变换示例examples 目录共 30 个示例并可进一步阅读 graph.rst 与 node.rst 学习完整 IR。关键文件速查API 文档入口tensor.rst、variable.rst、constant.rst核心实现ir/tensor.pyTensorL25、VariableL171、LazyValuesL222、SparseValuesL274、ConstantL333双向连接同步util/misc.py 的SynchronizedList顶层导出与版本init.py当前版本 0.6.1实操示例01_creating_a_model、02_creating_a_model_with_initializer、03_isolating_a_subgraph【免费下载链接】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),仅供参考