EEG_MI / src /foundation /README.md
Twu31's picture
Mirror of github.com/twu3202/EEG_MI at 66726e1
d37c079 verified
|
Raw
History Blame Contribute Delete
3.42 kB

EEG Foundation-Model probing

Goal: reuse a frozen pretrained EEG backbone and fit only a light linear/MLP probe on labelled MI data β€” i.e. no full retraining. This is how we'll test the 32-ch cap data later, and it's how we get an apples-to-apples comparison against the pyRiemann baselines.

Where things run

  • M5 Mac (MPS): fine for inference / linear-probing of the small models (EEGPT β‰ˆ 10M params, CBraMod). Use device="mps".
  • GPU server: needed only for fine-tuning the backbone or pretraining.

Setup

bash src/foundation/setup_foundation.sh   # clone repos + fetch weights

Repos land in third_party/, weights in checkpoints/.

Candidates (all channel-flexible, weights public)

Model Repo Input it expects License
EEGPT BINE022/EEGPT 58ch/256Hz/4s, patch 64 see repo
CBraMod wjq-learning/CBraMod (ch, seg, 200 pts/patch)=200Hz MIT
LaBraM 935963004/LaBraM 200Hz, patch, channel names see repo

Probe recipe (identical across models)

  1. Pull MI epochs from MOABB (same LeftRightImagery paradigm as the baseline) so the comparison is fair.
  2. Resample to the model's native rate (EEGPT 256 Hz, CBraMod/LaBraM 200 Hz); map our channel names to the model's channel embedding.
  3. Forward pass through the frozen backbone β†’ per-trial embedding.
  4. Fit LogisticRegression (or a 1-layer MLP) on embeddings; evaluate cross-subject exactly like the baseline.

probe_moabb.py implements this loop; encoders live in encoders.py.

Status (cross-subject 2a, ROC-AUC β€” see ../../results/RESULTS.md)

encoder wired? 2a AUC notes
raw βœ… 0.521 floor
cbramod βœ… (MPS) 0.560 weights in checkpoints/CBraMod/
labram βœ… (MPS) 0.595 weights ship in third_party/LaBraM/checkpoints/
eegpt ⬜ β€” needs the figshare checkpoint (below)

Run: python src/foundation/probe_moabb.py --dataset 2a --subjects 9 --encoder labram --fmin 0.5 --fmax 45 (set PYTORCH_ENABLE_MPS_FALLBACK=1 for the transformer ops MPS doesn't cover).

All frozen probes underperform the classic Riemannian baseline (~0.78) β€” expected: MI is a subtle spatial ERD contrast that a frozen generic backbone doesn't expose linearly. FMs need fine-tuning (not frozen probing) to compete.

Gotchas already handled (in encoders.py)

  • Pool over time only, keep channels β€” averaging over channels drives MI to chance.
  • Scale = Β΅V/100 (scale=0.01); MOABB returns Β΅V-scale here. Feed broadband 0.5–45 Hz.
  • LaBraM + timm 1.x: _timm1x_shim() aliases the old timm.models.layers/.registry.
  • PyTorch 2.6 torch.load: LaBraM ckpt needs weights_only=False (trusted repo file).

EEGPT (not yet wired β€” needs manual download)

Checkpoint is on figshare (blocked from automated fetch): https://figshare.com/s/e37df4f8a907a866df4b β†’ eegpt_mcae_58chs_4s_large4E.ckpt (58 ch, 256 Hz, 4 s, patch 64). Download it into checkpoints/, then mirror third_party/EEGPT/downstream/linear_probe_EEGPT_BCIC2A.py (EEGPTClassifier in downstream/Modules/models/EEGPT_mcae_finetune.py) as an EEGPTEncoder. Note its native rate is 256 Hz β€” run the probe with --sfreq 256.