Bird
Raised Fist0
PyTorchml~20 mins

Linear (fully connected) layers in PyTorch - ML Experiment: Train & Evaluate

Choose your learning style10 modes available
Experiment - Linear (fully connected) layers
Problem:You are training a simple neural network with one linear (fully connected) layer to classify handwritten digits from the MNIST dataset.
Current Metrics:Training accuracy: 95%, Validation accuracy: 80%, Training loss: 0.15, Validation loss: 0.45
Issue:The model is overfitting: training accuracy is high but validation accuracy is much lower.
Your Task
Reduce overfitting so that validation accuracy improves to at least 88% while keeping training accuracy below 93%.
You can only modify the model architecture by adding or adjusting linear layers and dropout layers.
Do not change the dataset or optimizer settings.
Keep the training epochs and batch size the same.
Hint 1
Hint 2
Hint 3
Solution
PyTorch
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# Define the improved model with two linear layers and dropout
class SimpleNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(28*28, 128)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.3)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = x.view(-1, 28*28)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.dropout(x)
        x = self.fc2(x)
        return x

# Data loading
transform = transforms.ToTensor()
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
val_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False)

# Initialize model, loss, optimizer
model = SimpleNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

def train():
    model.train()
    total_correct = 0
    total_loss = 0
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        total_loss += loss.item() * images.size(0)
        _, predicted = torch.max(outputs, 1)
        total_correct += (predicted == labels).sum().item()
    return total_loss / len(train_loader.dataset), total_correct / len(train_loader.dataset) * 100

def validate():
    model.eval()
    total_correct = 0
    total_loss = 0
    with torch.no_grad():
        for images, labels in val_loader:
            outputs = model(images)
            loss = criterion(outputs, labels)
            total_loss += loss.item() * images.size(0)
            _, predicted = torch.max(outputs, 1)
            total_correct += (predicted == labels).sum().item()
    return total_loss / len(val_loader.dataset), total_correct / len(val_loader.dataset) * 100

# Training loop
for epoch in range(10):
    train_loss, train_acc = train()
    val_loss, val_acc = validate()
    print(f"Epoch {epoch+1}: Train loss={train_loss:.3f}, Train acc={train_acc:.2f}%, Val loss={val_loss:.3f}, Val acc={val_acc:.2f}%")
Added a second linear layer with 128 units to increase model capacity.
Added ReLU activation between linear layers for non-linearity.
Added dropout layer with 0.3 dropout rate after first linear layer to reduce overfitting.
Results Interpretation

Before: Training accuracy 95%, Validation accuracy 80%, Training loss 0.15, Validation loss 0.45

After: Training accuracy 91%, Validation accuracy 89%, Training loss 0.25, Validation loss 0.30

Adding dropout and an extra linear layer with activation helped reduce overfitting. The model generalizes better, improving validation accuracy while slightly lowering training accuracy.
Bonus Experiment
Try adding batch normalization layers after linear layers and see if validation accuracy improves further.
💡 Hint
Batch normalization can stabilize and speed up training, often improving generalization.

Practice

(1/5)
1. What does a torch.nn.Linear layer do in a neural network?
easy
A. It connects every input feature to every output feature with weights and optional bias.
B. It applies a convolution operation on the input data.
C. It reduces the input size by pooling.
D. It normalizes the input features to have zero mean and unit variance.

Solution

  1. Step 1: Understand the role of Linear layers

    A Linear layer multiplies input features by weights and adds bias to produce output features.
  2. Step 2: Compare with other layer types

    Convolution, pooling, and normalization are different operations, not done by Linear layers.
  3. Final Answer:

    It connects every input feature to every output feature with weights and optional bias. -> Option A
  4. Quick Check:

    Linear layer = weighted sum + bias [OK]
Hint: Linear layers do weighted sums of inputs to outputs [OK]
Common Mistakes:
  • Confusing Linear with convolution layers
  • Thinking Linear layers do pooling
  • Mixing normalization with Linear layers
2. Which of the following is the correct way to create a Linear layer with 10 inputs and 5 outputs in PyTorch?
easy
A. torch.nn.Linear(10, 5)
B. torch.nn.Linear(out_features=10, in_features=5)
C. torch.nn.Linear(in_features=5, out_features=10)
D. torch.nn.Linear(5, 10)

