Thuta Learning
ProjectsAIintermediate

Project: CIFAR-10 Image Classifier

What you'll walk away with

  • Explain the core ideas behind Project: CIFAR-10 Image Classifier
  • Run the sample code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

A CIFAR-10-shaped problem means classifying 32x32 RGB images into one of 10 classes. This project stacks Conv2d → ReLU → MaxPool2d twice, shrinking spatial resolution from 32→16→8 while growing channel depth from 3→16→32 — the classic vision-CNN pattern of trading fine spatial detail for richer semantic features as the network goes deeper. The final feature map is flattened and passed through a single Linear layer that outputs 10 class logits, scored against the true labels with CrossEntropyLoss, the same loss used for any multi-class classification you've already seen.

Wrapping the synthetic images and labels in a TensorDataset and DataLoader ties the batching mechanics from the Deep Learning with PyTorch course back into a full vision pipeline: each epoch, the DataLoader shuffles and hands out batches, and you accumulate and print the average loss per epoch. Because the images and labels here are pure noise with no real relationship, the model cannot learn anything generalizable — but running the full pipeline on fast, disposable synthetic data first is a genuinely useful habit: it lets you confirm tensor shapes line up, the backward pass runs cleanly, and the optimizer is actually updating parameters, all before you ever point the same code at a real, slow-to-download dataset.

Connect it to a real scenario

The Tutorial Platform runs a course-thumbnail category tagger that automatically labels instructor-uploaded thumbnail images before they render in the catalog grid — sorting them into small categories like "code editor screenshot", "diagram/illustration", "presenter webcam shot", or "text-heavy slide". The small-CNN-plus-multi-epoch-training-loop shape built in this project is exactly that classifier's core: swap the class count from 10 to 4 and the pipeline is already the same one running in production.

Try the working example

python
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader

torch.manual_seed(0)

# Synthetic CIFAR-10-shaped data: 10 classes, 32x32 RGB images
num_samples = 200
num_classes = 10
images = torch.randn(num_samples, 3, 32, 32)
labels = torch.randint(0, num_classes, (num_samples,))

dataset = TensorDataset(images, labels)
loader = DataLoader(dataset, batch_size=32, shuffle=True)

class SmallCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 16, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),  # 32x32 -> 16x16
            nn.Conv2d(16, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),  # 16x16 -> 8x8
        )
        self.classifier = nn.Linear(32 * 8 * 8, num_classes)

    def forward(self, x):
        x = self.features(x)
        x = x.flatten(1)
        return self.classifier(x)

model = SmallCNN(num_classes)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)

num_epochs = 5
epoch_losses = []
for epoch in range(num_epochs):
    running_loss = 0.0
    for batch_images, batch_labels in loader:
        optimizer.zero_grad()
        outputs = model(batch_images)
        loss = criterion(outputs, batch_labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item() * batch_images.size(0)
    epoch_loss = running_loss / num_samples
    epoch_losses.append(epoch_loss)
    print(f"Epoch {epoch+1}/{num_epochs} - loss: {epoch_loss:.4f}")

print("Training complete.")
print(f"Loss went from {epoch_losses[0]:.4f} to {epoch_losses[-1]:.4f} over {num_epochs} epochs.")
You should see
Five lines print, "Epoch 1/5 - loss: X.XXXX" through "Epoch 5/5 - loss: X.XXXX", followed by "Training complete." and a one-line loss-trend summary. Because the labels have zero real relationship to the images, epoch 1's loss starts near ln(10)≈2.30, the random-guess value for 10-way cross entropy. With only 200 samples and enough capacity in the small CNN, the loss should trend gradually downward by epoch 5 (not necessarily monotonically) — which reflects the model memorizing the tiny training set, not real generalization.

5-minute try-it

Modify the loop to hold out a validation split — divide the 200 synthetic samples into 160 for training and 40 for validation, and compute and print a validation loss each epoch alongside the training loss. Notice that even as training loss falls, validation loss should not meaningfully improve, and think through why.

One important caution

Forgetting optimizer.zero_grad() before each loss.backward() call inside the batch loop silently accumulates gradients across batches, muddying the printed loss trend without raising any error.

Reading a falling loss as proof the model is "learning" is a trap here — with fully random labels, the loss drop reflects memorization of the tiny 200-sample set, not a classifier that would perform any better than chance on new data.

PyTorch Tutorials — Training a Classifier (CIFAR10)Computer Vision

Easy traps

  • Forgetting optimizer.zero_grad() before each loss.backward() call inside the batch loop silently accumulates gradients across batches, muddying the printed loss trend without raising any error.
  • Reading a falling loss as proof the model is "learning" is a trap here — with fully random labels, the loss drop reflects memorization of the tiny 200-sample set, not a classifier that would perform any better than chance on new data.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Modify the loop to hold out a validation split — divide the 200 synthetic samples into 160 for training and 40 for validation, and compute and print a validation loss each epoch alongside the training loss. Notice that even as training loss falls, validation loss should not meaningfully improve, and think through why.

You'll know it worked when: Five lines print, "Epoch 1/5 - loss: X.XXXX" through "Epoch 5/5 - loss: X.XXXX", followed by "Training complete." and a one-line loss-trend summary. Because the labels have zero real relationship to the images, epoch 1's loss starts near ln(10)≈2.30, the random-guess value for 10-way cross entropy. With only 200 samples and enough capacity in the small CNN, the loss should trend gradually downward by epoch 5 (not necessarily monotonically) — which reflects the model memorizing the tiny training set, not real generalization.

Project: CIFAR-10 Image Classifier | Thuta Learning