# Understanding the last two Linear Transformations in LeNet-5

I need help with understanding the LeNet-5 CNN:

1. How/Why does FC3 and FC4 have 120 and 84 parameters?
2. How are the filters 6 and 16 chosen? (intuition based on the dataset?)

Everywhere that I have looked, I haven't found an answer to #1, including LeCunn's original paper.

What am I missing?

I am tasked with swapping out the 5 x 5 kernels (f = 5), with 3 x 3 kernels (f = 3). If I understand where those values (especially 84, 120) come from, I think I will be able to do it. I was able to implement LeNet-5 using PyTorch.

If you have any suggestions what values would work best and why, I would be grateful. The dataset is Cifar-10.

Update:

I modified my code, as @Oxbowerce suggested to make to sure the the image size before unflattening matches the image size in the view before activating the first fully connected network:

In my constructor for the class LeNet:

self.fc1 = nn.Linear(4 * kernel_size * kernel_size * 16, 120)  # added 4


In the feed forward network:

x = x.view(-1, 4 * self.kernel_size * self.kernel_size * 16) # added 4


Here is my network class:

class LeNet(nn.Module):

def __init__(self, activation, kernel_size:int = 5):
super().__init__()

self.kernel_size = kernel_size

self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=kernel_size, stride=1)
self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=kernel_size, stride=1)

self.fc1 = nn.Linear(4 * kernel_size * kernel_size * 16, 120)  # added 4
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)

self.activation = activation

def forward(self, x):

x = self.pool(self.activation(self.conv1(x)))
x = self.pool(self.activation(self.conv2(x)))

x = x.view(-1, 4 * self.kernel_size * self.kernel_size * 16) # added 4

x = self.activation(self.fc1(x))
x = self.activation(self.fc2(x))
x = self.fc3(x)

return x



I call the train method with this:

model.train(activation=nn.Tanh(), learn_rate=0.001, epochs=10, kernel_size=3)


Train method:

def train(self, activation:Any, learn_rate:float, epochs:int, momentum:float = 0.0, kernel_size:int = 5) -> None:

self.activation = activation
self.learn_rate = learn_rate
self.epochs = epochs
self.momentum = momentum

self.model = LeNet(activation=activation, kernel_size=kernel_size)
self.model.to(device)

optimizer = torch.optim.SGD(params=self.model.parameters(), lr=learn_rate, momentum=momentum)

for epoch in range(1, epochs + 1):
loss = 0.0
correct = 0
total = 0
predicted = 0

for batch_id, (images, labels) in enumerate(self.train_loader):
images, labels = images.to(device), labels.to(device)

outputs = self.model(images)

# Calculate accurracy
predicted = torch.argmax(outputs, 1)
correct += (predicted == labels).sum().item() # <--- exception
total += labels.size(0)

loss.backward()
optimizer.step()

accuracy = 100 * correct / total
self.train_accuracy.append(accuracy)
self.train_error.append(error)
self.train_loss.append(loss.item())

self.test()

for i in self.model.parameters():
self.params.append(i)



Here is the stack trace of the error:

Error
Traceback (most recent call last):
File "/usr/lib/python3.9/unittest/case.py", line 59, in testPartExecutor
yield
File "/usr/lib/python3.9/unittest/case.py", line 593, in run
self._callTestMethod(testMethod)
File "/usr/lib/python3.9/unittest/case.py", line 550, in _callTestMethod
method()
File "/home/steve/workspace_psu/cs510nlp/hw2/venv/lib/python3.9/site-packages/nose/case.py", line 198, in runTest
self.test(*self.arg)
File "/home/steve/workspace_psu/cs510dl/hw2/test_cs510dl_hw2.py", line 390, in test_part2_relu_cel_k3
model.train(activation=activation, learn_rate=learn_rate, momentum=momentum, epochs=epochs, kernel_size=kernel_size)
File "/home/steve/workspace_psu/cs510dl/hw2/cs510dl_hw2.py", line 389, in train
correct += (predicted == labels).sum().item()
File "/home/steve/workspace_psu/cs510nlp/hw2/venv/lib/python3.9/site-packages/torch/tensor.py", line 27, in wrapped
return f(*args, **kwargs)
Exception: The size of tensor a (16) must match the size of tensor b (4) at non-singleton dimension 0


• It depends on for which layers you want to change the kernel size, if it just for the convolutional layers you would only have to change the kernel sizes for self.conv1 and self.conv2. Feb 8 at 12:34
• They do not need to be equal, it is just that in this case they are equal (i.e. image size before unflattening is 5x5 and the kernel size for the convolutional layers is also 5x5). That is why using kernel_size to calculate the number of neurons in the first fully connected layer works, this won't work if you change the kernel_size to 3. Feb 8 at 14:20