-
Notifications
You must be signed in to change notification settings - Fork 489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
burn-import initializer tensor not added to scope #1882
Comments
@skewballfox, do you think this is related to #1857? |
was this encountered with the version of burn last published in crates.io or on main? also, I'm a bit confused. is |
this is encountered on main. here is a more minimal reproducible example which clarifies the python#!/usr/bin/env python3
# used to generate model: onnx-tests/tests/resize/resize.onnx
import onnx
from onnx import helper, TensorProto, numpy_helper
import numpy as np
def main() -> None:
input_tensor = helper.make_tensor_value_info("input_tensor", TensorProto.FLOAT, [1, 1, 4, 4])
# Define the sizes tensor as an initializer
sizes = np.array([1, 1, 2, 2], dtype=np.int64)
sizes_initializer = numpy_helper.from_array(sizes, name="sizes")
resize_node = helper.make_node(
"Resize",
name="resize_node",
inputs=["input_tensor", "", "", "sizes"],
outputs=["output"],
mode="linear",
)
graph_def = helper.make_graph(
nodes=[resize_node],
name="ResizeGraph",
inputs=[input_tensor],
outputs=[
helper.make_tensor_value_info("output", TensorProto.FLOAT, [1, 1, 2, 2])
],
initializer=[sizes_initializer],
)
model_def = helper.make_model(graph_def, producer_name="resize")
onnx.save(model_def, "resize.onnx")
if __name__ == "__main__":
main() |
the same scope import error happens with constant input tensors too, e.g. python#!/usr/bin/env python3
# used to generate model: onnx-tests/tests/resize/resize.onnx
import onnx
from onnx import helper, TensorProto, numpy_helper
import numpy as np
def main() -> None:
input_tensor = helper.make_tensor_value_info("input_tensor", TensorProto.FLOAT, [1, 1, 4, 4])
# Define the sizes tensor as a constant node
sizes = np.array([1, 1, 2, 2], dtype=np.int64)
sizes_const_node = helper.make_node(
"Constant",
name="sizes_const",
inputs=[],
outputs=["sizes"],
value=numpy_helper.from_array(sizes)
)
resize_node = helper.make_node(
"Resize",
name="resize_node",
inputs=["input_tensor", "", "", "sizes"],
outputs=["output"],
mode="linear",
)
graph_def = helper.make_graph(
nodes=[sizes_const_node, resize_node],
name="ResizeGraph",
inputs=[input_tensor],
outputs=[
helper.make_tensor_value_info("output", TensorProto.FLOAT, [1, 1, 2, 2])
],
initializer=[],
)
model_def = helper.make_model(graph_def, producer_name="resize")
onnx.save(model_def, "resize.onnx")
if __name__ == "__main__":
main() |
@antimora I think this might not be related to lifting intializers or constants
sizes is in the list of initializers. the two fields with |
a minimal I don't think broken initializer gather python#!/usr/bin/env python3
# used to generate model: onnx-tests/tests/gather/gather.onnx
import onnx
from onnx import helper, TensorProto, numpy_helper
import numpy as np
def main() -> None:
input_tensor = helper.make_tensor_value_info("input_tensor", TensorProto.FLOAT, [1, 1, 4, 4])
# Define the indices tensor as an initializer
indices = np.array([0, 2], dtype=np.int64)
indices_initializer = helper.make_tensor(
name="indices",
data_type=TensorProto.INT64,
dims=indices.shape,
vals=indices,
)
gather_node = helper.make_node(
"Gather",
name="gather_node",
inputs=["input_tensor", "indices"],
outputs=["output"],
axis=2, # Assuming we want to gather along the height dimension (axis 2)
)
graph_def = helper.make_graph(
nodes=[gather_node],
name="GatherGraph",
inputs=[input_tensor],
outputs=[
helper.make_tensor_value_info("output", TensorProto.FLOAT, [1, 1, 2, 4])
],
initializer=[indices_initializer],
)
model_def = helper.make_model(graph_def, producer_name="gather")
onnx.save(model_def, "gather.onnx")
if __name__ == "__main__":
main() working constant gather python#!/usr/bin/env python3
# used to generate model: onnx-tests/tests/gather/gather.onnx
import onnx
from onnx import helper, TensorProto, numpy_helper
import numpy as np
def main() -> None:
input_tensor = helper.make_tensor_value_info("input_tensor", TensorProto.FLOAT, [1, 1, 4, 4])
# Define the indices tensor as a constant node
indices = np.array([0, 2], dtype=np.int64)
indices_const_tensor = helper.make_tensor(
name="indices_const_tensor",
data_type=TensorProto.INT64,
dims=indices.shape,
vals=indices,
)
indices_const_node = helper.make_node(
"Constant",
name="indices_const_node",
inputs=[],
outputs=["indices"],
value=indices_const_tensor,
)
gather_node = helper.make_node(
"Gather",
name="gather_node",
inputs=["input_tensor", "indices"],
outputs=["output"],
axis=2, # Assuming we want to gather along the height dimension (axis 2)
)
graph_def = helper.make_graph(
nodes=[indices_const_node, gather_node],
name="GatherGraph",
inputs=[input_tensor],
outputs=[
helper.make_tensor_value_info("output", TensorProto.FLOAT, [1, 1, 2, 4])
],
initializer=[],
)
model_def = helper.make_model(graph_def, producer_name="gather")
onnx.save(model_def, "gather.onnx")
if __name__ == "__main__":
main() I'd be curious to know more about the solution for dummy entries of empty input names, what mechanism of burn_import is causing empty names to be ignored? |
When we initialize nodes from the proto data all input arguments (which are simply strings, the original names of the arguments) pass through this function. All that get stored in the inputs is the original graph inputs and the node outputs after it has been processed. I was sort of wrong in my initial assumption: I assumed using an empty string for a Honestly I'm kind of stumped. I'm going to recreate the |
I thought the issue with the initializers might be that the original rename input function renamed arguments to empty string, so maybe there was some "if empty inline tensor" check happening, though when I tried that with size on whatever's happening only with initializers, but not other generated inputs, and not when those initializers are graph inputs. |
Looking at how that constant is defined, that will be lifted. So at the time scope is checked it will only exist as an input for resize. Do you guys off the top of your head know burn-graph side what is supposed to happen to input args with no parents? are they inlined, stored as variables? |
@skewballfox, you must be referring to ONNX Constant. Netron does not show individual Constant node if it's referenced by a single Node. ONNX import supports constants (see code). The constant value is stored as an attribute of the model module struct. It's a Param without gradients. |
This wouldn't be tied to a constant node because that constant is lifted, and the parent node is deleted. I was more wondering about the input arguments to nodes that aren't graph inputs or node outputs, so lifted constants (where the original node is deleted like above), initializers, generated arguments. The reason I ask is because this seems to be triggering when the argument only exist as an input, which is weird because scope is about determining ownership, but these args only exist in one place |
adding this line to burn/crates/burn-import/src/onnx/from_onnx.rs Line 163 in 71bd5ef
this needs to contain the initializer tensor names when passed to burn/crates/burn-import/src/onnx/to_burn.rs Line 328 in 71bd5ef
edit: nvm, this results in the initializer being a parameter to the error seems near this pathway, e.g. node input initializers are not being managed properly by burn/crates/burn-import/src/burn/graph.rs Line 40 in 71bd5ef
|
if I convert the initializer to a constant and disable constant lifting, I run into an error where an
let constant7: burn::module::Param<Tensor<B, 1, Int>> = burn::nn::Initializer::Zeros
.init([1], device)
.set_require_grad(false);
|
CC @nathanielsimard and @laggui . Should we make the initializer more generic for other tensors? |
here is some work towards generic initializers, it would need more careful handling: |
so I've been looking at output for
Reshape(
ReshapeNode {
input: TensorType {
name: Ident(
unsqueeze1_out1,
),
dim: 5,
kind: Float,
shape: None,
},
output: TensorType {
name: Ident(
unsqueeze2_out1,
),
dim: 6,
kind: Float,
shape: None,
},
shape: [
1,
1,
3,
4,
5,
1,
],
},
) I suspect that this is going to panic anytime we have a initializer or lifted constant that's typed as a tensor by burn graph. so far all the situations where I know constants are lifted in an op covered by test cases, the lifted values weren't tensors by this point. If you guys can think of a test case that has a lifted tensor for an op that takes a tensor argument, please let me know. |
Describe the bug
nodes with input tensors of category
initializer
fail to import due to initializer tensor names not being in scopeTo Reproduce
import a minimal onnx graph /w initializer tensor, e.g.
Expected behavior
burn-import generated model contains tensor initializers, e.g.
_Concat_output_0
is defined in the scopeActual behavior
burn import fails with
_Concat_output_0
is not in scope, panic at this line:burn/crates/burn-import/src/burn/scope.rs
Line 53 in 71bd5ef
Desktop (please complete the following information):
Additional context
this occurs in
resize
,gather
,mul_scalar
, andsub_scalar
node imports as well (with initializer tensors)The text was updated successfully, but these errors were encountered: