Repository: pytorch
Issue: torch.onnx.export generates an incorrect bias shape for certain conv transpose models
Labels: module: onnx,oncall: quantization,triaged

### 🐛 Describe the bug

When exporting a quantized 2d conv transpose model with bias=False, I noticed the generated onnx graph has an onnx.ConvTranspose with an incorrectly-shaped bias (of zeros). This is only reproduced with quantization and only with bias=False. Setting bias=True exports the correctly-shaped quantized bias. Here is some sample code:

```python
import torch
import onnx
import io

# pick some sizes
N = 1
Cin = 3
Cout = 7
Hin = 10
Win = 12
Hker = 5
Wker = 4

input_shape = (N, Cin, Hin, Win)

# define a quantized conv transpose module
class QConvTransposeLayerModule(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = torch.nn.Sequential(
            torch.nn.ConvTranspose2d(
                in_channels=Cin,
                out_channels=Cout,
                kernel_size=(Hker, Wker),
                stride=(1,1),
                padding=(0,0),
                output_padding=(0,0),
                groups=1,
                bias=False,
                dilation=1,
                ),
        )
        self.quantize = torch.quantization.QuantStub()
        self.dequantize = torch.quantization.DeQuantStub()

    def forward(self, input):
        qin = self.quantize(input)
        qout = self.layers(qin)
        out = self.dequantize(qout)
        return out

# get an inference module with a quantization scheme
def get_quantized_conv_transpose():
    model = QConvTransposeLayerModule()
    model.eval()
    model.qconfig = torch.quantization.default_qconfig
    torch.quantization.prepare(model, inplace=True)
    torch.manual_seed(0)
    for _ in range(32):
        model(torch.randn(input_shape, dtype=torch.float32))
    torch.quantization.convert(model, inplace=True)
    return model

model_torch = get_quantized_conv_transpose()

# export an onnx model to buffer
buffer = io.BytesIO()
sample_arg = torch.zeros(input_shape, dtype=torch.float32)
torch.onnx.export(model_torch, sample_arg, buffer, input_names=("input","weight"))

# get the onnx model from buffer & infer shapes
buffer = buffer.getvalue()
model_raw = onnx.load_from_string(buffer)
model_proto = onnx.shape_inference.infer_shapes(model_raw)

# inspect the graph to find the bias shape
graph = model_proto.graph

for node in graph.node:
    # get the input names for the ConvTranspose
    if node.op_type == "ConvTranspose":
        onnx_conv_inputs = node.input

for value_info in graph.value_info:
    # get value_info for third argument to onnx.ConvTranspose (bias)
    if value_info.name == onnx_conv_inputs[2]:
        print(f'Bias size in onnx model: {value_info.type.tensor_type.shape.dim[0].dim_value}')
        print(f'Correct bias size      : {Cout}')

# The onnx model has bias size of Cin, when it should return a bias size of Cout
# Note: the bias is a tensor of all zeros. 

# to save the model, uncomment
# onnx.save(model_proto, "somemodel.onnx")
```

The terminal output reads:

```
Bias size in onnx model: 3
Correct bias size      : 7
```

Because it is fairly simple to read the shape information from, this is the associated torch-mlir IR for the onnx graph:

