# Convolution Arithmetic

The relationship between input size, kernel size, stride, padding and dilation that determines an output's spatial dimensions.

**Rests on:** Stride and Padding, Tensors and Shapes

**Leads to:** Convolutional Layers, Receptive Fields, Scattering Transforms

*Deep Learning · Convolutional Networks*


## Why learn this

This is the arithmetic that decides the shape of every image in a neural network that looks at pictures. Master it and you can read any architecture diagram, debug the most common error in vision models, and understand why a network's layers shrink or stay the same. It unlocks the next steps: Convolutional Layers, Stride and Padding, Receptive Fields, and Pooling as a Statistic — and from there, whole systems like face recognition, medical imaging, and the vision parts of multimodal AI.


## The intuition

Imagine a photograph. A convolutional network doesn't look at the whole photo at once; it slides a small window across it, looking at one patch at a time. That window is the kernel. The size of the output depends on three decisions: how big the window is, how far it jumps between patches, and whether we add extra border values so the window can reach the edges. Convolution arithmetic is just tracking this sliding process: start at the top-left, place the window, record the position, move by the stride, repeat until you run out of space. The output's height and width are simply how many times you managed to place the window. Padding lets you start off the edge by adding blank space around the picture, so the window can cover the corners. Dilation stretches the window, skipping pixels inside it, so it sees a wider area without growing in size. All of this is arithmetic: multiply, add, subtract, divide — nothing more.


## An analogy

Think of a concierge at a hotel with a long row of rooms on one floor. The concierge walks along the corridor, checking each room for a 'Do Not Disturb' sign. The window is how many rooms he can see at once — say, three. The stride is how many steps he takes between checks — say, two. Padding is putting dummy 'empty' rooms at the ends of the corridor so he can check the very first and last real rooms properly. Dilation is a trick: he skips a room inside his view, so he effectively sees a wider spread without physically looking at more rooms. The number of checks he makes is the output size. Convolution arithmetic is just counting how many checks fit along that corridor. The analogy breaks down because in a real photo the window moves in two dimensions (height and width), but the counting is the same in each direction. Also, the concierge's checks are quick and simple — each check in a convolution multiplies and adds numbers, but that's the next step, not this one. Here, we only care about counting.


## Definition

Convolution arithmetic is the set of rules that determine the height and width of a feature map after a convolutional layer, given the input size, kernel size, stride, padding, and dilation.


## How it connects

You haven't studied other topics yet, so this stands on its own. But note: convolution arithmetic is the foundation for the Convolutional Layers that use these numbers to actually compute outputs, and it directly sets how Receptive Fields grow as you stack layers. A network's architecture diagram is full of 'input 32x32, kernel 3x3, stride 1, padding same → output 32x32' — you will be able to read those lines fluently.


## The mathematics

The mathematics we need here is simple arithmetic: adding, subtracting, multiplying, and dividing whole numbers. The core problem is this: if you have a line of positions and a window that covers a fixed number of them, how many times can you place the window without it sticking out the end?

To answer this, we look at four settings:
1. Input size (H_in): The total number of positions in the original line.
2. Kernel size (K): How many positions the window covers at once.
3. Padding (P): Extra empty positions added to the left and right of the input. This effectively makes the line longer before we start sliding the window.
4. Stride (S): How many positions we jump forward each time we place the window.
5. Dilation (D): The gap between the points inside the window. A dilation of 1 means the points are next to each other. A dilation of 2 means there is one empty spot between them.

First, we calculate the effective length of the window. If the dilation is greater than 1, the window takes up more space than its kernel size suggests. The formula for the effective window span is:
Effective Span = D * (K - 1) + 1

For example, if K=3 and D=2, the window covers positions at indices 0, 2, and 4 relative to its start. The total space it occupies from the first point to the last is 5 units (indices 0 through 4). So, Effective Span = 2 * (3 - 1) + 1 = 5.

Next, we account for padding. Padding adds P positions to the left and P positions to the right. The total length of the padded input is:
Padded Length = H_in + 2 * P

Now we determine how many windows fit. We start at position 0. The first window ends at Effective Span - 1. The next window starts at S, then 2*S, and so on. A window starting at position 'start' is valid only if it does not extend beyond the Padded Length. That is:
start + Effective Span <= Padded Length

The largest integer start that satisfies this is found by subtracting the effective span from the padded length, then dividing by the stride, and rounding down to the nearest whole number. This gives us the index of the last valid window (0-indexed). To get the count of windows, we add 1.

So the formula for the output size (H_out) is:
H_out = floor( (Padded Length - Effective Span) / S ) + 1

Substituting our terms back in:
H_out = floor( (H_in + 2*P - (D*(K-1) + 1)) / S ) + 1

This can be rewritten slightly for clarity by distributing the negative sign inside the parenthesis:
H_out = floor( (H_in + 2*P - D*(K-1) - 1) / S ) + 1

The 'floor' function means we round down to the nearest whole number. This is necessary because you cannot place a window a fraction of a time.

The same logic applies to width (W_in, W_out) using the horizontal kernel size and stride.


## A worked example

We have a one-dimensional input of length 8. Imagine 8 positions in a row, numbered 0 through 7.

Settings:
- Input size (H_in) = 8
- Kernel size (K) = 3
- Stride (S) = 2
- Padding (P) = 0 (no extra positions)
- Dilation (D) = 1 (standard window, no gaps inside)

Step 1: Calculate the effective span of the window.
Effective Span = D * (K - 1) + 1
Effective Span = 1 * (3 - 1) + 1 = 1 * 2 + 1 = 3.
The window covers 3 positions.

Step 2: Calculate the padded length.
Padded Length = H_in + 2 * P
Padded Length = 8 + 2 * 0 = 8.
The total available space is 8 positions.

Step 3: Apply the formula for output size.
H_out = floor( (Padded Length - Effective Span) / S ) + 1
H_out = floor( (8 - 3) / 2 ) + 1
H_out = floor( 5 / 2 ) + 1

Step 4: Perform the division.
5 divided by 2 is 2.5.

Step 5: Round down (floor).
The floor of 2.5 is 2.

Step 6: Add 1.
H_out = 2 + 1 = 3.

Verification by hand:
- Window 1 starts at position 0. It covers positions 0, 1, 2. This is valid because 2 < 8.
- Move by stride 2. Next start is position 2. It covers positions 2, 3, 4. This is valid because 4 < 8.
- Move by stride 2. Next start is position 4. It covers positions 4, 5, 6. This is valid because 6 < 8.
- Move by stride 2. Next start is position 6. It covers positions 6, 7, 8. Position 8 does not exist (indices are 0-7). So this placement is invalid.

We successfully placed the window at starts 0, 2, and 4. That is 3 placements.

Result: With input 8, kernel 3, stride 2, no padding, no dilation, the output length is 3.


## From scratch, in Python

```python
def conv_output_size(input_size, kernel_size, stride=1, padding=0, dilation=1):
    return (input_size + 2 * padding - dilation * (kernel_size - 1) - 1) // stride + 1

# Example from the maths section
print(conv_output_size(8, 3, stride=2))  # 3

# Same padding: output equals input
print(conv_output_size(8, 3, stride=1, padding=1))  # 8

# Valid padding (no padding) shrinks
print(conv_output_size(8, 3))  # 6

# Dilation example: kernel size 3, dilation 2 (effective kernel 5)
print(conv_output_size(8, 3, dilation=2))  # 4
```


## The same thing, with a framework

```python
import numpy as np

# NumPy doesn't provide a direct function, but we can compute the shape.
# If you have an input array of shape (1, 1, 8, 8) and a kernel of size 3,
# stride 2, padding 0, the output shape would be:
input_shape = (1, 1, 8, 8)  # batch, channels, height, width
kernel_size = 3
stride = 2
padding = 0

out_h = (input_shape[2] + 2 * padding - kernel_size) // stride + 1
out_w = (input_shape[3] + 2 * padding - kernel_size) // stride + 1
print(out_h, out_w)  # 3 3

# In PyTorch, you'd use torch.nn.Conv2d, but it computes this internally.
# Here's how to check the shape:
# import torch
# conv = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=2, padding=0)
# x = torch.randn(1, 1, 8, 8)
# out = conv(x)
# print(out.shape)  # torch.Size([1, 1, 3, 3])
```


## What the code does

We define a function called `conv_output_size` that takes the input length, kernel size, and optional stride, padding, and dilation. The `//` operator does integer division, which rounds down — that's the floor in the formula. The function returns the computed output size. Then we call it with a few combinations to see the pattern. Note: in Python, `//` is the floor division operator, and `*` is multiplication, `+` and `-` are addition and subtraction. The function is reusable, which is why we define it once and call it three times.


## Common misconceptions

**Kernel size and output size are the same thing** — If I use a 3x3 kernel, the output will also be 3x3.

The kernel is a window, not the output. The output is how many times the window fits across the input. In fact: The output size depends on input size, kernel, stride, padding, and dilation. A 3x3 kernel with stride 1 and no padding on an 8x8 input gives a 6x6 output.

**Padding is only for aesthetics** — Padding just makes the image look bigger; it doesn't change the arithmetic.

Padding adds extra positions that allow the window to cover the edges, directly changing the output size. In fact: Padding adds blank values around the border, allowing the kernel to cover edge pixels and preserving spatial dimensions (same padding) or reducing shrinkage.

**Stride doesn't affect output size, only speed** — Stride makes the network faster, but the output stays the same size.

Stride is how far the window jumps. Larger stride means fewer placements, so smaller output. In fact: Stride greater than 1 downsamples the output. For example, stride 2 halves the output size (approximately).

**Output size must always be a whole number** — The formula should always give an exact integer; if it doesn't, I made a mistake.

If the input doesn't divide evenly by stride, you can't place the window a fractional number of times. The formula rounds down. In fact: The output size is always an integer because you round down (floor) any fractional result. That's why the formula uses floor division.

**Dilation is the same as using a larger kernel** — Dilation 2 with a 3x3 kernel is the same as a 5x5 kernel.

Dilation spreads the kernel elements apart, but the kernel still has 3 weights. A 5x5 kernel has 25 weights. The receptive field is the same, but the number of parameters is different. In fact: Dilation increases the spacing between kernel elements, enlarging the receptive field without adding more weights. A 3x3 kernel with dilation 2 covers a 5x5 area but still has only 9 parameters.

**The formula only works for square images** — I only know how to compute output size for squares; rectangles confuse me.

The formula applies independently to height and width. In fact: Compute output height using input height, and output width using input width. They don't have to be equal.

**Padding can be any arbitrary number** — I can set padding to 10 for a 3x3 kernel and it'll work fine.

Padding adds zeros around the border. If padding is too large, the window will mostly see zeros, which may harm performance. In fact: Padding is a hyperparameter, but it's typically set to 'same' (padding = (kernel-1)//2 for stride 1) to preserve size, or to zero for valid convolution. Extreme padding values are unusual.


## In short

Convolution arithmetic tells you how many times a sliding window fits across an input. The output size = floor((input + 2*padding - dilation*(kernel-1) - 1) / stride) + 1. Master this formula to read and design any convolutional network, and to avoid the most common shape error in vision models.


## Where this is used

This exact arithmetic appears in every convolutional neural network. In computer vision, you design a network by specifying kernel sizes, strides, and paddings for each layer; the output size of each layer is computed with this formula. If you get it wrong, you'll get a shape mismatch error when trying to connect layers. In practice, developers use libraries like PyTorch or TensorFlow, but they still need to know this arithmetic to design architectures and debug. For example, a common design choice is 'same' padding, which sets padding so the output size equals the input size. This is done by setting padding = (kernel_size - 1) // 2 for stride 1. Without this, every layer would shrink the image until it disappears. In large language models, convolution is less common, but it appears in some architectures for processing text in a sliding-window fashion. In small models running on devices like phones, convolution arithmetic directly impacts memory and speed: smaller outputs mean fewer computations and less memory, so engineers often use stride 2 to downsample. In retrieval-augmented generation, convolution is rarely used, but the idea of sliding windows appears in some embedding techniques. In agentic systems, convolution arithmetic is not directly used, but the same principle of counting steps appears in state-space models. In everyday applied ML, this is everywhere: face detection, medical image segmentation, self-driving car perception. A developer using a pre-built model might not need to calculate output sizes, but when designing a custom architecture, they absolutely do. The most common error is off-by-one due to forgetting the +1 or using the wrong padding. Tools like PyTorch's 'same' padding handle it, but understanding the formula is essential for debugging shape errors.


---

Read this on the web: https://basavapurushottam.com/learn-ai/convolution-arithmetic
