Image Classification

Image classification assigns a label (or a set of labels) to a whole image. It is the foundational computer vision task: most detection and segmentation backbones were first trained as classifiers, and transfer learning from ImageNet-pretrained classifiers is the default starting point for almost every vision project.

Parent topic: Computer Vision.


1. How a Classifier Works

CNN Image Classifier — Feature Hierarchy Input 224×224×3 Stage 1 edges, colors Stage 2 textures Stage 3-4 parts, objects GAP + FC 1×1×C → K Spatial size shrinks, channels grow: 224² × 3 → 7² × 2048 (ResNet-50) → softmax over K classes.

A CNN stacks convolution, normalisation, and nonlinearity layers in stages; each stage downsamples spatially and increases channels. Early layers respond to edges and colour blobs, later ones to object parts. Global average pooling collapses the final feature map to a vector, and a linear layer produces K logits. Softmax turns logits into probabilities: pk = ezk / Σj ezj, trained with cross-entropy L = −log py.

Common backbones: ResNet / EfficientNet, ConvNeXt, MobileNet (edge), and Vision Transformers. For multi-label problems (an image can be both "beach" and "sunset") replace softmax with per-class sigmoids and binary cross-entropy.

2. Transfer Learning

Transfer Learning Strategies Linear probe stage 1 (frozen) stage 2 (frozen) stage 3 (frozen) head (train) freeze backbone, train head only Partial fine-tune stage 1 (frozen) stage 2 (frozen) stage 3 (train) head (train) unfreeze last stages + head Full fine-tune stage 1 (train) stage 2 (train) stage 3 (train) head (train) all layers, small LR Less data / closer domain → freeze more. More data / distant domain → fine-tune more.

Training from scratch needs very large datasets. With a pretrained backbone, a few hundred labelled images per class is often enough for a useful model. Start with a linear probe as a baseline, then unfreeze progressively. Use a lower learning rate for pretrained layers than for the new head, and match the pretrained model's input normalisation.

3. PyTorch Example

import torch, torchvision
from torch import nn
from torchvision import datasets, transforms
from torchvision.models import resnet50, ResNet50_Weights