```MLIR
module {
  func.func @main_graph(%arg0: !torch.vtensor<[1,3,10,12],f32>) -> !torch.vtensor<[1,7,14,15],f32> attributes {torch.onnx_meta.ir_version = 8 : si64, torch.onnx_meta.opset_version = 17 : si64, torch.onnx_meta.producer_name = "pytorch", torch.onnx_meta.producer_version = "2.4.0"} {
    %none = torch.constant.none
    %0 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<_> : tensor<ui8>} : () -> !torch.vtensor<[],ui8> 
    %1 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__1> : tensor<f32>} : () -> !torch.vtensor<[],f32> 
    %2 = torch.operator "onnx.QuantizeLinear"(%arg0, %1, %0) : (!torch.vtensor<[1,3,10,12],f32>, !torch.vtensor<[],f32>, !torch.vtensor<[],ui8>) -> !torch.vtensor<[1,3,10,12],ui8> 
    %3 = torch.operator "onnx.Cast"(%2) {torch.onnx.to = 2 : si64} : (!torch.vtensor<[1,3,10,12],ui8>) -> !torch.vtensor<[1,3,10,12],ui8> 
    %4 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__2> : tensor<f32>} : () -> !torch.vtensor<[],f32> 
    %5 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__3> : tensor<ui8>} : () -> !torch.vtensor<[],ui8> 
    %6 = torch.operator "onnx.DequantizeLinear"(%3, %4, %5) : (!torch.vtensor<[1,3,10,12],ui8>, !torch.vtensor<[],f32>, !torch.vtensor<[],ui8>) -> !torch.vtensor<[1,3,10,12],f32> 
    %7 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__4> : tensor<3x7x5x4xsi8>} : () -> !torch.vtensor<[3,7,5,4],si8> 
    %8 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__5> : tensor<1xf32>} : () -> !torch.vtensor<[1],f32> 
    %9 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__6> : tensor<1xsi8>} : () -> !torch.vtensor<[1],si8> 
    %10 = torch.operator "onnx.DequantizeLinear"(%7, %8, %9) : (!torch.vtensor<[3,7,5,4],si8>, !torch.vtensor<[1],f32>, !torch.vtensor<[1],si8>) -> !torch.vtensor<[3,7,5,4],f32> 
    %11 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__7> : tensor<1xsi64>} : () -> !torch.vtensor<[1],si64> 
    %12 = torch.operator "onnx.ConstantOfShape"(%11) {torch.onnx.value = dense_resource<__8> : tensor<1xsi32>} : (!torch.vtensor<[1],si64>) -> !torch.vtensor<[1],si32> 
    %13 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__9> : tensor<3xsi32>} : () -> !torch.vtensor<[3],si32> 
    %14 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__10> : tensor<1xf32>} : () -> !torch.vtensor<[1],f32> 
    %15 = torch.operator "onnx.Cast"(%12) {torch.onnx.to = 6 : si64} : (!torch.vtensor<[1],si32>) -> !torch.vtensor<[1],si32> 
    %16 = torch.operator "onnx.DequantizeLinear"(%13, %14, %15) : (!torch.vtensor<[3],si32>, !torch.vtensor<[1],f32>, !torch.vtensor<[1],si32>) -> !torch.vtensor<[3],f32> 
    %17 = torch.operator "onnx.ConvTranspose"(%6, %10, %16) {torch.onnx.dilations = [1 : si64, 1 : si64], torch.onnx.group = 1 : si64, torch.onnx.kernel_shape = [5 : si64, 4 : si64], torch.onnx.pads = [0 : si64, 0 : si64, 0 : si64, 0 : si64], torch.onnx.strides = [1 : si64, 1 : si64]} : (!torch.vtensor<[1,3,10,12],f32>, !torch.vtensor<[3,7,5,4],f32>, !torch.vtensor<[3],f32>) -> !torch.vtensor<[1,7,14,15],f32> 
    %18 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__11> : tensor<f32>} : () -> !torch.vtensor<[],f32> 
    %19 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__12> : tensor<ui8>} : () -> !torch.vtensor<[],ui8> 
    %20 = torch.operator "onnx.QuantizeLinear"(%17, %18, %19) : (!torch.vtensor<[1,7,14,15],f32>, !torch.vtensor<[],f32>, !torch.vtensor<[],ui8>) -> !torch.vtensor<[1,7,14,15],ui8> 
    %21 = torch.operator "onnx.Cast"(%20) {torch.onnx.to = 2 : si64} : (!torch.vtensor<[1,7,14,15],ui8>) -> !torch.vtensor<[1,7,14,15],ui8> 
    %22 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__13> : tensor<f32>} : () -> !torch.vtensor<[],f32> 
    %23 = torch.operator "onnx.Constant"() {torch.onnx.value = dense_resource<__14> : tensor<ui8>} : () -> !torch.vtensor<[],ui8> 
    %24 = torch.operator "onnx.DequantizeLinear"(%21, %22, %23) : (!torch.vtensor<[1,7,14,15],ui8>, !torch.vtensor<[],f32>, !torch.vtensor<[],ui8>) -> !torch.vtensor<[1,7,14,15],f32> 
    return %24 : !torch.vtensor<[1,7,14,15],f32>
  }
}

{-#
  dialect_resources: {
    builtin: {
      _: "0x0800000043",
      __1: "0x08000000336A853D",
      __2: "0x08000000336A853D",
      __3: "0x0800000043",
      __4: "0x080000001FB0E0C7EC0004EF644E8824A733D73070C5E0FF8E834A2C90F597FD04394B67FC488D8493F3DF2B0E23EA5D191B3307A18B0938EEDBD8900F0D95E74CC10BD6F6FD81816347227217DF3D5B7D0732C27EDE742844C7C2AEF372671555BD4E1A48C05141C9F66E0B5E2DB808577C3A8F0C265FF51195BF72A03E4E5EAE295EC24BC493E14E6A8B11789748CE637B1740CF27C93EA545EC9BBC2452AE7F28AC3A41FE4BECD81ECD0B7FD6F66F83D7C2BF15368EA9FF975FDEA961560AF64AC009F8F9137EBB52669EE9C9409B1672D060FD4E6BB58C98BC729244F56BA7048B203CD286CCBD1B1F8B66877223A96F4725BDF922F98051ED8D24F5E15FB7C7A0985D659BEE075E0FA539A689799E4B823B372662FE2FAAA3EA69469C6703B83FE7BB0CE910D8F67F0AE1F8DEBF7D425F47B0B4372FE8494BA7B6BFA4F597A226F8EE55E16DE353E60154CB5371239573F9F39B8897418927D8D7B8DFD088B45BF11C5A5F1761ACC29AC4ABB8EEFCD5651456C66ECEEA30410003018D7435217C25359C0CB402C8959921F8EEA3393297C749EE4A68C3676EB449CD9E3A6F687184C401B8AD",
      __5: "0x080000004BA72C3A",
      __6: "0x0800000000",
      __7: "0x080000000100000000000000",
      __8: "0x0800000000000000",
      __9: "0x08000000000000000000000000000000",
      __10: "0x0800000013F53338",
      __11: "0x08000000BD12B33C",
      __12: "0x080000003F",
      __13: "0x08000000BD12B33C",
      __14: "0x080000003F"
    }
  }
#-}
```



