Migrating from onnx.helper to onnx_ir APIs¶
This page shows practical migrations from common onnx.helper model-building
patterns to onnx_ir APIs.
For a native IR-first walkthrough without the protobuf comparison, see Constructing models.
Why migrate¶
onnx_ir keeps ONNX concepts (Model/Graph/Node/Value), but gives you:
More convenient constructors (
ir.val,ir.node,ir.tensor)Better graph mutation ergonomics
Utilities for value replacement, extraction, and transformation workflows
Memory efficiency when handling large tensors and large models
Mapping cheatsheet¶
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Example 1: Build a small model from scratch¶
With onnx.helper¶
import onnx
from onnx import TensorProto
x = onnx.helper.make_tensor_value_info("x", TensorProto.FLOAT, [2, 3])
out = onnx.helper.make_tensor_value_info("out", TensorProto.FLOAT, [2, 3])
bias = onnx.helper.make_tensor(
"bias",
TensorProto.FLOAT,
dims=[2, 3],
vals=[1.0] * 6,
)
add = onnx.helper.make_node("Add", inputs=["x", "bias"], outputs=["tmp"], name="add_bias")
relu = onnx.helper.make_node("Relu", inputs=["tmp"], outputs=["out"], name="relu")
graph = onnx.helper.make_graph(
[add, relu],
"g",
inputs=[x],
outputs=[out],
initializer=[bias],
)
model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid("", 20)])
With onnx_ir¶
import onnx_ir as ir
x = ir.val("x", dtype=ir.DataType.FLOAT, shape=[2, 3])
out = ir.val("out", dtype=ir.DataType.FLOAT, shape=[2, 3])
bias_tensor = ir.tensor(
[[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]],
dtype=ir.DataType.FLOAT,
)
bias = ir.val("bias", const_value=bias_tensor)
add = ir.node("Add", inputs=[x, bias], name="add_bias")
relu = ir.node("Relu", inputs=add.outputs, outputs=[out], name="relu")
graph = ir.Graph(
inputs=[x],
outputs=[out],
nodes=[add, relu],
initializers=[bias],
opset_imports={"": 20},
name="g",
)
model = ir.Model(graph, ir_version=10)
ir.save(model, "model.onnx")
Example 2: Create an initializer¶
An initializer is a named Value with a constant tensor value
that is registered with a graph. Create one in two steps:
import onnx_ir as ir
weight_tensor = ir.tensor(
[[1.0, 0.0], [0.0, 1.0]],
dtype=ir.DataType.FLOAT,
)
weight = ir.val("weight", const_value=weight_tensor)
Pass the value to onnx_ir.Graph when constructing a graph:
x = ir.val("x", dtype=ir.DataType.FLOAT, shape=[2, 2])
out = ir.val("out", dtype=ir.DataType.FLOAT, shape=[2, 2])
matmul = ir.node("MatMul", inputs=[x, weight], outputs=[out])
graph = ir.Graph(
inputs=[x],
outputs=[out],
nodes=[matmul],
initializers=[weight],
opset_imports={"": 20},
name="g",
)
Alternatively, omit initializers=[weight] from the constructor and register
the value afterward:
graph.register_initializer(weight)
The initializer must have a non-empty name, a const_value, and no producing
node. Specify dtype when constructing a tensor from Python values if the ONNX
operator requires a particular element type. You can also pass a NumPy array to
onnx_ir.tensor(); in that case its dtype is preserved.
Example 3: Create nodes with Python attributes directly¶
With onnx.helper, attributes often require explicit helper calls.
With ir.node, plain Python values are converted automatically.
import onnx_ir as ir
x = ir.val("x", dtype=ir.DataType.FLOAT, shape=[1, 3, 8, 8])
conv = ir.node(
"Conv",
inputs=[x, ir.val("w"), ir.val("b")],
attributes={
"kernel_shape": [3, 3],
"pads": [1, 1, 1, 1],
"strides": [1, 1],
"group": 1,
},
name="conv0",
)
Example 4: Graph rewrite (replace a node output)¶
This is a common migration pain-point when using protobuf-level APIs directly.
import onnx_ir as ir
model = ir.load("model.onnx")
graph = model.graph
# Suppose we replace a node producing old_out with a new node producing new_out.
old_node = next(node for node in graph if node.name == "old_node")
inp = old_node.inputs[0]
new_node = ir.node("Identity", [inp], name="new_node")
graph.insert_after(old_node, [new_node])
# Redirect all downstream users and graph outputs.
ir.convenience.replace_all_uses_with(
old_node.outputs,
new_node.outputs,
replace_graph_outputs=True,
)
graph.remove([old_node], safe=True)
ir.save(model, "rewritten.onnx")
Example 5: Extract a bounded subgraph¶
import onnx_ir as ir
model = ir.load("model.onnx")
subgraph = ir.convenience.extract(
model.graph,
inputs=["input_0", "weight_0"],
outputs=["layer3_out"],
)
submodel = ir.Model(subgraph, ir_version=model.ir_version)
ir.save(submodel, "subgraph.onnx")
Migration tips¶
Start by replacing
make_tensor_value_info/make_nodewithir.val/ir.node.Keep names explicit while migrating to preserve external interfaces.
Prefer value-based rewrites (
replace_all_uses_with) over positional list surgery.Use
ir.save/ir.loadat the boundaries and keep transformation logic in IR.Preserve names, topological order, and type/shape information during rewrites. Run the corresponding repair or validation pass only when needed.