Conv2d Output Size Calculator

By · Last updated:

Conv2d Output Size Calculator (N, C, H, W)

Pick a layer type and enter the input size, channels, kernel, stride, padding and dilation. You get the output shape, the trainable parameters and the number of activations, with a diagram and the formula worked through. Add layers to the stack to follow a tensor through a whole network.

Examples:
Kernel, stride, padding, dilation and output_padding take one number (3) or height then width (3 5).
Layer stack:
Next step: Once every layer’s shape lines up, count the weights in the whole network, including the dense layers that follow the flatten. Then work out how many training steps an epoch takes for your dataset and batch size.

Conv2d output size is the height and width of the feature map a convolution layer produces: floor((input + 2 × padding − dilation × (kernel − 1) − 1) / stride) + 1 for each dimension. This conv2d output size calculator returns the full output shape (N, C, H, W), the layer’s parameter count and the number of activations for Conv2d, pooling and ConvTranspose2d layers.

Why does PyTorch say “mat1 and mat2 shapes cannot be multiplied”?

That error almost always comes from the first nn.Linear after the convolutions. The layer was created with an in_features value that someone guessed or copied, and the flattened feature map arriving at it has a different length. A message such as mat1 and mat2 shapes cannot be multiplied (64x256 and 400x120) reads: a batch of 64 vectors of length 256 met a weight matrix that expects length 400.

The rule being broken is the ordinary one for a matrix product: the columns of the first matrix must equal the rows of the second. The fix is to know the shape before running anything. Each convolution and pooling layer changes height and width in a predictable way, so the flattened size, channels × height × width, can be worked out on paper or with the layer stack above.

What is the Conv2d output size formula?

\[ H_{out} = \left\lfloor \frac{H_{in} + 2p – d\,(k-1) – 1}{s} \right\rfloor + 1 \]

\(H_{in}\) is the input height, \(k\) the kernel size, \(s\) the stride, \(p\) the padding added to each side and \(d\) the dilation. The brackets mean round down. Width uses the same formula with its own values, and pooling layers follow the identical rule. This is the expression given in the PyTorch documentation for torch.nn.Conv2d.

Channels play no part in height or width. A convolution sets the output channels to whatever you ask for, and pooling leaves them unchanged. Channels do decide the number of trainable parameters:

\[ \text{parameters} = \left( \frac{k_h\, k_w\, C_{in}}{\text{groups}} + 1 \right) C_{out} \]

The + 1 is the bias for each output channel; drop it when bias=False. Pooling has no parameters.

Worked example: a 7 × 7 stride-2 convolution on a 224 × 224 image

The calculator loads with the first layer of a ResNet: 3 input channels, 64 output channels, kernel 7, stride 2, padding 3, dilation 1.

\[ H_{out} = \left\lfloor \frac{224 + 2\cdot 3 – 1\cdot(7-1) – 1}{2} \right\rfloor + 1 = \lfloor 111.5 \rfloor + 1 = 112 \]

Width is the same, so a batch of one image goes from (1, 3, 224, 224) to (1, 64, 112, 112). That is 64 × 112 × 112 = 802,816 activations, or about 3.06 MiB as 32-bit floats. The parameter count is (7 × 7 × 3 + 1) × 64 = 148 × 64 = 9,472. Torchvision’s ResNet switches the bias off because batch normalisation follows, which leaves 9,408. To total a full model, dense layers included, use the neural network parameter calculator.

What do common layer settings do to a 224 input?

Most architectures reuse a handful of configurations. Each row below applies the formula to a 224-pixel side.

LayerKernelStridePaddingDilationOutput on 224Effect
Conv 1 × 11101224Keeps size; changes channels only
Conv 3 × 33111224Keeps size
Conv 5 × 55121224Keeps size
Conv 3 × 3, no padding3101222Loses k − 1 = 2 pixels
Conv 3 × 3, dilated3122224Keeps size with a 5-wide span
Conv 3 × 3, strided3211112Halves
Conv 7 × 7 stem7231112Halves
Pool 2 × 22201112Halves
Pool 3 × 33211112Halves
Patch embedding 16 × 161616011414 × 14 = 196 patches
ConvTranspose 2 × 22201448Doubles
ConvTranspose 4 × 44211448Doubles

Why is the result rounded down?

The kernel only produces an output where it fits entirely inside the padded input. If the stride does not divide the remaining length exactly, the leftover rows and columns at the bottom and right are skipped. In the worked example 223 / 2 = 111.5, so one padded row and one padded column are never read.

A consequence is that different inputs can give the same output. With kernel 3, stride 2 and padding 1, inputs of 27 and 28 both produce 14. Pooling layers in PyTorch accept ceil_mode=True to round up instead; this calculator uses the default, which rounds down.

How do “same” and “valid” padding differ in PyTorch and Keras?

“Valid” means no padding in both libraries, so the output shrinks by d(k − 1) at stride 1. “Same” is where they part ways.

In PyTorch, padding is normally a number that is applied to both sides of each dimension. The string padding='same' is accepted only when the stride is 1; it pads a total of d(k − 1), and when that total is odd the extra cell goes on the bottom and right. The output then equals the input size.

In Keras, padding="same" works with any stride and gives an output of ceil(input / stride). TensorFlow computes the total padding needed for that size and, when it is odd, also puts the extra cell on the bottom and right. For kernel 3, stride 2 on a 224 input, Keras pads 0 on top and 1 at the bottom, while PyTorch with padding=1 pads 1 on both. Both give 112, but the windows sit one pixel apart, which matters when porting weights between frameworks. Keras also orders tensors as (N, H, W, C) by default, where PyTorch uses (N, C, H, W).