### Versions

PyTorch version: 2.4.0.dev20240408+cpu
Is debug build: False
CUDA used to build PyTorch: None
ROCM used to build PyTorch: N/A

OS: Ubuntu 22.04.3 LTS (x86_64)
GCC version: Could not collect
Clang version: 14.0.0-1ubuntu1.1
CMake version: version 3.29.2
Libc version: glibc-2.35

Python version: 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (64-bit runtime)
Python platform: Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
Is CUDA available: False
CUDA runtime version: No CUDA
CUDA_MODULE_LOADING set to: N/A
GPU models and configuration: No CUDA
Nvidia driver version: No CUDA
cuDNN version: No CUDA
HIP runtime version: N/A
MIOpen runtime version: N/A
Is XNNPACK available: True

CPU:
Architecture:                       x86_64
CPU op-mode(s):                     32-bit, 64-bit
Address sizes:                      48 bits physical, 48 bits virtual
Byte Order:                         Little Endian
CPU(s):                             16
On-line CPU(s) list:                0-15
Vendor ID:                          AuthenticAMD
Model name:                         AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
CPU family:                         25
Model:                              116
Thread(s) per core:                 2
Core(s) per socket:                 8
Socket(s):                          1
Stepping:                           1
BogoMIPS:                           6587.19
Flags:                              fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl tsc_reliable nonstop_tsc cpuid extd_apicid pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext perfctr_core ssbd ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx512_bf16 clzero xsaveerptr arat npt nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold v_vmsave_vmload avx512vbmi umip avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid
Virtualization:                     AMD-V
Hypervisor vendor:                  Microsoft
Virtualization type:                full
L1d cache:                          256 KiB (8 instances)
L1i cache:                          256 KiB (8 instances)
L2 cache:                           8 MiB (8 instances)
L3 cache:                           16 MiB (1 instance)
Vulnerability Gather data sampling: Not affected
Vulnerability Itlb multihit:        Not affected
Vulnerability L1tf:                 Not affected
Vulnerability Mds:                  Not affected
Vulnerability Meltdown:             Not affected
Vulnerability Mmio stale data:      Not affected
Vulnerability Retbleed:             Not affected
Vulnerability Spec rstack overflow: Mitigation; safe RET, no microcode
Vulnerability Spec store bypass:    Mitigation; Speculative Store Bypass disabled via prctl and seccomp
Vulnerability Spectre v1:           Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:           Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIBP conditional, RSB filling, PBRSB-eIBRS Not affected
Vulnerability Srbds:                Not affected
Vulnerability Tsx async abort:      Not affected

Versions of relevant libraries:
[pip3] numpy==2.0.0rc1
[pip3] onnx==1.15.0
[pip3] torch==2.4.0.dev20240408+cpu
[pip3] torchvision==0.19.0.dev20240408+cpu
[conda] Could not collect

cc @jerryzh168 @jianyuh @raghuramank100 @jamesr66a @vkuzo @jgong5 @Xia-Weiwen @leslie-fang-intel @avikchaudhuri @gmagogsfm @zhxchen17 @tugsbayasgalan @angelayi @suo @ydwu4