Skip to content

Repository files navigation

Cerebral Aneurysm Classification

Detecting and localizing cerebral aneurysms in digital subtraction angiography

Python PyTorch Task Status

English | 한국어 · Training guide

Warning

Research and educational project. Not a medical device. This system has not been clinically validated and must not be used for diagnosis or patient care.


Overview

A cerebral aneurysm is a localized dilation of an intracranial artery; rupture is frequently fatal, so detecting one on angiography matters clinically. This repository frames that detection as two coupled learning problems over anonymized digital subtraction angiography (DSA):

Task Output
1 Detection — is an aneurysm present? one real value in [0, 1] per patient
2 Localization — which arterial segments? binary label for each of 21 segments

Each patient contributes 8 angiographic views. Since no single view exposes every artery, images and labels are partitioned along the brain's anterior (internal carotid) and posterior (vertebral) circulation before training, producing four models that are combined by an explicit decision rule.

Project goal

What this repository contains. The original system placed first (Grand Prize) in the 2023 K-ium medical AI competition, but its training code was not preserved — only the trained checkpoints and a written description. The code here is an open reimplementation reconstructed from that description: a complete, runnable pipeline covering data construction, preprocessing, training, evaluation, and challenge-format inference. Reproduction results are reported in Results; they do not currently match the original system.


Results

Validation figures from the reimplementation, on a patient-grouped 80/20 split (seed 42, ~900 patients). AUROC is the competition's primary metric.

Task Model Metric Result Random baseline
Detection — anterior MedNet (ResNet-18) AUROC 0.677 0.500
Detection — posterior MedNet (ResNet-18) AUROC 0.490 0.500
Localization — anterior SwinV2-tiny mAP 0.071 ~0.043
Localization — posterior ResNet-18 mAP 0.051 ~0.013

How to read this. Anterior detection is the only task with a clear margin over chance (AUROC 0.677), and it remains weak — useful as a baseline, not as a decision aid. Posterior detection sits at chance: the model learned nothing usable. Both localization scores are low in absolute terms, though posterior is roughly 3.8× its random baseline and anterior about 1.7×, so some ranking signal exists. Accuracy is deliberately excluded from this table: with 1–4% positive label cells, predicting all-zeros scores 96–99% while being clinically worthless.

Reproduce these numbers with scripts/evaluate.py; see TRAINING.md. Nothing in this table is estimated.

Why the gap

Three factors are identifiable rather than speculative:

  1. Severe class imbalance. Positive views: 22.8% anterior, 7.2% posterior for detection; 4.1% and 1.2% of label cells for localization. Posterior aneurysms are both rarer and smaller.
  2. Label noise from view geometry. Contrast injected on one side often opacifies the other, so a patient-level positive label may describe an aneurysm not visible in the view it is attached to.
  3. Lost preprocessing detail. The original text/margin-removal coordinates were not recorded; the reconstruction infers them, and residual annotation artifacts are plausible confounders.

See Current status for what is being addressed.


Method

Pipeline

Pipeline

Task formulation

Brain circulation splits into anterior (internal carotid, ICA) and posterior (vertebral, VA) systems, and labels are grouped to match:

Circulation Backbone vessel Segments
Anterior ICA ICA, AntChor, ACA, ACOM, MCA
Posterior VA VA, PICA, SCA, BA, PCA, PCOM

This yields four datasets — detection and localization, each split anterior and posterior.

Data

train.csv holds one row per patient: Index, Aneurysm (0/1), and 21 location columns. Each patient has 8 images, {Index}{SIDE}{VESSEL}-{ANGLE}.jpg:

Suffix Injection Circulation Labelled from
LI-A, LI-B left internal carotid anterior L_* anterior columns
RI-A, RI-B right internal carotid anterior R_* anterior columns
LV-A, LV-B left vertebral posterior L_* posterior columns
RV-A, RV-B right vertebral posterior R_* posterior columns

A and B are two projection angles. BA (basilar artery) is midline with no L/R variant and is shared by both posterior sides — which is why 5 anterior + 6 posterior labels map onto 21 CSV columns rather than 22.

One patient row therefore expands to 4 anterior + 4 posterior training rows. Left/right are merged at the label level and resolved again at inference.

The dataset is private, anonymized medical imaging and is not distributed here.

Dataset construction

Preprocessing

Training images arrive in three forms, handled separately:

Form Handling
No margin, no text used unchanged
Text + margin margin removed by pixel value; the fixed text region is masked and filled with mean background sampled from an adjacent patch
Margin only margin width varies per image, so horizontal and vertical center lines are scanned for the gray boundary and the image is cropped there

Training-time augmentation applies rotation (±10°), translation, scaling, and brightness/contrast jitter. Horizontal flipping is deliberately excluded — laterality is anatomically meaningful, and each view is labelled with its own side's columns, so mirroring would contradict the label.

Models

Task Backbone Initialization Loss
Detection (both) ResNet-18 MedicalNet (Med3D) BCEWithLogitsLoss, positive-weighted
Localization — anterior swinv2_cr_tiny_ns_224 (timm) ImageNet Asymmetric Loss
Localization — posterior ResNet-18 (torchvision) ImageNet Asymmetric Loss

