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(