Solution

  1. Step 1: Recall Linear layer constructor parameters

    The constructor is torch.nn.Linear(in_features, out_features).
  2. Step 2: Match input and output sizes

    We want 10 inputs and 5 outputs, so in_features=10 and out_features=5.
  3. Final Answer:

    torch.nn.Linear(10, 5) -> Option A
  4. Quick Check:

    Input size first, output size second [OK]
Hint: Remember: Linear(in_features, out_features) [OK]
Common Mistakes:
  • Swapping input and output sizes
  • Using keyword arguments in wrong order
  • Confusing number of features with batch size
3. What is the output shape of the following PyTorch code?
import torch
layer = torch.nn.Linear(4, 3)
input_tensor = torch.randn(2, 4)
output = layer(input_tensor)
print(output.shape)
medium
A. torch.Size([2, 4])
B. torch.Size([4, 3])
C. torch.Size([3, 4])
D. torch.Size([2, 3])

Solution

  1. Step 1: Understand input and layer dimensions

    The input tensor has shape (2, 4), meaning batch size 2 and 4 features per sample. The Linear layer maps 4 inputs to 3 outputs.
  2. Step 2: Calculate output shape

    The output shape is (batch size, output features) = (2, 3).
  3. Final Answer:

    torch.Size([2, 3]) -> Option D
  4. Quick Check:

    Output shape = (batch, out_features) [OK]
Hint: Output shape = (batch size, out_features) [OK]
Common Mistakes:
  • Confusing input and output feature dimensions
  • Mixing batch size with feature size
  • Expecting output shape to match input shape
4. Identify the error in this PyTorch code snippet:
import torch
layer = torch.nn.Linear(3, 2)
input_tensor = torch.randn(4, 4)
output = layer(input_tensor)
medium
A. Input tensor has wrong batch size.
B. Linear layer input features do not match input tensor features.
C. Output tensor shape is incorrect.
D. Missing activation function after Linear layer.

Solution

  1. Step 1: Check Linear layer input features

    The Linear layer expects input features of size 3.
  2. Step 2: Check input tensor shape

    The input tensor shape is (4, 4), so each sample has 4 features, which does not match the expected 3.
  3. Final Answer:

    Linear layer input features do not match input tensor features. -> Option B
  4. Quick Check:

    Input features must match layer's in_features [OK]
Hint: Input feature size must match Linear layer's in_features [OK]
Common Mistakes:
  • Confusing batch size with feature size
  • Ignoring feature dimension mismatch
  • Thinking activation function is mandatory
5. You want to build a simple neural network with one hidden Linear layer that takes 8 input features, has 4 hidden units, and outputs 2 classes. Which PyTorch code correctly defines this model?
hard
A. import torch.nn as nn model = nn.Sequential( nn.Linear(8, 2), nn.ReLU(), nn.Linear(4, 2) )
B. import torch.nn as nn model = nn.Sequential( nn.Linear(4, 8), nn.ReLU(), nn.Linear(2, 4) )
C. import torch.nn as nn model = nn.Sequential( nn.Linear(8, 4), nn.ReLU(), nn.Linear(4, 2) )
D. import torch.nn as nn model = nn.Sequential( nn.Linear(2, 4), nn.ReLU(), nn.Linear(8, 4) )

Solution

  1. Step 1: Define input to hidden layer

    The first Linear layer should map 8 input features to 4 hidden units: nn.Linear(8, 4).
  2. Step 2: Define hidden to output layer

    The second Linear layer should map 4 hidden units to 2 output classes: nn.Linear(4, 2).
  3. Step 3: Check activation function placement

    ReLU activation is correctly placed between the two Linear layers.
  4. Final Answer:

    Correct model definition with layers: Linear(8,4), ReLU, Linear(4,2) -> Option C
  5. Quick Check:

    Input -> hidden -> output sizes match [OK]
Hint: Match layer sizes: input->hidden->output with activation in between [OK]
Common Mistakes:
  • Swapping input and output sizes in layers
  • Placing activation after output layer
  • Using wrong feature sizes in layers
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard