The output size of ConvTranspose2d differs from the expected output size

username: yunjey
cooked: <p>In <a href="http://pytorch.org/docs/_modules/torch/nn/modules/conv.html#ConvTranspose2d" rel="nofollow noopener">documentation</a>, there are the code annotations as below.</p>
<pre><code>input = autograd.Variable(torch.randn(1, 16, 12, 12))
downsample = nn.Conv2d(16, 16, 3, stride=2, padding=1)
upsample = nn.ConvTranspose2d(16, 16, 3, stride=2, padding=1)
h = downsample(input)
h.size()         # (1, 16, 6, 6)
output = upsample(h, output_size=input.size())
output.size()    # (1, 16, 12, 12)
</code></pre>
<p>However, the actual <code>output.size()</code> returns <b>(1, 16, 11, 11)</b>. Is this normal? I think it should be (1, 16, 12, 12) because <code>ConvTranspose2d</code> is just opposite operation for <code>Conv2d</code>.</p>

username: fmassa
cooked: <p>This is expected, and that’s why there is the <code>output_size</code> argument to <code>ConvTranspose2d</code>, that compensates for it.<br>
As an example, suppose there is a convolution of stride 2 and kernel size 2. When applying it to a 4x4 image, the result is a 2x2 image. Now, if you apply the same convolution to a 5x5 image, the result will also be 2x2, so for a given <code>Conv2d</code>, two inputs map to the same output size.</p>

username: nsknsl
cooked: <aside class="quote no-group" data-username="fmassa" data-post="2" data-topic="1876">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/fmassa/48/58_2.png" class="avatar"> fmassa:</div>
<blockquote>
<p>output_size</p>
</blockquote>
</aside>
<p>ConvTranspose2d doesn’t have output_size argument, do you mean output_padding?<br>
oh , I found output_size in source code</p>

username: Jiankai
cooked: <p>So how to solve this problem?</p>

username: Konstantin_Osminin
cooked: <p>There is <code>output_size</code> argument in <code>nn.ConvTranspose2d.forward</code> function. Just provide desired output shape during forward pass.</p>

username: daydayjump
cooked: <p>I find that ‘output_padding is provided to resolve this ambiguity by effectively increasing the calculated output shape on one side. Note that output_padding is only used to find output shape, but does not actually add zero-padding to output.’ in the official ducuments<a href="https://pytorch.org/docs/stable/nn.html#convtranspose2d" rel="nofollow noopener">convtranspose2d</a>.<br>
So I think we can use ‘output_padding’ parameters to make output size same.</p>

username: ahkarami
cooked: <aside class="quote quote-modified" data-post="1" data-topic="52528">
  <div class="title">
    <div class="quote-controls"></div>
    <img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/ahkarami/48/630_2.png" class="avatar">
    <a href="https://discuss.pytorch.org/t/convtranspose2d-weight-shape/52528">ConvTranspose2d Weight Shape</a> <a class="badge-category__wrapper " href="/c/vision/5"><span data-category-id="5" data-drop-close="true" class="badge-category " title="Topics related to either pytorch/vision or vision research related topics"><span class="badge-category__name">vision</span></span></a>
  </div>
  <blockquote>
    I have a question about ConvTranspose2d.weight shape. As mentioned in the <a href="https://pytorch.org/docs/stable/nn.html#convtranspose2d" rel="noopener nofollow ugc">PyTorch documentation</a> the shape of ConvTranspose2d.weight tensor is as follows: 
(in_channels, out_channels​, kernel_size[0], kernel_size[1]) 
Note that I have omitted the groups parameter in the above shape. 
Now, my question is that why not its shape as follows? 
(out_channels, in_channels, kernel_size[0], kernel_size[1]) 
My problem related to the position of (in_channels, out_channels). As in some resources the Transpo…
  </blockquote>
</aside>


username: ahkarami
cooked: <p><a class="mention" href="/u/apaszke">@apaszke</a> would you please answer my above question about the shape of <strong>weight tensor</strong> in transposed convolution?</p>

username: epignatelli
cooked: <p>It would be great to mention this in the <a href="https://pytorch.org/docs/stable/nn.html#convtranspose2d" rel="nofollow noopener">docs of the <code>nn.ConvTranspose2d object</code></a></p>

username: Joshua_Clancy
cooked: <p>Hello! does anyone know what to do when working with the functional version? f.conv_transpose2d does not have an output_size argument. I need to use the functional version since I am tying weights.</p>

username: ptrblck
cooked: <p><code>output_size</code> is used to get the right <code>output_padding</code> as seen in <a href="https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/conv.py#L774" rel="nofollow noopener">this line of code</a>.<br>
However, as you can see, this method is not exposed, so you could just define the <code>output_padding</code> manually or use this hacky way to invoke the method:</p>
<pre><code class="lang-python">conv = nn.ConvTranspose2d(3, 1, 2, 2, bias=False)
x = torch.randn(1, 3, 10, 10)

# vanilla
output_vanilla = conv(x)
print(output.shape)
&gt; torch.Size([1, 1, 20, 20])

# output_size
output_size = conv(x, output_size=(21, 21))
print(output.shape)
&gt; torch.Size([1, 1, 21, 21])

