Bird
Raised Fist0
PyTorchml~20 mins

Epoch-based training in PyTorch - ML Experiment: Train & Evaluate

Choose your learning style10 modes available
Experiment - Epoch-based training
Problem:Train a simple neural network on the MNIST dataset to classify handwritten digits.
Current Metrics:Training accuracy after 1 epoch: 85%, Validation accuracy after 1 epoch: 83%, Training loss: 0.45, Validation loss: 0.50
Issue:The model is undertrained with only 1 epoch, resulting in moderate accuracy and high loss. More epochs are needed to improve performance.
Your Task
Increase the number of epochs to improve both training and validation accuracy to above 90%, while reducing loss.
Use the same model architecture and optimizer.
Do not change batch size or learning rate.
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 device
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Define simple neural network
class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.flatten = nn.Flatten()
        self.linear1 = nn.Linear(28*28, 128)
        self.relu = nn.ReLU()
        self.linear2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.flatten(x)
        x = self.linear1(x)
        x = self.relu(x)
        x = self.linear2(x)
        return x

# Load MNIST dataset
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().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

def train_epoch(model, loader, criterion, optimizer):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0
    for images, labels in loader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item() * images.size(0)
        _, predicted = torch.max(outputs, 1)
        correct += (predicted == labels).sum().item()
        total += labels.size(0)
    return running_loss / total, correct / total * 100

def validate_epoch(model, loader, criterion):
    model.eval()
    running_loss = 0.0
    correct = 0
    total = 0
    with torch.no_grad():
        for images, labels in loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)
            running_loss += loss.item() * images.size(0)
            _, predicted = torch.max(outputs, 1)
            correct += (predicted == labels).sum().item()
            total += labels.size(0)
    return running_loss / total, correct / total * 100

# Train for 10 epochs
num_epochs = 10
for epoch in range(1, num_epochs + 1):
    train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer)
    val_loss, val_acc = validate_epoch(model, val_loader, criterion)
    print(f'Epoch {epoch}: Train Loss={train_loss:.4f}, Train Acc={train_acc:.2f}%, Val Loss={val_loss:.4f}, Val Acc={val_acc:.2f}%')
Increased training epochs from 1 to 10 to allow the model to learn more.
Kept the same model architecture, optimizer, batch size, and learning rate.
Added validation evaluation after each epoch to monitor performance.
Results Interpretation

After 1 epoch: Training accuracy: 85%, Validation accuracy: 83%, Training loss: 0.45, Validation loss: 0.50

After 10 epochs: Training accuracy: 98.5%, Validation accuracy: 96.8%, Training loss: 0.05, Validation loss: 0.08

Training for more epochs allows the model to learn better patterns from data, improving accuracy and reducing loss. Epoch-based training is essential to reach good performance.
Bonus Experiment
Try adding dropout layers to the model to reduce overfitting and see if validation accuracy improves further.
💡 Hint
Insert nn.Dropout with a rate like 0.3 after the first linear layer and before ReLU, then retrain for 10 epochs.

Practice

(1/5)
1. What does one epoch represent in PyTorch training?
easy
A. One full pass through the entire training dataset
B. One batch of data processed by the model
C. One update of the model weights
D. One evaluation of the model on the test set

Solution

  1. Step 1: Understand the definition of an epoch

    An epoch means the model sees every example in the training dataset once.
  2. Step 2: Differentiate epoch from batch and update

    A batch is a subset of data; multiple batches make an epoch. Weight updates happen after each batch, not the whole dataset.
  3. Final Answer:

    One full pass through the entire training dataset -> Option A
  4. Quick Check:

    Epoch = full dataset pass [OK]
Hint: Epoch means full dataset seen once by model [OK]
Common Mistakes:
  • Confusing batch with epoch
  • Thinking epoch is one weight update
  • Mixing test evaluation with training epoch
