Two real projects, one video: classify images with a CNN and transfer learning, then train a tiny GPT that writes Shakespeare-like text.
Intro
Two Models, One Video
Two models, one video. The first looks at a photo and says what is in it. The second reads a pile of old text and then starts writing its own. Both are built with the same tools you already know, and by the end you will have trained both yourself.
- Image classifier
- Tiny language model
- Both trained by you
Capstones 9 and 10
Last time we finished customer segmentation and a recommendation system. Today is capstones nine and ten. Remember the rule for this whole capstone series: we report only results we measured ourselves. Any number you see me work through for arithmetic is labelled as an example, and your own runs will give different numbers.
- Report only measured results
- Examples are labelled
- No architecture re-teaching
The Plan
Here is the plan. For images we run a full pipeline: a baseline convolutional network, augmentation, transfer learning, diagnostics, a confusion matrix, and Grad-CAM. For language we train a small character-level transformer, watch perplexity fall, sample with temperature, and be honest about its limits. We finish by comparing what each project costs in compute.
- Image pipeline
- Language model
- Compute comparison
Image Data and Baseline
The Dataset: CIFAR-10
We need a standard public dataset so anyone can compare notes. CIFAR-ten has sixty thousand colour images, thirty-two by thirty-two pixels, in ten classes such as airplane, cat, and truck. Fifty thousand are for training and ten thousand are for testing. It is small enough to train on one modest GPU, yet hard enough to be interesting.
- 60,000 images, 10 classes
- 32 by 32 colour pixels
- 50,000 train, 10,000 test
Splits and Normalisation
Before modelling, carve out a validation set from the training data, for example forty-five thousand to train and five thousand to validate. The test set stays locked away, exactly as in lesson thirty-five. Then normalise each colour channel using the mean and standard deviation computed on the training images only. A classic mistake is peeking at test statistics, which is a quiet form of leakage.
- Validation from training data
- Test set stays locked
- Stats from train only
The Baseline CNN
Now the baseline. Using what we learned in lessons fifty-one and fifty-two, stack three convolution blocks, each with convolution, batch norm, ReLU, and max pooling, then a small linear classifier. Channels might go from three to thirty-two, sixty-four, and one hundred twenty-eight. Keep it deliberately simple, because a baseline exists to be beaten.
- Three conv blocks
- Pool halves the size
- Linear head, 10 logits
Train and Log the Baseline
Train with cross-entropy loss and the Adam optimiser, a batch size of one hundred twenty-eight, and a few epochs. Log training loss, validation loss, and validation accuracy every epoch. Chance on ten balanced classes is ten percent accuracy, so that is your floor. Write down whatever accuracy you actually measure on validation, because every later improvement is judged against it.
- Cross-entropy and Adam
- Log every epoch
- Chance level is 10 percent
Augmentation and Diagnostics
Reading the Curves
Read the curves like lesson fifty taught us. If training loss keeps falling while validation loss turns upward, the network is memorising. With only fifty thousand images, a baseline CNN usually does this. The gap between the two curves is the symptom, and more varied data is one of the best cures.
- Train down, validation up
- The gap means memorising
- Pick the best epoch
Augmentation: Crops and Flips
Augmentation creates new training examples by applying label-preserving changes. For CIFAR, the standard pair is a random crop after padding four pixels, and a random horizontal flip. A flipped cat is still a cat. But think before you augment: flipping a handwritten digit or rotating a street sign upside down could change the meaning.
- Random crop with padding
- Random horizontal flip
- Keep the label true
Train Only, Fresh Each Epoch
Two practical rules. Apply augmentation only to the training set, never to validation or test, because those must measure the real data. And the augmentation is random each epoch, so the network almost never sees the exact same pixels twice. Expect training accuracy to rise more slowly, and the validation gap to shrink.
- Never augment validation
- Random each epoch
- Gap should shrink
Sanity Checks That Save Hours
Diagnostics also catch bugs. Here is a classic sanity check: try to overfit a single small batch of thirty-two images. If the loss does not go near zero, something is broken, perhaps the learning rate or the labels. Also confirm you call model dot eval before validation, so batch norm and dropout behave correctly, and that initial loss is near the natural log of ten, about two point three.
- Overfit one tiny batch
- Initial loss near 2.30
- Switch to eval mode
Transfer Learning
Borrow a Pretrained Network
A bigger lever is transfer learning, from lesson fifty-two. Take a ResNet eighteen already trained on ImageNet, whose early layers detect edges, textures, and simple shapes that are useful everywhere. Replace the final layer with a new one that outputs ten classes. You are borrowing millions of images worth of learning for free.
- ResNet-18 from ImageNet
- Early layers: edges, textures
- Swap the final layer
Freeze, Then Fine-Tune
Do it in two stages. First freeze the pretrained body and train only the new head for a couple of epochs, with a normal learning rate. Then unfreeze everything and fine-tune with a much smaller learning rate, perhaps ten to a hundred times smaller, so you nudge the features instead of wrecking them. Use a different learning rate for head and body if you like.
- Stage one: train the head
- Stage two: unfreeze, small LR
- Different rates per part
Transfer Learning Gotchas
Three details trip people up. The pretrained network expects ImageNet normalisation, so use its mean and standard deviation, not your own. It was trained on larger images, so you will often upscale the thirty-two pixel inputs, or adjust the first layer and stride. And keep augmentation on. Then compare to the baseline on the same validation set and record the real difference you measure.
- Use ImageNet normalisation
- Mind the input size
- Compare on same validation set
Error Analysis
The Confusion Matrix
A single accuracy number hides where the model fails. Now, and only now, evaluate once on the test set, and build a confusion matrix. Rows are true classes, columns are predicted classes. The diagonal holds correct predictions, and every off-diagonal cell is a specific kind of mistake.
- Rows: true class
- Columns: predicted class
- Off-diagonal: mistakes
Recall and Precision Per Class
Let us read one with example numbers. Suppose there are one thousand test cats. If eight hundred are labelled cat, one hundred are labelled dog, and one hundred are scattered elsewhere, then recall for cat is eight hundred over one thousand, which is zero point eight. Precision looks down the column instead: of everything predicted cat, how many truly were cats. These are lesson sixteen's metrics, now per class.
- Recall: read along the row
- Precision: read down the column
- Example: 800 / 1000 = 0.8
Look at the Errors
Do not stop at the matrix. Pull out the actual images from the worst cell, for example cats predicted as dogs, and look at them with your own eyes. You may find blurry pictures, odd crops, or even wrong labels in the dataset. Sorting mistakes by model confidence also helps: confident errors are the most informative ones.
- Open the worst cell
- Check for bad labels
- Sort by confidence
Grad-CAM: The Recipe
To see why the network decides, we use Grad-CAM. Take the last convolution layer, with feature maps A sub k. Compute the gradient of the class score with respect to each map, and average it over all positions to get a weight alpha k. Then form a weighted sum of the maps and apply ReLU, keeping only evidence that pushes the class score up.
- Gradients of class score
- Average to weights alpha k
- Weighted sum, then ReLU
Right for the Wrong Reason?
The result is a coarse heat map, which you upsample and overlay on the picture. A healthy model lights up the animal. A worrying one lights up the background, such as grass behind a horse or water behind a ship. That is shortcut learning, and it tells you the model may be right for the wrong reason. Treat Grad-CAM as a clue, not proof.
- Heat map over the image
- Check object versus background
- A clue, not proof
Small Language Model Setup
Text to Integers
Now the second project. We will train a character-level GPT on a small public-domain text, such as the tiny Shakespeare file of roughly one million characters, or any Project Gutenberg book. Character-level means the vocabulary is tiny, about sixty-five symbols for that file, so we skip the tokenizer machinery from lesson fifty-four. A small byte-pair encoding version works too, with a bigger vocabulary and shorter sequences.
- Public-domain text
- About 65 characters
- BPE is an alternative
Predict the Next Character
The task is next-token prediction. Take a window of, say, two hundred fifty-six characters as input. The target is the same window shifted one position to the right. So every position in the window is a training example, and a single batch teaches the model many predictions at once. Split the text by position, the first ninety percent for training and the last ten percent for validation, never by shuffling windows.
- Target is input shifted by one
- Every position is an example
- Split by position, not shuffle
The Model: A Small GPT
The model uses the transformer block from lesson fifty-six, and we will not re-teach it. Stack token embeddings plus position embeddings, then say six blocks of masked self-attention and feed-forward layers with residuals and layer norm, then a final linear layer to vocabulary logits. A reasonable small setup is embedding size three hundred eighty-four, six heads, and six layers, around ten million parameters. Pick sizes your hardware can handle.
- Embeddings plus positions
- Six masked blocks
- Linear head to logits
Training and Perplexity
Cross-Entropy and the Starting Loss
The loss is cross-entropy between predicted next-character distribution and the true character. Here is a free sanity check. A model that guesses uniformly over sixty-five characters has loss equal to the natural log of sixty-five, about four point one seven. If your first step prints something far from that, your initialisation or labels are wrong.
- Uniform guess over 65
- Loss = ln 65 = 4.17
- Check step zero
Perplexity Explained
Perplexity is just the exponential of the cross-entropy loss, measured in nats. It reads as the effective number of choices the model is torn between. Uniform guessing gives perplexity sixty-five. As an example, a validation loss of one point five gives e to the one point five, about four point four eight, meaning the model is as unsure as choosing among roughly four and a half characters.
- Perplexity = exp(loss)
- Uniform guess: 65
- Example: exp(1.5) = 4.48
The Training Run
The training loop looks like lesson fifty. Use AdamW, a short warmup, a cosine decay of the learning rate, gradient clipping at one, and evaluate on validation every few hundred steps. Plot training and validation loss, plus perplexity. On a small file you will see the same story as before: validation eventually stops improving while training keeps dropping. Save the checkpoint with the best validation loss.
- AdamW, warmup, cosine decay
- Clip gradients at 1.0
- Keep best validation checkpoint
Three Classic Mistakes
Common mistakes here are subtle. Forgetting the causal mask lets the model peek at the answer, and the loss collapses suspiciously fast. Using overlapping windows between train and validation leaks text across the boundary. And if loss becomes not a number, lower the learning rate, check clipping, and consider mixed precision settings. Always debug on a tiny slice first.
- Missing causal mask
- Leaking across the split
- Exploding loss
Sampling and Limits
Generating Text
Generation works by repeating one step. Feed in a prompt, get logits for the next character, turn them into probabilities, sample one character, append it, and repeat. The sampling knob is temperature, which divides the logits before the softmax. Low temperature sharpens the distribution, and high temperature flattens it.
- Predict, sample, append
- Repeat character by character
- Temperature divides logits
Temperature by the Numbers
Here is the arithmetic. Take three logits: two, one, and zero. At temperature one, the probabilities are about zero point six seven, zero point two five, and zero point zero nine. At temperature zero point five the logits become four, two, zero, and the probabilities sharpen to about zero point eight seven, zero point one two, and zero point zero two. At temperature two they flatten to roughly zero point five one, zero point three one, and zero point one nine.
- T = 0.5: 0.87, 0.12, 0.02
- T = 1: 0.67, 0.25, 0.09
- T = 2: 0.51, 0.31, 0.19
What Temperature Does to Text
What does that do to text? Very low temperature gives safe but repetitive output, often looping the same phrase. Temperature around one gives the most natural variety. High temperature produces creative nonsense and invented words. Generate samples at several temperatures from the same prompt, and quote them exactly as they come out.
- Low: repetitive
- Around 1: varied
- High: garbled
Honest Limits at This Scale
Now the honest part. A small model trained on one book learns spelling, punctuation, the layout of a script, and local word patterns. It does not learn meaning, plot, or facts. Expect text that looks like the source in shape but wanders in sense, with invented words and characters that lose track of who is speaking. That is not a bug in your code; it is what a few million parameters and one million characters can do.
- Learns form, not meaning
- Loses long-range coherence
- Few parameters, little data
Memorisation Check
A related risk is memorisation. With a tiny dataset, a model can reproduce long passages of its training text word for word, and the validation curve is how you notice. Low validation loss with fluent output may simply mean copying. Compare generated passages against the source before you claim the model wrote anything original.
- Small data invites copying
- Compare output to the source
- Validation loss is your guard
Compute Budget
Estimating Compute
Let us compare the two projects. Training cost is roughly six times the number of parameters times the number of tokens processed. As an illustrative example, a ten-million-parameter model reading one million characters in an epoch needs about six times ten to the seventh times ten to the sixth, which is six times ten to the thirteenth floating point operations per epoch. That is small on a modern GPU, though real speed depends on your hardware and code.
- FLOPs about 6 x N x D
- Example: 6 x 10^13 per epoch
- Hardware sets real time
Where Each Project Spends Compute
The image project has a different shape. A pretrained ResNet-eighteen has about eleven million parameters, but each image is pushed through many convolutions at several resolutions, and you repeat for fifty thousand images over many epochs. Fine-tuning is cheaper than training from scratch because you need fewer epochs. The language model has the quadratic cost of attention in context length, so doubling the context roughly quadruples that part.
- CNN: images times epochs
- Fine-tuning needs fewer epochs
- Attention: quadratic in context
Measure, Do Not Guess
Do not trust my estimates or anyone else's. Measure your own: wall-clock time per epoch, peak GPU memory, and the number of parameters, along with the final metric. Put them in a table for each project, and record the seed, hardware, and library versions. That table is your honest result, and it lets anyone reproduce or challenge your claims.
- Time, memory, parameters
- Record seed and hardware
- Report only your numbers
What Is Your Bottleneck?
One last lesson from putting both projects side by side. The image task rewarded borrowing: a pretrained body beat starting from scratch on little data. The language task showed the opposite pressure: with only a book of text, scale of data and model is the limiting factor, which is why lesson fifty-seven talked about pretraining. Knowing which bottleneck you face decides what to try next.
- Images: borrow features
- Language: data and scale
- Diagnose before you fix
Recap and Next Steps
Recap
Let us recap. For images, we split carefully, trained a baseline, used augmentation and transfer learning, read diagnostics, built a confusion matrix, and used Grad-CAM to check what the model looked at. For language, we trained a character-level GPT, tracked loss and perplexity, sampled with temperature, and stayed honest about the limits.
- Image pipeline end to end
- GPT, perplexity, temperature
- Compare compute, report honestly
Next: Forecasting and Deployment
Your homework is to run both projects and fill in your own table. Change one thing at a time, such as the augmentation, the temperature, or the context length, and write down what happened. Next lesson, capstones eleven and twelve: time-series forecasting, and an end-to-end deployed machine learning app.
- Run both projects yourself
- Change one thing at a time
- Next: capstones 11 and 12