A ResNet-18 trained to predict the distribution of human annotations on CIFAR-10 images, rather than a single hard label. Uses the CIFAR-10H dataset which provides per-image soft labels derived from crowdsourced annotations.
Standard image classifiers output a single predicted class. This project trains a model to output a full probability distribution over all ten CIFAR-10 classes that matches how human annotators actually responded to each image — including their disagreements. Three loss functions are compared: KL divergence, cross-entropy with soft labels, and a custom entropy-weighted KL loss.
DNN/
├── scripts/
│ ├── prepare_data.py # download and process CIFAR-10H into .npy arrays
│ ├── analyze_data.py # EDA plots (entropy histogram, class entropy, confusion matrix)
│ ├── train_full.py # pre-train on CIFAR-10, then fine-tune on CIFAR-10H (all 3 losses)
│ ├── eval.py # evaluate checkpoints on the held-out test set
│ ├── robustness.py # evaluate under noise and blur corruptions
│ ├── gradcam.py # Grad-CAM visualisations
│ └── convert.py # utility to convert checkpoint formats if needed
├── data/
│ ├── raw/ # CIFAR-10 files + cifar10h-probs.npy go here
│ └── processed/ # generated by prepare_data.py
├── checkpoints/ # saved model weights
├── outputs/ # plots and JSON results
├── logs/ # training logs
├── report.tex # LaTeX report
└── report.md # original markdown notes
Python 3.9+ and a CUDA-capable GPU are recommended. CPU training works but Stage 1 pre-training will be slow.
Install dependencies:
pip install torch torchvision numpy scipy matplotlib seaborn tqdm opencv-pythonTested with PyTorch 2.x. No specific version pinning needed beyond that.
cd d:\Coding\MU\DNN
python scripts/prepare_data.pyThis produces final_images.npy, final_probs.npy, and final_entropy.npy in data/processed/. It is safe to re-run — progress is checkpointed in data/processed/prepare_state.json so completed steps are skipped.
All scripts are run from the project root (d:\Coding\MU\DNN), not from inside scripts/.
python scripts/analyze_data.pySaves entropy histogram, per-class entropy bar chart, human confusion matrix, and low/high entropy image grids to outputs/.
python scripts/train_full.pyThis runs in two stages:
- Pre-trains on CIFAR-10 for 30 epochs (skipped if
checkpoints/pretrained.ptalready exists) - Fine-tunes three separate models — one per loss function (
kl,ce,kl_entropy) — for up to 50 epochs each with early stopping
Checkpoints are saved to checkpoints/ as kl.pt, ce.pt, and kl_entropy.pt. Logs go to logs/train_all.log.
python scripts/eval.pyEvaluates all three checkpoints on the held-out test set (last 2,000 images). Saves per-model JSON results to outputs/ and a combined outputs/summary.json.
python scripts/robustness.pyRuns the KL+Entropy model on the test set under Gaussian noise (outputs/robustness.json.
python scripts/gradcam.pySaves activation overlay images to outputs/gradcam/ for five selected test images.
After training and evaluation, outputs/summary.json should contain something close to:
| Model | Accuracy | Mean KL | Mean JS | Cosine |
|---|---|---|---|---|
| KL | 0.933 | 0.230 | 0.048 | 0.945 |
| CE | 0.917 | 0.264 | 0.053 | 0.936 |
| KL+Entropy | 0.924 | 0.241 | 0.051 | 0.940 |
Exact numbers may vary slightly depending on GPU and PyTorch version due to non-determinism in CUDA operations. The random seed is set to 42 in train_full.py but this only controls CPU-side randomness.
- The data split is 6000/2000/2000 (train/val/test). The val split is used only for early stopping during fine-tuning. The test split (
[8000:]) is never seen during training. - The model architecture is ResNet-18 with the first conv layer changed to
$3\times3$ /stride-1 and the max-pool removed. This is necessary because CIFAR-10 images are$32\times32$ and the standard ResNet-18 design would over-downsample them. - On Windows,
prepare_data.pyuses a multiprocessing pool for entropy computation but falls back to single-threaded if that fails. Theif __name__ == '__main__':guard is required for this to work correctly. - Training logs are appended, not overwritten. Delete
logs/train_all.logif you want a clean run.