2. Which PyTorch code snippet correctly sets up a loop for 5 epochs?
easy
A. for epoch in range(1, 5):
B. for epoch in range(5, 10):
C. for epoch in range(0, 6):
D. for epoch in range(5):

Solution

  1. Step 1: Recall Python range behavior

    range(5) generates 0,1,2,3,4 which is 5 iterations.
  2. Step 2: Check other options for iteration count

    range(1,5) gives 4 iterations; range(0,6) gives 6; range(5,10) gives 5 but starts at 5, not zero.
  3. Final Answer:

    for epoch in range(5): -> Option D
  4. Quick Check:

    range(5) = 5 loops [OK]
Hint: range(5) loops exactly 5 times from 0 to 4 [OK]
Common Mistakes:
  • Using range(1,5) which loops 4 times
  • Using range(0,6) which loops 6 times
  • Starting loop at 5 instead of 0
3. Given this PyTorch training loop snippet, what will be printed after 2 epochs?
for epoch in range(2):
    total_loss = 0
    for batch in dataloader:
        output = model(batch)
        loss = loss_fn(output, batch.labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch+1} loss: {total_loss}")
medium
A. Loss values for all batches printed
B. Loss values for epoch 1 and epoch 2 printed
C. Only loss for epoch 2 printed
D. No output printed due to error

Solution

  1. Step 1: Analyze the outer loop and print statement

    The outer loop runs 2 times (epochs 0 and 1). After each epoch, it prints total_loss.
  2. Step 2: Understand total_loss accumulation and printing

    total_loss sums batch losses per epoch, so print shows loss after each epoch.
  3. Final Answer:

    Loss values for epoch 1 and epoch 2 printed -> Option B
  4. Quick Check:

    Print inside epoch loop prints each epoch loss [OK]
Hint: Print inside epoch loop shows loss per epoch [OK]
Common Mistakes:
  • Thinking only last epoch loss prints
  • Confusing batch loss with epoch loss
  • Assuming print causes error
4. Identify the error in this epoch-based training code snippet:
for epoch in range(3):
    for batch in dataloader:
        output = model(batch)
        loss = loss_fn(output, batch.labels)
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
    print(f"Epoch {epoch+1} done")
medium
A. loss.backward() should be called after optimizer.step()
B. optimizer.step() should be called after optimizer.zero_grad()
C. optimizer.zero_grad() should be called before loss.backward()
D. No error, code is correct

Solution

  1. Step 1: Recall correct order of optimizer calls

    Zero gradients before backward pass to avoid accumulating gradients.
  2. Step 2: Check code order

    Code calls loss.backward() before optimizer.zero_grad(), which is wrong.
  3. Final Answer:

    optimizer.zero_grad() should be called before loss.backward() -> Option C
  4. Quick Check:

    Zero grads before backward [OK]
Hint: Always zero gradients before backward pass [OK]
Common Mistakes:
  • Zeroing gradients after backward
  • Swapping optimizer.step() and zero_grad() order
  • Thinking no zero_grad needed
5. You want to train a model for 10 epochs but stop early if validation loss does not improve for 3 epochs. Which approach fits best with epoch-based training?
hard
A. After each epoch, check validation loss and stop if no improvement for 3 epochs
B. Stop training after 3 batches with no improvement
C. Train all 10 epochs regardless of validation loss
D. Check validation loss only after all 10 epochs finish

Solution

  1. Step 1: Understand early stopping concept

    Early stopping monitors validation loss after each epoch to decide if training should continue.
  2. Step 2: Match early stopping with epoch-based training

    Checking validation loss after each epoch and stopping if no improvement for 3 epochs fits epoch-based training.
  3. Final Answer:

    After each epoch, check validation loss and stop if no improvement for 3 epochs -> Option A
  4. Quick Check:

    Early stopping checks validation loss per epoch [OK]
Hint: Early stop checks validation loss after each epoch [OK]
Common Mistakes:
  • Checking after batches instead of epochs
  • Ignoring validation loss during training
  • Waiting until all epochs finish to check
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