This is an automated email from the ASF dual-hosted git repository.

tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new e46c0610e9 [Relax][Onnx] Pass output_padding param in ConvTranspose 
(#18635)
e46c0610e9 is described below

commit e46c0610e9696734ef8b81134aff4e5997d84c15
Author: Nguyen Duy Loc <[email protected]>
AuthorDate: Mon Jan 5 20:59:09 2026 +0700

    [Relax][Onnx] Pass output_padding param in ConvTranspose (#18635)
    
    ### Summary
    Inconsistent shapes of results produced by TVM and ONNX View due to the
    ConvTranspose operator
    
    ### Steps to Reproduce
    - ONNX View: Output Shape = (1, 6, 56, 56)
    <img width="500" height="350" alt="Screenshots"
    
src="https://github.com/user-attachments/assets/8a7129b4-9ebc-47a0-aa47-6060e8df2827";
    />
    
    - TVM:  Output Shape = (1, 6, 55, 55) (due to output_padding=[0, 0])
    ```
    class Module:
        def main(input: R.Tensor((1, 3, 28, 28), dtype="float32"), weight: 
R.Tensor((3, 6, 3, 3), dtype="float32"), bias: R.Tensor((6,), dtype="float32")) 
-> R.Tensor((1, 6, 55, 55), dtype="float32"):
            R.func_attr({"num_input": 1, "params": [metadata["ffi.Tensor"][0], 
metadata["ffi.Tensor"][1]]})
            with R.dataflow():
                lv: R.Tensor((1, 6, 55, 55), dtype="float32") = 
R.nn.conv2d_transpose(input, weight, strides=[2, 2], padding=[1, 1, 1, 1], 
output_padding=[0, 0], dilation=[1, 1], groups=1, data_layout="NCHW", 
kernel_layout="IOHW", out_layout="NCHW", out_dtype="void")
                lv1: R.Tensor((1, 6, 1, 1), dtype="float32") = R.reshape(bias, 
R.shape([1, 6, 1, 1]))
                gv: R.Tensor((1, 6, 55, 55), dtype="float32") = R.add(lv, lv1)
                R.output(gv)
            return gv
    ```
    
    ### Expected
    - output_padding = [1, 1]
    ```
    class Module:
        def main(input: R.Tensor((1, 3, 28, 28), dtype="float32"), weight: 
R.Tensor((3, 6, 3, 3), dtype="float32"), bias: R.Tensor((6,), dtype="float32")) 
-> R.Tensor((1, 6, 56, 56), dtype="float32"):
            R.func_attr({"num_input": 1, "params": [metadata["ffi.Tensor"][0], 
metadata["ffi.Tensor"][1]]})
            with R.dataflow():
                lv: R.Tensor((1, 6, 56, 56), dtype="float32") = 
R.nn.conv2d_transpose(input, weight, strides=[2, 2], padding=[1, 1, 1, 1], 
output_padding=[1, 1], dilation=[1, 1], groups=1, data_layout="NCHW", 
kernel_layout="IOHW", out_layout="NCHW", out_dtype="void")
                lv1: R.Tensor((1, 6, 1, 1), dtype="float32") = R.reshape(bias, 
R.shape([1, 6, 1, 1]))
                gv: R.Tensor((1, 6, 56, 56), dtype="float32") = R.add(lv, lv1)
                R.output(gv)
            return gv
    ```
    
    ### Resolve
    - When implement converts an onnx ConvTranspose node into an equivalent
    Relax expression, get and pass output_padding param into op.
    - Fixed: #18601
---
 python/tvm/relax/frontend/onnx/onnx_frontend.py | 1 +
 src/relax/op/nn/convolution.cc                  | 2 +-
 tests/python/relax/test_frontend_onnx.py        | 6 ++++--
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/python/tvm/relax/frontend/onnx/onnx_frontend.py 
b/python/tvm/relax/frontend/onnx/onnx_frontend.py
index b41f56bfd4..2212fa6c68 100644
--- a/python/tvm/relax/frontend/onnx/onnx_frontend.py
+++ b/python/tvm/relax/frontend/onnx/onnx_frontend.py
@@ -1334,6 +1334,7 @@ class ConvTranspose(OnnxOpConverter):
             weight=inputs[1],
             strides=attr.get("strides", 1),
             padding=attr.get("pads", 0),
+            output_padding=attr.get("output_padding", 0),
             dilation=attr.get("dilations", 1),
             groups=attr.get("group", 1),
             data_layout=data_layout,
diff --git a/src/relax/op/nn/convolution.cc b/src/relax/op/nn/convolution.cc
index ca09c0f1cb..3fba58ede2 100644
--- a/src/relax/op/nn/convolution.cc
+++ b/src/relax/op/nn/convolution.cc
@@ -786,7 +786,7 @@ Expr conv2d_transpose(Expr data, Expr weight, 
ffi::Array<IntImm> strides,
   CHECK_GT(groups, 0) << "The number of groups in convolution is expected to 
be positive. However, "
                          "the given number of groups is "
                       << groups;
-  CHECK_EQ(output_padding.size(), 2) << "The input output_padding length is 
expected to be 4. "
+  CHECK_EQ(output_padding.size(), 2) << "The input output_padding length is 
expected to be 2. "
                                         "However, the given output_padding is "
                                      << output_padding;
   CHECK_EQ(strides.size(), 2)
diff --git a/tests/python/relax/test_frontend_onnx.py 
b/tests/python/relax/test_frontend_onnx.py
index 447e1ac99d..eb4c557e75 100644
--- a/tests/python/relax/test_frontend_onnx.py
+++ b/tests/python/relax/test_frontend_onnx.py
@@ -1171,11 +1171,12 @@ def test_conv(stride: int, dilation: int, pad: int, 
bias: bool, auto_pad: str):
     _verify_conv([3, 4, 32, 32, 32], [2, 4, 3, 3, 3])  # group=2
 
 
[email protected]("stride", [1, 2])
[email protected]("stride", [2])
 @pytest.mark.parametrize("dilation", [1])
 @pytest.mark.parametrize("bias", [True, False])
 @pytest.mark.parametrize("pad", [0, 2])
-def test_conv_transpose(stride: int, dilation: int, pad: int, bias: bool):
[email protected]("output_pad", [0, 1])
+def test_conv_transpose(stride: int, dilation: int, pad: int, bias: bool, 
output_pad: int):
     def _verify_conv_transpose(input_shape, weight_shape):
         nd = len(weight_shape) - 2
         output_shape = [input_shape[0], weight_shape[0]] + [
@@ -1190,6 +1191,7 @@ def test_conv_transpose(stride: int, dilation: int, pad: 
int, bias: bool):
             strides=[stride] * nd,
             dilations=[dilation] * nd,
             pads=[pad] * nd * 2,
+            output_padding=[output_pad] * nd,
             group=input_shape[1] // weight_shape[1],
         )
         graph = helper.make_graph(

Reply via email to