weights = ResNet50_Weights.IMAGENET1K_V2
train_tf = transforms.Compose([
    transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(),
    transforms.ColorJitter(0.2, 0.2, 0.2), transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
val_tf = weights.transforms()

# folder layout: data/train/<class>/*.jpg, data/val/<class>/*.jpg
train = datasets.ImageFolder("data/train", train_tf)
val = datasets.ImageFolder("data/val", val_tf)
tl = torch.utils.data.DataLoader(train, 32, shuffle=True, num_workers=4)
vl = torch.utils.data.DataLoader(val, 64, num_workers=4)

dev = "cuda" if torch.cuda.is_available() else "cpu"
model = resnet50(weights=weights)
for p in model.parameters():
    p.requires_grad = False                     # linear probe first
model.fc = nn.Linear(model.fc.in_features, len(train.classes))
model.to(dev)
opt = torch.optim.AdamW(model.fc.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()

for epoch in range(5):
    model.train()
    for x, y in tl:
        x, y = x.to(dev), y.to(dev)
        loss = loss_fn(model(x), y)
        opt.zero_grad(); loss.backward(); opt.step()
    model.eval(); correct = n = 0
    with torch.no_grad():
        for x, y in vl:
            pred = model(x.to(dev)).argmax(1).cpu()
            correct += (pred == y).sum().item(); n += len(y)
    print(f"epoch {epoch}: val acc {correct / n:.3f}")

After the probe converges, set requires_grad=True on layer4 (and later layer3) and continue with a learning rate around 10x lower.

4. Evaluation

Confusion Matrix (3 classes) cat cat 88 9 3 dog dog 7 90 3 fox fox 10 6 84 true predicted Diagonal: correct predictions. Off-diagonal: which classes get confused. Precision (dog): 90 / (9+90+6) ≈ 0.86 Recall (fox): 84 / 100 = 0.84 Accuracy hides per-class failures — report per-class and macro-F1.

Top-1 accuracy is fine for balanced benchmarks; for real data use per-class precision/recall, macro-F1, and the confusion matrix. Check calibration (do 0.9-confidence predictions succeed 90% of the time?) if downstream logic uses probabilities, and evaluate on data from the deployment environment, not only a random split of the training set.

5. Pitfalls

6. A Training Recipe That Works

Training Recipe for a Production Classifier Raw images labels, groups Augment crop, flip, color Backbone pretrained Head linear, K logits Loss CE, smoothing Calibrate temperature

Modern image classifiers are trained with a fairly standard recipe, and most gains on a real project come from applying it carefully rather than from a new architecture.

Data augmentation is the main regularizer. RandomResizedCrop (random scale and aspect ratio, then resize to 224) and horizontal flip are the baseline; color jitter, RandAugment or TrivialAugment add more variation. Mixup and CutMix blend images and labels, which improves robustness and calibration on many benchmarks. Choose augmentations that preserve the label: flipping is wrong for digits or left/right anatomy, and heavy color jitter is wrong when color is the signal (ripeness of fruit, skin lesions).

Optimization: AdamW or SGD with momentum, a short linear warmup and then cosine decay. For fine-tuning, a learning rate around 1e-4 to 1e-3 for the head and 10x lower for the backbone is a reasonable starting range; weight decay of 0.01–0.05 with AdamW is common. Label smoothing (target 0.9 for the true class, the remainder spread over others) reduces overconfidence. Mixed precision (bf16 or fp16 with loss scaling) roughly halves memory and speeds training on recent GPUs.

Input resolution is a real hyperparameter. Small objects or fine texture (defects, pathology tiles) need higher resolution or tiling; doubling the side length roughly quadruples compute for a CNN. Test-time augmentation (averaging predictions over a flip and a few crops) gives a modest, cheap accuracy gain when latency allows.

7. Worked Example: Softmax, Loss and Calibration

Suppose a 3-class model (cat, dog, fox) outputs logits z = (2.0, 1.0, 0.1) for an image of a cat. Exponentiating gives roughly (7.39, 2.72, 1.11), which sum to 11.22, so the softmax probabilities are about (0.66, 0.24, 0.10). The cross-entropy loss is −log(0.66) ≈ 0.42. If the image were actually a fox, the loss would be −log(0.10) ≈ 2.30: confident mistakes cost much more than uncertain ones, which is why the loss pushes probabilities toward honesty.

Deep networks are nonetheless often overconfident (Guo et al., 2017). Temperature scaling fixes much of this after training: divide every logit by a single scalar T fitted on a validation set. With T = 2 the same logits become (1.0, 0.5, 0.05), and the probabilities soften to about (0.48, 0.29, 0.23). The ranking, and therefore accuracy, does not change, only the confidence. Measure calibration with Expected Calibration Error (ECE) and a reliability diagram.

8. Open-Set and Deployment Concerns

Closed-Set vs Open-Set Inference Input image any content Classifier softmax over K max p >= threshold return class max p < threshold return unknown Human review or fallback

A softmax classifier must pick one of its K classes even for an image it has never seen anything like. In deployment that is a serious failure mode: a plant-disease model will confidently label a photo of a shoe. Simple baselines include thresholding the maximum softmax probability (Hendrycks & Gimpel, 2017), using the maximum logit or an energy score, and adding an explicit "other" class trained on diverse negative images. None is perfect, so route low-confidence predictions to a human or a safe default.

Other production concerns:

9. Choosing a Backbone

FamilyStrengthsWatch out for
ResNet-50Well understood, strong baseline, easy to fine-tuneNot the most accurate per FLOP today
EfficientNet / MobileNetGood accuracy per parameter, mobile-friendlyDepthwise convs can be slower on some GPUs than FLOPs suggest
ConvNeXtCNN modernized with transformer-era design choicesLarger variants need substantial compute
ViT / DeiT / SwinScale well with data and pretraining; strong transferData-hungry from scratch; patch size sets resolution cost
CLIP image encoderZero-shot via text prompts; good linear probesInherits web-data biases; prompt wording matters

A practical order: start with a linear probe on a CLIP or DINOv2 embedding (minutes, often a strong baseline), then fine-tune a ResNet-50 or ConvNeXt-T, and only then try larger models if the error analysis shows the model, not the data, is the bottleneck.

10. Further Reading