Skip to content

[MRG] Add EEG classification task#6

Open
tgnassou wants to merge 26 commits into
benchopt:mainfrom
tgnassou:eeg
Open

[MRG] Add EEG classification task#6
tgnassou wants to merge 26 commits into
benchopt:mainfrom
tgnassou:eeg

Conversation

@tgnassou

@tgnassou tgnassou commented May 28, 2026

Copy link
Copy Markdown

DATASETS:

  • Add BNCI2014-001
  • Add SleepPhysionet
  • Add HGD

MODELS:

  • Add Baseline (EEGNet)
  • Add REVE
  • add CbraMod

@tgnassou tgnassou changed the title [WIP] Add EEG classification task [MRG] Add EEG classification task Jun 3, 2026
@tgnassou

tgnassou commented Jun 3, 2026

Copy link
Copy Markdown
Author

@tomMoral Ready to be merge in our side

@felixdivo

Copy link
Copy Markdown
Contributor

It might make sense to do one review from within the EGG subgroup?

@tgnassou

Copy link
Copy Markdown
Author

Do we need to be additional things ?

@felixdivo

Copy link
Copy Markdown
Contributor

Do we need to be [do?] additional things ?

I think a rebase on/merge of main into this would be helpful. The API around solvers changed substantially (e.g., #22, #28, #31, #39, #42), so this might solve some issues while we are still on the feature branch. Also, this should repair the currently failing CI pipeline.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an EEG classification track to the TSFM benchmark by adding three EEG datasets and three EEG-focused solvers (a baseline EEGNet classifier and two foundation-model + linear-probe solvers).

Changes:

  • Add EEG datasets: BNCI2014-001, Sleep PhysioNet, and HGD (motor imagery).
  • Add EEG solvers: EEGNet (braindecode), REVE + linear probe, and CBraMod + linear probe.
  • Plumb dataset metadata needed by foundation models (e.g., sampling frequency and channel names).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
solvers/reve.py Adds REVE foundation-model encoder with resampling + linear probe adapter.
solvers/eegnet.py Adds a braindecode EEGNetv4 training/prediction solver.
solvers/cbramod.py Adds CBraMod foundation-model encoder with resampling/truncation + linear probe adapter.
datasets/sleepphysionet.py Adds Sleep PhysioNet windowed sleep-stage classification dataset loader.
datasets/hgd.py Adds HGD motor-imagery classification dataset loader and preprocessing.
datasets/bnci2014_001.py Adds BNCI2014-001 motor-imagery classification dataset loader.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread datasets/hgd.py
Comment on lines +78 to +82
# windows_dataset.description contains a 'split' column ('train'/'test')
# Each window is indexed into its base dataset; we replicate that mapping.
descriptions = windows_dataset.datasets # list of WindowsDataset

for ds in descriptions:
Comment thread solvers/eegnet.py
Comment on lines +11 to +13
import numpy as np
import torch
from benchopt import BaseSolver
Comment thread solvers/eegnet.py
}


class EEGNetAdapter:
Comment on lines +3 to +6
Wraps the sleep recordings from the Sleep Physionet.
Each recording is split into a training portion (first 10 %) and a test
portion.
Labels are from 0 to 4, corresponding to the sleep stages W, N1, N2, N3, REM.


class Dataset(BaseDataset):
"""Sleep classification dataset (TSB-UAD).
Comment on lines +85 to +87
factor : float
Factor to multiply the raw signals by (e.g. to convert from V to uV
train_ratio : float
Comment thread datasets/bnci2014_001.py
@@ -0,0 +1,126 @@
import numpy as np
Comment thread datasets/bnci2014_001.py
y_test = np.concatenate(
[y_all[i] for i in range(len(y_all)) if i not in ids_train]
)
np.unique(y_train), np.unique(y_test) # sanity check
Comment thread solvers/eegnet.py
Comment on lines +100 to +115
X_t = torch.tensor(X, dtype=torch.float32, device=self._device)
y_t = torch.tensor(y, dtype=torch.long, device=self._device)

dataset = torch.utils.data.TensorDataset(X_t, y_t)
loader = torch.utils.data.DataLoader(
dataset, batch_size=self.batch_size, shuffle=True
)

self._network.train()
for _ in range(self.n_epochs):
for xb, yb in loader:
self._optimizer.zero_grad()
logits = self._network(xb)
loss = self._criterion(logits, yb)
loss.backward()
self._optimizer.step()
Comment thread solvers/eegnet.py
Comment on lines +152 to +161
X = self._prepare_inputs(np.asarray(X, dtype=np.float32))
X_t = torch.tensor(X, dtype=torch.float32, device=self._device)

self._network.eval()
preds = []
with torch.no_grad():
for i in range(0, len(X_t), self._batch_size):
logits = self._network(X_t[i:i + self._batch_size])
preds.append(logits.argmax(dim=1).cpu().numpy())
return np.concatenate(preds)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants