A ResNet50 transfer-learning image classifier that tells cats from dogs at ~94% validation accuracy (AUC 0.98) — trained in two stages on the Oxford-IIIT Pet dataset, with Grad-CAM heatmaps to show where the model is looking.
It also ships as a complete, beginner-friendly tutorial: a ~40-page guide
(guide.pdf) walks you through building the whole thing from
scratch, from PyTorch basics to two-stage fine-tuning. See
Learn by building.
Each prediction comes with a heatmap highlighting the pixels that drove the decision — red = "this is why I said dog/cat":
| Input | Grad-CAM | Input | Grad-CAM |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Stage | What trains | Val accuracy | Precision | Recall | AUC |
|---|---|---|---|---|---|
| 1 — frozen backbone | new head only | 86.3% | 0.92 | 0.96 | 0.93 |
2 — fine-tune layer4 |
head + deepest block | 94.2% | 0.96 | 0.97 | 0.98 |
Oxford-IIIT Pet, ResNet50 (ImageNet1K), 10 + 10 epochs, ~5,900 train / ~1,500 val.
Requires Python ≥ 3.9 and PyTorch. MPS (Apple Silicon), CUDA, and CPU are all supported automatically.
git clone https://github.com/mtkl6/cat-dog-classifier.git
cd cat-dog-classifier
python -m venv venv && source venv/bin/activate
pip install -r requirements.txtDownload the trained weights from the Hugging Face model page:
pip install huggingface_hub
python -c "from huggingface_hub import hf_hub_download as d; \
d('mtkl6/cat-dog-classifier','cat_dog_classifier.pt',local_dir='.')"
python test.py path/to/your/image.jpg # or: python test.py (uses pic/)This prints each prediction with a confidence score and writes a heatmap
overlay to gradcam/.
python train.py # downloads the dataset on first run, trains both stages
tensorboard --logdir runs # watch the curves (see TENSORBOARD.md)Two checkpoints are written — best_model_stage1.pt, best_model_stage2.pt —
plus the final cat_dog_classifier.pt.
Transfer learning reuses a ResNet50 already trained on ImageNet and adapts it to cats-vs-dogs instead of learning from zero:
- Stage 1 — frozen backbone. Freeze all of ResNet50, replace its 1000-class
head with
Dropout(0.4) → Linear(2048, 1), and train only that head (lr 1e-3). Fast, and it can't damage the pretrained features. - Stage 2 — gentle fine-tune. Unfreeze just the deepest block (
layer4) and nudge it with a tinylr 1e-5so task-specific features adapt without forgetting the generic ones.
The head outputs a single logit; sigmoid(logit) > 0.5 means dog (cats are 0,
dogs are 1). Training uses BCEWithLogitsLoss, the Adam optimizer,
ReduceLROnPlateau, data augmentation (flip / rotate / crop / color jitter),
early stopping, and logs loss/accuracy/precision/recall/AUC to TensorBoard.
The checkpoint is a raw state_dict; rebuild the architecture, then load:
import torch, torch.nn as nn
from torchvision import models, transforms
from PIL import Image
model = models.resnet50()
model.fc = nn.Sequential(nn.Dropout(0.4), nn.Linear(2048, 1))
model.load_state_dict(torch.load("cat_dog_classifier.pt", weights_only=True))
model.eval()
tf = transforms.Compose([
transforms.Resize((224, 224)), transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
x = tf(Image.open("cat.jpg").convert("RGB")).unsqueeze(0)
p_dog = torch.sigmoid(model(x)).item()
print("dog" if p_dog > 0.5 else "cat", f"({max(p_dog, 1-p_dog):.1%})")This repo started life as a teaching project, and the guide is still its
heart. guide.pdf (source: guide.typ) is a 13-chapter,
beginner-oriented walkthrough that has you write train.py yourself:
setup → the dataset → data pipeline → augmentation → neural-net basics → transfer learning → building the model → the training loop → stage 1 → stage 2 → evaluation → inference → resources
train.py is written as a readable, heavily-commented learner scaffold;
solution/train.py is a complete reference implementation
(using the lighter MobileNetV2) for comparison.
train.py two-stage transfer-learning trainer (ResNet50), TensorBoard logging
test.py inference + Grad-CAM heatmap generation
solution/train.py reference implementation (MobileNetV2 variant)
guide.pdf / .typ the full beginner's guide (Typst source + rendered PDF)
pic/ sample input images
gradcam/ example Grad-CAM outputs
TENSORBOARD.md how to view training curves
requirements.txt
The code and guide are MIT-licensed (see LICENSE).
The model trains on the Oxford-IIIT Pet dataset
(Parkhi et al., 2012), downloaded automatically by torchvision on first run
and not redistributed here (data/ is gitignored). The dataset is provided
for research/educational use under its own terms; please cite the original
authors if you use it.
If you use this project, please cite it (see CITATION.cff):
@software{cat_dog_classifier_2026,
author = {Moritz (mtkl6)},
title = {Cat vs Dog Classifier: a ResNet50 transfer-learning tutorial},
year = {2026},
url = {https://github.com/mtkl6/cat-dog-classifier}
}- Backbone: ResNet50 via torchvision.
- Dataset: Oxford-IIIT Pet.
- Grad-CAM: Selvaraju et al., Grad-CAM: Visual Explanations from Deep Networks (2017).



