# PyTorch > nn.Conv2d:computation of number of features output from nn.Conv2d

I have x = nn.linear() following x=conv2d()

I understand in_features of linear() must be calculated from x. by calculating channels * height * width of x.

I know from, the documentation for conv2d, this stuff I dont get:

I dont follow this post on nn.Conv2d output comptation), but it says you can get dimensions of x with x.shape.

I still dont understand how to apply those equations from the documentation of nn.conv.

I know:

1. The input features are the pixel size (i.e.: h*w) of the image * channels and
2. pixel size (H*W) of your image after applying Conv2d comes from the above equations, but I dont understand it. Are we just defining notation in the input: and output: parts?

Then it gives formulas for H_out and W_out, the height and width of the image output from conv2d based on H_in and W_in, the height and width of image input to conv2D, respectively. I don’t understand how to apply those equations.

1. how do I get H_in and W_in? Would those be x and x based on the assumptions that what comes after input: and ouput: are the dimensions of x. I.e.: N and the number of channels are the first two dimensions.

Assuming:

• we are using x as it is after the line just before linear() and
• input and output above show conv2D’s input and output orders of dimensions and the notation for the equations.
1. Are padding, dilation, and kernel_size taken from the parameters values of the preceding conv2D call? If the preceding call is:
`self.conv2 = nn.Conv2d(6, 16, 5) `
This means: `self.conv2 = nn.Conv2d(in_channels = 6, out_channels = 16, kernel_size = 5)` kernel size is 5, what is kernel_size and where to I get padding and dilation?

Then, say we have a call to nn.Linear afterward. It’s in_features parameter would be C * H_{out} * W_{out}, where:
C = the out_channels parameter used in the prior conv2D call?

The current sad state of the code:

``````class Net(nn.Module):
def __init__(self):
super().__init__()
# Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
self.conv1 = nn.Conv2d(in_channels = 1, out_channels = 6, kernel_size = 5)

self.conv2 = nn.Conv2d(in_channels = 6, out_channels = 16, kernel_size = 5)

self.pool = nn.MaxPool2d(2, 2)
# To decide the number of input features your function can take, you first need to figure out what is the output pixel value of your final conv2d.
# Let's say your final conv2d function returns a 3x3 pixel image with output channels as 50, in such case the input features to your linear function should be 3x3x50
# and you can have any number of output features.

#calculate H & W of self.conv2 output from
#I GOT STUCK HERE. I WENT TO TUESDAY OFFICE HOURS 5 MIN LATE AND NO ONE WAS THERE
#I DIDN'T GET AN ANSWER TO MY POST #1032 ABOUT POST #815
#         print(self.conv2.shape)
#       It seems self.conv2 has no shape attribute.
#         H_in =
#         H =
#         W =
#Linear(in_features, out_features, bias=True, device=None, dtype=None)
#self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc1 = nn.Linear(16 * H * W, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)

def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # flatten all dimensions except batch
x = F.relu(self.fc1(x)) #relu is an activation function
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
``````

Yes, the spatial dimensions (height and width) defined in dim2 and dim3, respectively for the expected 4-dimensional input in the shape `[batch_size, channels, height, width]`.

Yes

If you specify the `kernel_size` with a single `int`, the height and width of the kernel will be set to this value. `kernel_size` and also `kernel_size` will have a value of 5. The docs also explain this behavior.

If you don’t specify these values explicitly, the default values will be used also as given in the docs.

Yes, that’s correct.

Thank you!

In
`def __init__(self):` I have …

``````        self.conv2 = nn.Conv2d(in_channels = 6, out_channels = 16, kernel_size = 5)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
``````

This last line above is nn.Linear(16 * h * w, 120) and I need h & w?
, but self.conv2.shape doesn’t exist so self.conv2 doesn’t give me the height of an image.
`print(self.conv2.shape)` I get:
`'Conv2d' object has no attribute 'shape'`

For your parameters (since you are not giving values for padding, stride and dilation the default are used) the equation would look like this If you want to use this approach instead of solving the equation

Then you need to pass something through the `conv` to see what is happening.

``````# Example
conv2 = torch.nn.Conv2d(in_channels = 6, out_channels = 16, kernel_size = 5)
img = torch.rand(1, 6, 50, 50)

print(conv2(img).shape)

# Output:
# torch.Size([1, 16, 46, 46])
``````

Which is the same result that we got from the formula.

As @Matias_Vasquez described you could either calculate the activation shape manually or pass an input to the model to print the actual shape.
E.g. in your model you would most likely have a few conv, pool, etc. layers before the first linear layer is used.
If you don’t want to calculate the `in_features`, set this argument to any random value (e.g. `1`), pass the input tensor to the model, and print the activation shape before passing it to the linear layer:

``````x = self.conv(x)
x = x.view(x.size(0), -1)
print(x.shape)
x = self.fc(x) # will raise a shape mismatch here
``````