What does dilation change?

Dilation spreads the kernel taps apart without adding weights. A kernel of size k with dilation d spans d(k − 1) + 1 cells, so a 3 × 3 kernel with dilation 2 covers a 5 × 5 area using 9 weights. On a 32-pixel input with no padding it gives 32 − 4 = 28, exactly what a 5 × 5 kernel would give. To keep the size at stride 1, set the padding to d(k − 1) / 2.

Why do transposed convolutions need output_padding?

\[ H_{out} = (H_{in}-1)\,s – 2p + d\,(k-1) + p_{out} + 1 \]

A transposed convolution undoes the size change of a convolution with the same settings, and \(p_{out}\) is the output_padding argument. Because of the floor, a strided convolution maps several input sizes to one output size, so the reverse direction is ambiguous. Kernel 3, stride 2, padding 1 turns both 27 and 28 into 14. Going back from 14 the formula gives 27; setting output_padding=1 adds one row and column on one side and gives 28. It only chooses among the valid sizes, so it must be smaller than the stride or the dilation.

Where is the output shape needed in practice?

Sizing the classifier head. LeNet-style code written for 32 × 32 images ends in a 16 × 5 × 5 = 400 feature vector. Feed it 28 × 28 MNIST digits and the same layers produce 16 × 4 × 4 = 256, which is the mismatch quoted at the top of this page. The LeNet-5 stack preset traces it: 28 to 24 to 12 to 8 to 4. The final linear layer then produces the scores that go through the softmax function and into the cross-entropy loss.

Skip connections. A U-Net concatenates encoder and decoder feature maps, which must match exactly. An odd size breaks this: 75 pools to 37, and a 2 × 2 stride-2 transposed convolution brings it back to 74, not 75.

Common mistakes

  • Counting padding once. The value is added to both sides, so padding 3 adds 6 to the height and 6 to the width.
  • Forgetting the floor. Dividing 223 by 2 and rounding to 112 before adding 1 gives 113, which is wrong.
  • Hard-coding in_features. The flattened size depends on the input resolution. Changing the image size changes it, even though every convolution still runs.
  • Assuming pooling defaults. In nn.MaxPool2d the stride defaults to the kernel size, not to 1.
  • Using padding = same with a stride. PyTorch raises an error; Keras silently returns ceil(input / stride).
  • Groups that do not divide the channels. Both the input and output channel counts must be multiples of groups. A depthwise 3 × 3 layer on 32 channels has groups = 32 and only (3 × 3 × 1 + 1) × 32 = 320 parameters.

How do you check the output shape in PyTorch?

Pass a dummy tensor through the layer and print the shape. This reproduces the worked example and the LeNet stack:

import torch
import torch.nn as nn

def conv_out(size, k, s=1, p=0, d=1):
    return (size + 2 * p - d * (k - 1) - 1) // s + 1

print(conv_out(224, 7, s=2, p=3))                    # 112

conv = nn.Conv2d(in_channels=3, out_channels=64,
                 kernel_size=7, stride=2, padding=3)
x = torch.randn(1, 3, 224, 224)
print(conv(x).shape)                                 # torch.Size([1, 64, 112, 112])
print(sum(p.numel() for p in conv.parameters()))     # 9472

net = nn.Sequential(
    nn.Conv2d(1, 6, 5), nn.MaxPool2d(2, 2),
    nn.Conv2d(6, 16, 5), nn.MaxPool2d(2, 2),
    nn.Flatten(),
)
print(net(torch.randn(1, 1, 28, 28)).shape)          # torch.Size([1, 256])

up = nn.ConvTranspose2d(16, 8, kernel_size=3, stride=2,
                        padding=1, output_padding=1)
print(up(torch.randn(1, 16, 7, 7)).shape)            # torch.Size([1, 8, 14, 14])

The Keras equivalent of the first layer is keras.layers.ZeroPadding2D(3) followed by keras.layers.Conv2D(64, 7, strides=2, padding="valid"), which returns (None, 112, 112, 64) for a (224, 224, 3) input.

Frequently asked questions

What is the formula for Conv2d output size?

Output size equals floor((input + 2 times padding – dilation times (kernel – 1) – 1) / stride) + 1, applied to height and width separately. A 224 pixel input with kernel 7, stride 2 and padding 3 gives floor(223 / 2) + 1 = 112.

How do I keep the output the same size as the input?

Use stride 1 and padding equal to dilation times (kernel – 1) divided by 2. For a 3 by 3 kernel that is padding 1, for 5 by 5 it is padding 2, and for 7 by 7 it is padding 3. In PyTorch, setting padding to the string same does this automatically for stride 1.

Does the number of channels change the output height and width?

No. Height and width depend only on the input size, kernel, stride, padding and dilation. The channel counts set the depth of the output and the number of parameters, which is (kernel height times kernel width times input channels divided by groups, plus 1 for the bias) times output channels.

Why does my first Linear layer fail with a shape error after the convolutions?

The in_features of the Linear layer must equal channels times height times width of the last feature map. If the network was written for a 32 by 32 image and receives 28 by 28, the flattened size changes from 400 to 256 and PyTorch reports that mat1 and mat2 shapes cannot be multiplied.

Related calculators

The deep learning calculators hub lists every tool for sizing and training networks. These follow on most directly from a layer’s output shape: