Determining the result of a convolution operation
Following guide from https://medium.com/mlreview/a-guide-to-receptive-field-arithmetic-for-convolutional-neural-networks-e0f514068807 I'm attempting to calculate the number of output features using code below : 

The output of : 

%reset -f

import torch
import torch.nn as nn

my_tensor = torch.randn((1, 16, 12, 12), requires_grad=False)
print(my_tensor.shape)

update_1 = nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1)
print(update_1(my_tensor).shape)


is : 

torch.Size([1, 16, 12, 12])
torch.Size([1, 16, 6, 6])


How is torch.Size([1, 16, 6, 6]) calculated in the context of applying formula :



(taken from https://medium.com/mlreview/a-guide-to-receptive-field-arithmetic-for-convolutional-neural-networks-e0f514068807)

Attempting to calculate the number of output features manually by applying the formula : 

stride = 2
padding = 1
kernel_size = 3

# 2304 as n_in = 1 * 16 * 16 * 12

n_out = ((2304 + (2 * padding) - kernel_size) / stride) + 1

print(n_out)


prints : 1152.5

But the produced number of output features is print(1 * 16 * 6 *6) = 576. I've taken the product of 1,16,6,6 as this is the shape of the result of print(update_1(my_tensor).shape)

Update : 

Based on questions below I've updated code to :

%reset -f

import torch
import torch.nn as nn
from math import floor

stride_value = 2
padding_value = 1
kernel_size_value = 3

number_channels = 3
width = 10
height = 12

my_tensor = torch.randn((1, number_channels, width, height), requires_grad=False)
print(my_tensor.shape)

update_1 = nn.Conv2d(in_channels=number_channels, 
                     out_channels=16, 
                     kernel_size=kernel_size_value, 
                     stride=stride_value, 
                     padding=padding_value)

print(update_1(my_tensor).shape)

n_out = floor((number_channels + (2 * padding_value) - kernel_size_value) / stride_value) + 1
print(n_out)


print(my_tensor.shape) produces : 
torch.Size([1, 3, 10, 12])

print(update_1(my_tensor).shape) produces : 
torch.Size([1, 16, 5, 6])

print(update_1(n_out).shape) produces : 
2

2 does not match the number of output features in each dimension. Have I implemented the calculation correctly ?

As the number of horizontal features produces is 5 and number of vertical features produces is 6 is this formula not applicable where the number of features differ as for an image it does not make sense to have differing x and y axis values length ? 

I see where you confusion is coming from. The formula computes the linear number of outputs, whereas you assume that it operates on the whole tensor.

So the correct code is:

from math import floor

stride = 2
padding = 1
kernel_size = 3

n_out = floor((12 + (2 * padding) - kernel_size) / stride) + 1

print(n_out)


Therefore, it outputs 6 "horizontal" features.
Since the input tensor has the same "vertical" dimension (12), the formula will also produce 6 "vertical" features.
Finally, 16 is the number of output channels you have specified in Conv2d.

Putting it all together, the output is

1 image in a batch,
16 channels,
6 horizontal features, and
6 vertical features,


which totals in 576 features.

UPDATE

By convention, the number of output channels is not calculated by the formula, but provided manually as a second parameter to nn.Conv2d.

Therefore, to correct the second code above:

import torch
import torch.nn as nn
from math import floor

stride_value = 2
padding_value = 1
kernel_size_value = 3

number_channels = 3
width = 10
height = 12

my_tensor = torch.randn((1, number_channels, width, height), requires_grad=False)
print(my_tensor.shape)

update_1 = nn.Conv2d(in_channels=number_channels, 
                     out_channels=16, 
                     kernel_size=kernel_size_value, 
                     stride=stride_value, 
                     padding=padding_value)

print(update_1(my_tensor).shape)

n_out1 = floor((width + (2 * padding_value) - kernel_size_value) / stride_value) + 1
n_out2 = floor((height + (2 * padding_value) - kernel_size_value) / stride_value) + 1
print("(Expected: 5, 6)", n_out1, n_out2)