Then use the printed shape of dim1 to set the `in_features`.
Alternatively, you could also use the lazy layers e.g. via `nn.LazyLinear` to let this layer calculate the `in_features` for you.

1 Like

I was trying to print the shape of conv2 object to see how I can get H_in.

Are you saying I should literally copy/paste that somewhere in my existing code? In the construction of the net class? I’m not sure how or where to incorporate this into my existing code.

This isn’t possible as the conv layer can work on a variable input shape as long as the spatial shape of the input is large enough for the current conv setup.
The output shape thus depends on the input shape and the conv setup. The conv layer alone will not define the output shape.

Sorry guys I’m so lost. I dont really understand this stuff and was coasting despite that by asking questions and getting the coding. Nothing about NN made sense to me.

In the code in my question, this line gives me a matrix multiplication error:
self.fc1 = nn.Linear(16 * 5 * 5, 120).
I’m told the first parameter is wrong, but can be calculated.
The calculation include h_in, which I dont know how to get.
What’s the easy way to get an in_features that will run?

Like this?
`self.fc1 = nn.Linear(nn.LazyLinear, 120)`

`module 'torch.nn' has no attribute 'LazyLinear'`

See my previous post with a code snippet which shows how to print the activation shape.

No, `nn.LazyLinear` is a layer, not an argument of `nn.Linear.

``````self.fc1 = nn.LazyLinear(120)
``````

should work.

`self.fc1 = nn.LazyLinear(120)`
`module 'torch.nn' has no attribute 'LazyLinear'`

You might need to update your PyTorch version to be able to use this layer, as your version seems to be too old.

This code is all going inside

`class Net(nn.Module):`
with a

``````def __init__(self):
super().__init__()
``````

part and a
`def forward(self, x):`
part.

I had this in the constructor(?), `def __init__(self):` part:

``````self.conv2 = nn.Conv2d(in_channels = 6, out_channels = 16, kernel_size = 5)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
``````

You are suggesting I modify

``````x = self.conv(x)
x = x.view(x.size(0), -1)
print(x.shape)
x = self.fc(x) # will raise a shape mismatch here
``````

to work in this context.

It almost looks like your syntax is different. How does the code in the model/class definition correspond to your code? The class has

self.someName = someFunction(someParameters)
self.someName1 = someFunction1(someParameters1)

x = self.someName(x)
x = self.someName1(x)

I dont know how to translate between them.

I’m just trying to adapt the code in
https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#define-a-convolutional-neural-network
to work with MNIST input which is just one channel greyscale instead of 3 like CIFAR10.

My code should be used in the `forward` method, not the `__init__`.
As you can see in the tutorial, the `__ini__` initializes the modules, while the `forward` uses them.

I dont know how or if that’s possible or allowed:
I have these imports:

``````%matplotlib inline

import torch
import torchvision
import torchvision.transforms as transforms

import matplotlib.pyplot as plt
import numpy as np

import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import random
import numpy as np

import os
``````

I have the constructor:

``````    def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
``````

I’m getting a matrix multiplication error because
self.fc1 = nn.Linear(16 * 5 * 5, 120) has the wrong number of input features.
How do I find the number of input features using these H_out, H_in equations or otherwise work around the error? Dont I need to replace “16 * 5 * 5” with something?

The full model definitions is:

``````class Net(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)

def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # flatten all dimensions except batch
x = F.relu(self.fc1(x)) #relu is an activation function
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x

net = Net()
``````

In this context, could I and how would I apply your suggestion to:

I dont see how you get the result from the equations. You are still missing H_in. How do I get that when I have:

``````class Net(nn.Module):
def __init__(self):
super().__init__()
#self.conv1 = nn.Conv2d(3, 6, 5)
self.conv1 = nn.Conv2d(1, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)

def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # flatten all dimensions except batch
x = F.relu(self.fc1(x)) #relu is an activation function
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x

net = Net()
``````

As you know, I am seeing a shape mismatch error in the first linear layer, a matrix multiplication error of (a x b) @ (c x d), but b != c.

I don’t see where or how to get height_in and width_in for the functions to calculate the `in_features` manually.

My environment or something does not recognize LazyLinears when I replace
`self.fc1 = nn.Linear(16 * 5 * 5, 120)`
with
`self.fc1 = nn.LazyLinears(120)`.

You can print the shape of `x` right before you feed it to your first linear layer.

``````print(x.shape)
``````

As @ptrblck said, the forward method uses your layers when you pass something through your network.

For debugging purposes you can add a print statement in this forward method so that you get an idea of what is happening before your get your error.

So the `H_in` would refer to the height of the input shape for each layer. This means that `H_out` from conv1 would be the same as `H_in` for conv2.

Also, the shapes are in the format `BxCxHxW` = `Batch x Channel x Height x Width`.