# functional API
weight = conv.weight.detach().clone()
output_func = F.conv_transpose2d(x, weight, stride=2)
print(output_func.shape)
&gt; torch.Size([1, 1, 20, 20])
print((output_func-output_vanilla).abs().max())
&gt; tensor(0., grad_fn=&lt;MaxBackward1&gt;)

# hacky way to get output padding
output_padding = nn.ConvTranspose2d._output_padding(
    self=None,
    input=x,
    output_size=(21, 21),
    stride=(2, 2),
    padding=(0, 0),
    kernel_size=(2, 2)
)

output_func_size = F.conv_transpose2d(
    x, weight, stride=2, output_padding=output_padding)
print(output_func_size.shape)
&gt; torch.Size([1, 1, 21, 21])
print((output_func_size-output_size).abs().max())
&gt; tensor(0., grad_fn=&lt;MaxBackward1&gt;)
</code></pre>
<p>I’m not sure, why <code>_output_padding</code> is wrapped in a class method and not exposed publicly, so I would rather recommend to calculate the <code>output_padding</code> argument manually without relying on this hacky approach.</p>

username: Joshua_Clancy
cooked: <p>Thanks alot! yeah I’ll start with tweaking the output_padding, but that is a good alternative.</p>

username: mathematics
cooked: <p>Hi would I able to put <code>ConvTranspose2d</code>  in <code>Sequential</code> and adding other layers and writing <code>output_size</code>.<br>
I am working on wgan, where I want to experiment with both different <code>Upsample</code> layer with it,</p>
<p>Like this, I used both layers just to increase image dimensions by <code>2</code></p>
<pre><code class="lang-auto">def upconv(in_channels, out_channels, kernel_size=1, stride=1, padding=0, batch_norm=True, up=False, trans=True):
    layers = []
    # conv_layer = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, padding_mode=padding_mode)
    if up:
        layers.append(nn.Upsample(scale_factor=scale_factor, mode=mode, align_corners=align_corners))
    if trans:
      layers.append(nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding))
      
    if batch_norm:
        layers.append(nn.BatchNorm2d(out_channels))
    return nn.Sequential(*layers)
</code></pre>
<p>but <code>Upsample</code> works , but for <code>ConvTranspose2d</code> I need to write <code>output_size</code> so that it would just double dimension , but It is not possible</p>
<pre><code class="lang-auto">a = torch.randn((1, 32, 16, 16))
upconv(32, 16)(a, output_size=(a.shape[0], a.shape[1], a.shape[2]*2, a.shape[3]*2))
</code></pre>
<p>How can i do this? <img src="https://discuss.pytorch.org/images/emoji/apple/thinking.png?v=9" title=":thinking:" class="emoji" alt=":thinking:"></p>

username: ptrblck
cooked: <p>I think the easiest way would be to create a custom module and pass the <code>output_size</code> to it, if it’s static and should be used in an <code>nn.Sequential</code> container as seen here:</p>
<pre><code class="lang-python">class MyConvTranspose2d(nn.Module):
    def __init__(self, conv, output_size):
        super(MyConvTranspose2d, self).__init__()
        self.output_size = output_size
        self.conv = conv
        
    def forward(self, x):
        x = self.conv(x, output_size=self.output_size)
        return x


conv = nn.ConvTranspose2d(1, 1, 2, 2)
x = torch.randn(1, 1, 24, 24)
out = conv(x)
print(out.shape)
&gt; torch.Size([1, 1, 48, 48])

out = conv(x, output_size=(49, 49))
print(out.shape)
&gt; torch.Size([1, 1, 49, 49])

my_conv = MyConvTranspose2d(conv, output_size=(49, 49))
out = my_conv(x)
print(out.shape)
&gt; torch.Size([1, 1, 49, 49])

model = nn.Sequential(MyConvTranspose2d(conv, output_size=(49, 49)))
out = model(x)
print(out.shape)
&gt; torch.Size([1, 1, 49, 49])
</code></pre>
<p>If you need to set <code>output_size</code> dynamically, I would recommend to write a custom model and not use <code>nn.Sequential</code>.</p>

username: mathematics
cooked: <p>Thanks for great idea. It is working. <img src="https://discuss.pytorch.org/images/emoji/apple/smiley.png?v=9" title=":smiley:" class="emoji" alt=":smiley:"></p>
<pre><code class="lang-auto">class UpOrTrans(nn.Module):
    def __init__(self,in_channels, out_channels, mode='up', kernel_size=1, stride=1, scale_factor=2,  padding=0, batch_norm=True, **kwargs):
        super(UpOrTrans, self).__init__()
        self.mode = mode
        upmode = kwargs.get('upmode', 'nearest')
        align_corners = kwargs.get('align_corners', None)
        self.batch_norm = batch_norm
        
        if self.mode == 'up':
            self.up = nn.Upsample(scale_factor=scale_factor, mode=upmode, align_corners=align_corners)
            self.conv = nn.Conv2d(in_channels, out_channels,  kernel_size, stride, padding)
        
        if self.mode == 'trans':
            self.trans = nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride, padding)
        
        if self.batch_norm:
            self.bn = nn.BatchNorm2d(out_channels)
        
    def forward(self, x, output_size=None):
        if self.mode == 'up':
            x = self.up(x)
            x = self.conv(x)
        if self.mode == 'trans':
            x = self.trans(x, output_size=output_size)
        
        if self.batch_norm:
            x = self.bn(x) 
        return x
</code></pre>