ResNet-18 for Image Classification: Residual Learning on MNIST and CIFAR-10
How we implemented a ResNet-18 classification system with dual CLI/GUI interfaces, and what the residual connection formulation buys you over plain convolutional stacks.
Image classification is one of the most well-studied problems in computer vision, but the gap between a working tutorial implementation and a system you can actually deploy and iterate on is significant. This article documents the architecture, training formulation, and system design choices behind a classification framework we built on ResNet-18, evaluated against MNIST and CIFAR-10.
The residual connection
The central idea in ResNet, introduced by He et al. (2016), is simple: instead of asking a stack of layers to learn a mapping directly, reformulate the problem so the layers learn the residual . The original mapping becomes , implemented as a shortcut connection that adds the block’s input to its output.
When the input and output dimensions match, this addition requires no parameters. When they differ (across downsampling layers), a convolution projects the shortcut to the correct dimension:
The practical consequence is that gradients can flow directly through the shortcut path back to early layers during backpropagation, sidestepping the vanishing gradient problem that made networks beyond ~20 layers difficult to train with plain architectures.
ResNet-18 specification
ResNet-18 consists of an initial convolution followed by four layer groups, each containing two residual blocks with convolutions, and a final global average pooling step before the classification head.
| Layer group | Blocks | Channels | Stride | Output size (CIFAR-10) |
|---|---|---|---|---|
| conv1 | — | 64 | 2 | 16×16 |
| layer1 | 2 | 64 | 1 | 16×16 |
| layer2 | 2 | 128 | 2 | 8×8 |
| layer3 | 2 | 256 | 2 | 4×4 |
| layer4 | 2 | 512 | 2 | 2×2 |
| avgpool | — | — | — | 1×1 |
| fc | — | 10 | — | — |
Total trainable parameters: approximately 11.2 million. The model is adapted at the input layer
to handle one-channel MNIST images (by setting in_channels=1) versus three-channel CIFAR-10
inputs, with everything downstream unchanged.
Each residual block applies the sequence: convolution → batch normalisation → ReLU → convolution → batch normalisation, then adds the shortcut before the final activation:
Batch normalisation before each activation stabilises the distribution of inputs to each layer, allowing higher learning rates and reducing sensitivity to initialisation.
Training formulation
The loss function is standard cross-entropy over the classes. For a single example with true class and softmax output probabilities :
For MNIST we used Adam with a learning rate of and trained for 10 epochs with a batch size of 64. For CIFAR-10, SGD with momentum and a cosine annealing schedule performed better than Adam in our experiments — a pattern that holds broadly for ResNet training on CIFAR-class tasks:
where is the total number of epochs, , and .
Dataset normalisation
Both datasets are normalised per-channel to zero mean and unit variance using statistics computed over the training set. For CIFAR-10, the per-channel means and standard deviations are:
For MNIST, the single-channel statistics are , .
System architecture
The system applies Separation of Concerns across three layers: user interfaces (CLI and PyQt5 GUI), application logic (trainer and inference classes), and core components (model, data loaders, utilities). The CLI is suitable for scripting and remote training; the GUI wraps the same trainer classes with a PyQt5 interface that surfaces real-time loss curves and provides direct access to checkpoints and export functions.
Checkpointing saves the model state dict alongside the optimiser state and current epoch, so training can be resumed cleanly after interruption. The best checkpoint (by validation accuracy) is saved separately from the latest checkpoint.
Results
On the held-out test sets:
| Dataset | Epochs | Test accuracy | Inference speed (GPU) |
|---|---|---|---|
| MNIST | 10 | 99.3–99.5% | ~1000 images/sec |
| CIFAR-10 | 50 | 92–94% | ~800 images/sec |
The MNIST result is near the practical ceiling for this architecture without data augmentation. The CIFAR-10 result is consistent with published ResNet-18 baselines on this benchmark; pushing past 94% typically requires heavier augmentation (Cutout, AutoAugment) or a wider network.
Inference pipeline
The inference engine supports both single-image and batch-directory processing. Predictions are returned as a ranked list of class probabilities with configurable top- and confidence threshold filtering. Output is serialised to JSON, with batch results also written to CSV for downstream analysis.
For a single-image prediction, the top class and its confidence are determined as:
The batch pipeline processes a directory of images and aggregates a confusion matrix and per-class precision, recall, and F1 scores using the standard definitions. These are exported as a Bokeh interactive HTML dashboard alongside static PNG and CSV outputs.
He, K. et al. (2016). Deep Residual Learning for Image Recognition. CVPR 2016.