MedicalNet ships 3D kernels ([out, in, D, H, W]) from volumetric pretraining. src/models.py deflates them to 2D by averaging over the depth axis — the inverse of I3D inflation — and adapts the stem to single-channel input before loading, so the grayscale medical pretraining is actually used. Coverage is reported at load time (~84%; the checkpoint contains no shortcut projections), and if it falls below a threshold the builder falls back to ImageNet rather than silently training from scratch.

Model architecture

Decision rule

  1. Detect. Views scoring above the binary threshold are treated as aneurysm-bearing; all others have every location label forced to 0.
  2. Localize. Positive views run through the location model; each segment is thresholded at its own 90th percentile of predicted scores.
  3. Aggregate. Anterior and posterior scores are averaged over their 4 views each; the larger average becomes the patient-level score. Per-view location predictions map back to L_/R_ columns by originating view, with BA pooled across all posterior views.

Getting started

git clone https://github.com/pmy02/Cerebral_Aneurysm_Classification.git
cd Cerebral_Aneurysm_Classification
git lfs install && git lfs pull        # fetch checkpoints

conda create -n aneurysm python=3.10 -y && conda activate aneurysm
pip install torch torchvision          # macOS; use the CUDA index on Linux
pip install -r requirements.txt
python -m scripts.check_env                          # verify CUDA / Apple MPS
python -m scripts.check_data  --csv data/train_set/train.csv --image-root data/train_set
python -m scripts.train       --config configs/binary_anterior.yaml \
    --csv data/train_set/train.csv --image-root data/train_set
python -m scripts.evaluate    --config configs/binary_anterior.yaml \
    --checkpoint Model/MedNet_ant_binary.pt \
    --csv data/train_set/train.csv --image-root data/train_set
python -m scripts.predict     --test-csv data/test_set/test.csv \
    --image-root data/test_set --out output.csv

Training runs on CUDA, Apple Silicon (MPS), or CPU — detected automatically. On a Mac use the lighter configs/mac/*.yaml presets.

Full walkthrough: TRAINING.md · 한국어 — setup, data layout, per-model commands, runtimes, troubleshooting.


Repository structure

Cerebral_Aneurysm_Classification/
├── src/
│   ├── data.py             # CSV → per-view frames, patient split, transforms
│   ├── preprocessing.py    # margin / text removal and cropping
│   ├── models.py           # MedNet (Med3D→2D), SwinV2, ResNet-18 builders
│   ├── losses.py           # Asymmetric Loss
│   ├── metrics.py          # AUROC, F1, mAP, per-class support
│   ├── checkpoint.py       # weight loading with layer-match reporting
│   └── device.py           # CUDA / MPS / CPU selection
├── scripts/
│   ├── check_env.py        # verify GPU acceleration
│   ├── check_data.py       # pre-flight dataset validation
│   ├── train.py            # config-driven training with early stopping
│   ├── evaluate.py         # checkpoint → metrics table
│   ├── predict.py          # test set → challenge-format output.csv
│   ├── infer.py            # decision-rule helpers
│   └── gradcam.py          # Grad-CAM heatmaps
├── configs/                # one YAML per task × circulation
│   └── mac/                # lighter Apple Silicon presets
├── Model/                  # checkpoints (Git LFS)
├── TRAINING.md             # local training guide
└── README.md

Current status

Working

  • End-to-end pipeline: data construction → training → evaluation → submission CSV
  • Patient-grouped splitting (no view-level leakage between train and validation)
  • MedicalNet 3D→2D transfer with explicit coverage reporting
  • Cross-platform training (CUDA / Apple MPS / CPU)

Known limitations

  • Posterior detection performs at chance; not usable as-is
  • Localization is weak in absolute terms for both circulations
  • Rare segments (e.g. SCA, PCOM, PCA) can have zero positive examples in a validation split; those are excluded from macro averages and flagged rather than silently averaged in
  • Preprocessing coordinates are inferred, not recovered from the original

Next steps

  • Patient-level cross-validation instead of a single split, so rare segments are evaluable
  • Loss and sampling strategies targeted at the posterior imbalance
  • Recovering or re-deriving the original preprocessing parameters

Explainability

Grad-CAM was applied to text- and margin-stripped images without augmentation to inspect which regions drove predictions. Heatmaps were interpretable, but were used for qualitative inspection rather than rigorous attribution, since they were not calibrated against ground-truth localization accuracy.

Grad-CAM example

Exploratory experiments

Approaches tried, including those that did not work:

Approach Outcome
Rule-based cropping around positive segments Abandoned — two aneurysms in one segment still yield a single 1, so exact positions are unrecoverable
Splitting by position / angle / direction, with weighted sampling Imbalance dominated; weighting gave marginal gains
Self-supervised pretraining (autoencoder, 200 epochs) Imbalance still limited downstream supervised performance
Contrastive learning over ResNet-50 features Same limitation
ResNet-18 with fcconv head (preserve spatial information) F1 flat at 53.68% across epochs
DenseNet for stronger feature extraction No decisive improvement

Contact

Minyoung Park · LinkedIn · minyo0119@gmail.com

About

Deep learning pipeline for detecting and localizing cerebral aneurysms in digital subtraction angiography (DSA) — MedNet, SwinV2, ResNet-18

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages