Skip to content
SpeedTomyPublic

About

Leakage-aware deep-learning pipeline for imagined-digit decoding from MindBigData EEG — scikit-learn baselines + PyTorch 1D-CNN/GRU ensemble (21.3% acc / 19.8% macro-F1 vs 10% chance).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

MindBigData EEG Digit Classification

End-to-end neural-network pipeline that tries to decode digits 0–9 from raw EEG signals in the MindBigData dataset, with an honest comparison to the published literature.

Final score: 21.30% test accuracy / 19.80% macro-F1 on the held-out EPOC test set, 19.91% ± 0.63% accuracy under 5-fold cross-validation. Random chance on 10 balanced classes is 10%. Legitimate published results on the same task sit in the 16–22% range (Tsamourgelis & Adamopoulos, 2025).

Animated pipeline demo

Real EPOC test event #7190: true label = 7, ensemble prediction = 7 with 84% confidence.


Why this problem is harder than it looks

MindBigData is sometimes called the "MNIST of brain digits", but the analogy is misleading:

  • The input is a noisy multi-channel EEG signal recorded from a consumer EEG headset, not a clean 28×28 image.
  • Data come from a single subject (D. Vivancos, 2014–2015), so there is no between-subject generalization to talk about.
  • The dataset has no official train/test split, and a same trial produces several rows in the raw file (one per electrode). Splitting by row instead of by event silently leaks the test set into training and inflates the accuracy reported in many papers.
  • A peer-reviewed review of the field (Tsamourgelis & Adamopoulos, Stats 2025) attributes most published 70–98% numbers to data leakage and dimension mishandling. The honest range is closer to 16–22% accuracy on the 10-class task.

The goal of this project is not to chase that headline number but to build a calibrated pipeline that respects the dataset's structure and to quantify what actually works.


Headline results (EPOC, 14-channel device)

Approach Test acc Test macro-F1
1D CNN, 20 epochs 15.05% 13.68%
Lightweight Transformer 15.44% 11.36%
CNN+GRU baseline 16.64% 15.46%
CNN+GRU + temporal derivatives 17.60% 16.44%
Previous best derivative ensemble 18.41% 17.54%
Augmented CNN+GRU (single seed) 20.03% 18.78%
3-seed augmented ensemble 20.81% 19.46%
5-seed augmented + Test-Time Augmentation 21.30% 19.80%

A 5-fold cross-validation of the augmented single-seed recipe gives a mean accuracy of 19.91% ± 0.63% and mean macro-F1 of 18.27% ± 0.76%, confirming the single-split numbers are not a lucky tirage.

A separate binary sanity check (blank screen vs. any digit, 159 vs. 159, balanced) reaches 97.9% test accuracy with the same architecture. Detecting that a stimulus was shown is trivial; identifying which digit is the actual hard problem.


What actually moved the score

Each row below is an isolated change relative to the row above it.

Change Effect
Per-event z-score normalization (vs. global) base (vs. ~chance)
FFT band-pass 1–45 Hz / 0.5–45 Hz neutral
Clip to [-3, +3] neutral
Append first temporal derivatives (14 → 28 ch.) +1.3 pts F1
EEG augmentation (time jitter, gaussian noise, channel dropout, mixup) +2.3 pts F1
Cosine LR + warmup + label smoothing small but stable
5-seed equal-weight ensemble +0.9 pts F1
Test-Time Augmentation (n=8, jitter=6, noise=0.02) +0.1 pts F1

The takeaway: an EEG-specific augmentation pipeline + multi-seed ensembling does most of the lifting; "clever" preprocessing tricks like band-pass and clipping do not.


Repository layout

.
├── README.md                       (this file)
├── LICENSE                         MIT
├── requirements.txt
├── src/                            data loaders, preprocessing, models, trainers
├── tests/                          unit tests for every public function
├── notebooks/
│   ├── Insight_Project_Report.ipynb
│   ├── EPOC_Colab_Pipeline.ipynb   GPU pipeline with EEGNet + EEG-Conformer
│   └── build_colab_notebook.py
├── configs/
│   └── epoc_heterogeneous_ensemble.json
├── docs/
│   ├── presentation.tex            10-minute LaTeX/Beamer summary
│   ├── figures/                    PDF/JPG/GIF outputs
│   ├── insight/insight_results.md
│   └── epoc/epoc_plan.md
├── results/metrics/                JSON metric files per experiment
└── JOURNAL.md                      day-by-day project log

Raw and preprocessed data files, .pt model weights, and intermediate artifacts are intentionally gitignored — the pipeline regenerates them from scratch.


Reproducing the results

# 1. Environment
python3 -m venv .venv && .venv/bin/python -m pip install -r requirements.txt

# 2. Download the MindBigData EPOC zip from https://www.mindbigdata.com/opendb/
#    and extract it to data/raw/epoc/EP1.01.txt

# 3. Preprocess (resample 256, per-event z-score, stratified split by event)
.venv/bin/python -m src.preprocess_mindbigdata --device EP --exclude-label -1

# 4. Export to NumPy for fast loading
.venv/bin/python -m src.export_events_npz \
    --events data/processed/epoc/epoc_events_256.jsonl.gz \
    --splits data/processed/epoc/epoc_splits.json \
    --output data/processed/epoc/epoc_events_256.npz

# 5. Append temporal derivatives (14 channels -> 28)
.venv/bin/python -m src.add_temporal_derivatives \
    --input data/processed/epoc/epoc_events_256.npz \
    --output data/processed/epoc/epoc_events_256_with_derivatives.npz \
    --summary-output results/metrics/epoc/derivatives_summary.json

# 6. Train the 5 augmented seeds (any order)
for seed in 52 7 21 99 13; do
  .venv/bin/python -m src.train_cnn_gru_augmented \
    --events data/processed/epoc/epoc_events_256_with_derivatives.npz \
    --splits data/processed/epoc/epoc_splits.json \
    --metrics-output results/metrics/epoc/cnn_gru_augmented_seed${seed}_metrics.json \
    --model-output  models/epoc/cnn_gru_augmented_seed${seed}.pt \
    --epochs 35 --batch-size 128 --gru-hidden-size 96 --seed $seed
done

# 7. Evaluate the 5-seed ensemble with TTA
.venv/bin/python -m src.evaluate_ensemble_tta \
    --members configs/epoc_heterogeneous_ensemble.json \
    --output results/metrics/epoc/ensemble_5seeds_augmented_tta8_metrics.json \
    --n-passes 8 --time-jitter 6 --noise-std 0.02

# 8. (optional) 5-fold cross-validation sanity check
.venv/bin/python -m src.make_kfold_indices \
    --events data/processed/epoc/epoc_events_256_with_derivatives.npz \
    --output data/processed/epoc/epoc_kfold5_indices.json
.venv/bin/python -m src.train_kfold_cv \
    --events data/processed/epoc/epoc_events_256_with_derivatives.npz \
    --folds  data/processed/epoc/epoc_kfold5_indices.json \
    --output-dir results/metrics/epoc/kfold5

A Colab-ready notebook (notebooks/EPOC_Colab_Pipeline.ipynb) reproduces the augmented CNN+GRU and trains heavier architectures (EEGNet and EEG-Conformer) on a GPU. It expects a quantized int8 .npz upload (~205 MB) to keep Colab's web upload short.


Acknowledgements

  • Dataset. MindBigData (David Vivancos), https://www.mindbigdata.com/opendb/. Subject-level EEG recordings during visual digit presentation.
  • Calibration benchmark. A. Tsamourgelis & A. Adamopoulos, EEG Signal Analysis for Numerical Digit Classification: Methodologies and Challenges, Stats 8(1):14, MDPI, 2025. Provides a careful review of the field and the legitimate accuracy range used to calibrate this project.
  • EEGNet / EEG-Conformer. Lawhern et al. 2018; Song et al. 2022. Implemented in src/train_eegnet.py and in the Colab notebook.

This repository started as a 6-week mini-project for an undergraduate neural-networks course. The methodology, calibration, and write-up are the parts of the work I would defend; the absolute score is, on purpose, where the literature says it ought to be.

License

MIT — see LICENSE.

About

Leakage-aware deep-learning pipeline for imagined-digit decoding from MindBigData EEG — scikit-learn baselines + PyTorch 1D-CNN/GRU ensemble (21.3% acc / 19.8% macro-F1 vs 10% chance).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages