# nn.Upsample versus upsampling with transposed convolution

**URL:** <https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813>\
**Category:** vision\
**Created:** [August 28, 2017, 9:18pm UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813 "2017-08-28T21:18:06Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![aitutakiv](https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/a/c68b51/32.png) [@aitutakiv](https://discuss.pytorch.org/u/aitutakiv)\
**Post date:** [August 28, 2017, 9:18pm UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/1 "2017-08-28T21:18:06Z")

</div>

I’m trying to understand the difference between nn.Upsample and upsampling via transposed convolution. I wrote an example upsampling a 2x2 identity matrix both ways - can anyone explain why they are different?

[https://gist.github.com/katerakelly/3fb565f172df3a371f5178d51e8f1039](https://gist.github.com/katerakelly/3fb565f172df3a371f5178d51e8f1039)

For bilinear upsampling, it seems that each output should be a function of \> 1 input, for smoothing purposes. Yet this is not what nn.Upsample does (it preserves original values) - why is this?

---

<div class="post-metadata">

**Author:** ![Jiaming\_Liu](https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/j/cc9497/32.png) [@Jiaming\_Liu](https://discuss.pytorch.org/u/Jiaming_Liu)\
**Post date:** [August 28, 2017, 11:17pm UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/2 "2017-08-28T23:17:23Z")

</div>

nn.Upsample:

Args:  
size (tuple, optional): a tuple of ints ([D\_out], H\_out, W\_out) output sizes  
scale\_factor (int / tuple of ints, optional): the multiplier for the image height / width / depth  
**mode (string, optional): the upsampling algorithm: nearest | bilinear | trilinear. Default: nearest**

---

<div class="post-metadata">

**Author:** ![aitutakiv](https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/a/c68b51/32.png) [@aitutakiv](https://discuss.pytorch.org/u/aitutakiv)\
**Post date:** [August 29, 2017, 6:40am UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/3 "2017-08-29T06:40:53Z")

</div>

I don’t see how this is an answer to my question, as I use “bilinear” mode?

---

<div class="post-metadata">

**Author:** ![longcw](https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/longcw/32/124_2.png) [@longcw](https://discuss.pytorch.org/u/longcw)\
**Post date:** [August 29, 2017, 11:52am UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/4 "2017-08-29T11:52:50Z")

</div>

You can follow the caffe document if you want to use convolution transpose as a bilinear upsampling layer:  
[http://caffe.berkeleyvision.org/doxygen/classcaffe\_1\_1BilinearFiller.html](http://caffe.berkeleyvision.org/doxygen/classcaffe_1_1BilinearFiller.html)

```auto
layer {
  name: "upsample", type: "Deconvolution"
  bottom: "{{bottom_name}}" top: "{{top_name}}"
  convolution_param {
    kernel_size: {{2 * factor - factor % 2}} stride: {{factor}}
    num_output: {{C}} group: {{C}}
    pad: {{ceil((factor - 1) / 2.)}}
    weight_filler: { type: "bilinear" } bias_term: false
  }
  param { lr_mult: 0 decay_mult: 0 }
}

```

I modified your code:

```python
def make_bilinear_weights(size, num_channels):
    ''' Make a 2D bilinear kernel suitable for upsampling
    Stack the bilinear kernel for application to tensor '''
    factor = (size + 1) // 2
    if size % 2 == 1:
        center = factor - 1
    else:
        center = factor - 0.5
    og = np.ogrid[:size, :size]
    filt = (1 - abs(og[0] - center) / factor) * \
           (1 - abs(og[1] - center) / factor)
    print filt
    filt = torch.from_numpy(filt)
    w = torch.zeros(num_channels, 1, size, size)
    for i in range(num_channels):
        w[i, 0] = filt
    return w

# Define a toy grid
x = np.array([[1, 2], [3, 4]], dtype=np.float32)
x = Variable(torch.from_numpy(x[np.newaxis, np.newaxis, :, :]))

# Upsample using Pytorch bilinear upsampling
out1 = F.upsample(x, None, 2, 'bilinear')

# Upsample using transposed convolution
# kernel size is 2x the upsample rate for smoothing
# output will need to be cropped to size
c = x.size(1)
out2 = F.conv_transpose2d(x, Variable(make_bilinear_weights(4, 1)), stride=2, padding=1, groups=c)

output:
Variable containing:
(0 ,0 ,.,.) = 
  1.0000 1.3333 1.6667 2.0000
  1.6667 2.0000 2.3333 2.6667
  2.3333 2.6667 3.0000 3.3333
  3.0000 3.3333 3.6667 4.0000
[torch.FloatTensor of size 1x1x4x4]

Variable containing:
(0 ,0 ,.,.) = 
  0.5625 0.9375 1.3125 1.1250
  1.1250 1.7500 2.2500 1.8750
  1.8750 2.7500 3.2500 2.6250
  1.6875 2.4375 2.8125 2.2500
[torch.FloatTensor of size 1x1x4x4]

```

---

<div class="post-metadata">

**Author:** ![aitutakiv](https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/a/c68b51/32.png) [@aitutakiv](https://discuss.pytorch.org/u/aitutakiv)\
**Post date:** [August 29, 2017, 5:06pm UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/5 "2017-08-29T17:06:53Z")

</div>

Thanks, I see why I had the wrong output size now.  
Can anyone comment on why `out1` and `out2` are different??

---

<div class="post-metadata">

**Author:** ![IssamLaradji](https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/issamlaradji/32/1006_2.png) [@IssamLaradji](https://discuss.pytorch.org/u/IssamLaradji)\
**Post date:** [December 23, 2017, 7:41pm UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/6 "2017-12-23T19:41:53Z")

</div>

The outputs are different between `conv_transpose2d` and `F.upsample` so the code for `make_bilinear_weights` is probably incorrect…

---

<div class="post-metadata">

**Author:** ![ISMAX](https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/ismax/32/16764_2.png) [@ISMAX](https://discuss.pytorch.org/u/ISMAX)\
**Post date:** [February 27, 2021, 7:49pm UTC](https://discuss.pytorch.org/t/nn-upsample-versus-upsampling-with-transposed-convolution/6813/7 "2021-02-27T19:49:09Z")

</div>

The major difference between nn.Upsample and nn.ConvTranspose2d is that **nn.ConvTranspose2d has learnable weights** because it has convolution kernels like nn.Conv2d, whereas nn.Upsample has no learnable weights and just applies a choosen interpolation algorithm ( ‘nearest’ , ‘linear’ , ‘bilinear’ , ‘bicubic’ or ‘trilinear’).  
So I would say that nn.ConvTranspose2d is more powerful because it is “learnable”.

.
