Engineering
Brain Tumor MRI Classification
- Role
- The Vision Transformer, the evaluation harness, and the GAN augmentation
- Period
- Feb 2026 – May 2026
- Stack
- Python
- PyTorch
- ResNet-50
- ViT-B/16
- Scikit-learn
- Streamlit
Context#
Four-way classification of 2D brain MRI slices into glioma, meningioma, pituitary and no tumor, plus a binary tumor against no-tumor variant. The data is Masoud Nickparvar's Brain Tumor MRI Dataset on Kaggle, itself a merge of three upstream collections, and it mixes acquisition planes and contrast types freely inside each class.

What we built#
Five model families, chosen to span the space rather than to win: a Random Forest over HOG and LBP features, a custom CNN with squeeze-and-excitation blocks at 6.6M parameters, a fine-tuned ResNet-50 at 24M, a fine-tuned ViT-B/16 at 86M, and a class-conditional GAN generating extra training data. Eleven runs, about a hundred minutes total on one H100.
Four of us built it over a semester. I worked on the Vision Transformer, the evaluation harness, the GAN augmentation pipeline and the Streamlit inference app. Mario Saenz wrote the preprocessing module and contributed more commits than I did.
The best 4-class result was 0.9575 accuracy, from the GAN-augmented ResNet-50.
Duplicates across the split#
Hashing the JPEG files finds no duplicates anywhere in the dataset. Hashing the decoded pixel arrays finds 114 test images, 7.125% of the split, that are byte-identical to an image in training. The containers differ; the pixels do not.

At a cosine threshold of 0.99, 455 test images (28.4%) have a near-duplicate in training. The same query against training images of a different class returns 2 hits out of 1,600, so the threshold separates real duplicates from generic head-shape similarity rather than flagging everything.
Recomputed over the 1,145 test images that have no near-duplicate:
reported clean subset delta
resnet50_aug 0.9575 0.9424 -0.0151
resnet50 0.9487 0.9319 -0.0168
vit 0.9487 0.9310 -0.0177
custom_cnn 0.9450 0.9275 -0.0175
baseline_rf 0.8806 0.8358 -0.0448
That column is indicative rather than a drop-in replacement, since removing duplicates guts the per-class support unevenly: no-tumor falls from 400 test images to 58. The same ordering shows up a step earlier, at 0.9971 train accuracy, 0.9902 validation and 0.9575 test, because training and validation are split file-wise out of one directory and share its duplication while test comes from a separate one.
The source is traceable. This copy of the dataset had been rebalanced by hand to exactly 1400 training and 400 test images per class against a canonical 7,023 total, with no script anywhere that does it. Images were copied until the classes came out even, and the copies crossed the split.
The GAN collapsed#
Sampling eight near-orthogonal latent vectors per class from the trained generator produces outputs with 0.9999 mean pairwise cosine similarity. The generator ignores its input and emits one fixed image per class.

Run with --gen-per-class 500, it therefore added four distinct images
duplicated 500 times each, not 2,000 synthetic samples. The augmented run
finished 0.88 points above the plain ResNet-50, but that margin cannot be
attributed to the synthetic data: each configuration was trained exactly once,
and the augmented run is the only one of the four whose best validation loss
lands on its final epoch, so it stopped at the 20-epoch cap while still
improving where the plain run stopped on patience.

Glioma sets the ceiling#
Per-class recall on the 4-class problem, test split:
glioma mening notumor pituit
resnet50_aug 0.8375 0.9975 1.0000 0.9950
resnet50 0.8150 0.9925 0.9925 0.9950
vit 0.8125 0.9875 1.0000 0.9950
custom_cnn 0.8075 0.9725 1.0000 1.0000
baseline_rf 0.6675 0.8800 1.0000 0.9750
Every model is worst on glioma and at or near perfect on no-tumor. Across the five families the spread is 0.1700 on glioma and 0.0075 on no-tumor. ViT-B/16 carries thirteen times the custom CNN's parameters and buys 0.005 of glioma recall with them, and a Random Forest doing no representation learning at all fails in the same direction as everything above it.

The failures are not the subtle tumors. Nearly every one carries a large, unmistakable lesion. What they share is that they do not look like the training distribution as images: set against the 335 gliomas the same model got right, they are roughly twice as bright, carry five times the bright-area fraction, and hold 57.8 times the fraction of near-saturated white pixels.
At least three have the thick bright skull ring of a CT rather than an MRI, though the dataset ships no DICOM headers and states no modality, so that reads the pixels rather than establishing a fact. Most of the rest are bright-lesion-on-dark images in a collection whose dominant appearance is the reverse. The model fails on unfamiliar image types rather than on hard tumors, and no-tumor is what it returns when an image resembles nothing in training.
Those 21 errors are also only 19 distinct images. Two pairs among them are pixel-identical to each other, inside the test set.
What this shows#
Every number here comes from a single run. The seed is set but cuDNN determinism is not, and no configuration was trained twice, so differences of a few tenths of a point are not separable from run-to-run variance.
The binary results need their floor quoted alongside them. That split is 1,200 tumor against 400 no-tumor, so always answering tumor scores 0.7500, and the best binary accuracy of 0.9831 is a gain of 13.31 points over that. The Random Forest baseline is also not like-for-like: it was selected on test accuracy and trained on 5,600 images where the deep models trained on 4,480.
This is coursework on 2D slices from a public dataset, never evaluated against radiologist reads or on clinical data. It is not a diagnostic tool.