Skip to content

Fix BatchNorm fusion producing invalid ONNX when Conv nodes share weight initializers#2883

Merged
gramalingam merged 2 commits intomainfrom
copilot/fix-invalid-export-with-optimize-true
Apr 10, 2026
Merged

Fix BatchNorm fusion producing invalid ONNX when Conv nodes share weight initializers#2883
gramalingam merged 2 commits intomainfrom
copilot/fix-invalid-export-with-optimize-true

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 7, 2026

When a model reuses Conv2d+BatchNorm2d blocks (same weights called twice), the BatchNorm fusion rewrite creates a new initializer with the same name as the shared weight, overwriting it in the graph's initializer dict. This sets _is_initializer = False on the original value still referenced by the second Conv node, producing an invalid model:

InvalidArgument: Node input 'conv.weight_1' is not a graph input, initializer, or output of a previous node.

Reproducer:

class MWE(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv = nn.Conv2d(1, 16, kernel_size=3, padding=1)
        self.bn = nn.BatchNorm2d(16)

    def forward(self, x):
        f_1 = self.bn(self.conv(x[:, 0:1]))
        f_2 = self.bn(self.conv(x[:, 1:2]))
        return f_1 + f_2

torch.onnx.export(MWE().eval(), ..., optimize=True)  # Invalid model

Changes

  • _fuse_batchnorm.py: Added a check in _FuseBatchNormBase.check() that rejects the fusion when the inbound node's weight/bias initializers are used by nodes outside the matched pattern. This prevents the overwrite of shared initializers.
  • _fuse_batchnorm_test.py: Added regression test with two Conv+BN pairs sharing the same weight and bias initializers.

…ght initializers

Add a check in _FuseBatchNormBase.check() to verify that the inbound Conv/ConvTranspose/Gemm
node's weight and bias initializers are not shared with other nodes outside the matched pattern.

When two Conv+BatchNorm pairs share the same weight initializer, fusing the first pair
overwrites the shared initializer in the graph with fused values, leaving the second Conv
node with an invalid (unregistered) weight reference, producing an invalid ONNX model.

Fixes #2382

Agent-Logs-Url: https://github.com/microsoft/onnxscript/sessions/10e4a5fd-e010-48dc-8a29-991b7b0a6ca7

Co-authored-by: justinchuby <11205048+justinchuby@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix invalid ONNX export with optimize set to true Fix BatchNorm fusion producing invalid ONNX when Conv nodes share weight initializers Apr 7, 2026
Copilot AI requested a review from justinchuby April 7, 2026 18:51
@justinchuby justinchuby marked this pull request as ready for review April 7, 2026 20:23
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.06%. Comparing base (1ef0ec9) to head (5c5277f).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2883      +/-   ##
==========================================
+ Coverage   72.04%   72.06%   +0.01%     
==========================================
  Files         239      239              
  Lines       29305    29324      +19     
  Branches     2880     2884       +4     
==========================================
+ Hits        21112    21131      +19     
  Misses       7216     7216              
  Partials      977      977              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Copy Markdown
Collaborator

@gramalingam gramalingam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok. I think it would help to have a utility to do this kind of destructive transformation (the rule shouldn't have done this in the first place). Specifically, an utility could implement replace_uses_of_initializer_in( old_initializer_value, new_value, nodes) ... to abstract this logic. If the initializer has no uses outside the set of nodes, we can simply alter/swap. Otherwise, we can create a new initializer with the new value and a new (unique) name and use that instead. This sort of improvement can be done in a later PR though.

@github-project-automation github-project-automation bot moved this from Todo to Done in ONNX Script Review Board Apr 8, 2026
@gramalingam gramalingam merged commit 864b785 into main Apr 10, 2026
30 of 34 checks passed
@gramalingam gramalingam deleted the copilot/fix-invalid-export-with-optimize-true branch April 10, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

[ONNX] optimize=True produces invalid export when reusing Conv2d+BatchNorm2d block

3 participants