b-ausoj/unified_eeg_benchmark

Python

0

120 commits

updated Sep 15, 2025

See the code

README

🧠 EEG-Bench

A standardized and extensible benchmark for evaluating classical and foundation models across clinical and BCI EEG decoding tasks.

This benchmark supports rigorous cross-subject and cross-dataset evaluation across 25 datasets. It includes clinical classification tasks (e.g. epilepsy, Parkinson’s, schizophrenia) and motor imagery paradigms (e.g. left vs. right hand, 5-finger decoding), and provides baselines from CSP to foundation models like BENDR, LaBraM, and NeuroGPT.

📦 Installation

Setup Environment

conda env create -f environment.yml
conda activate eeg_bench

Configure Paths

If you want to override the default settings, update the following:

  • In eeg_bench/config.json, modify the "data", "cache", and "chkpt" paths to point to your preferred directories.
  • In your MNE configuration, adjust the MOABB download paths accordingly.

📁 Project Structure

eeg_bench/
├── datasets/         # EEG dataset loaders (BCI & clinical)
├── models/           # All model implementations (CSP, LaBraM, etc.)
├── tasks/            # Benchmark tasks (MI, clinical diagnosis)
└── utils/            # Helpers
benchmark_console.py  # CLI interface to run experiments

🚀 Running a Benchmark

The benchmark can be run via the script benchmark_console.py with the arguments --model and --task.

python benchmark_console.py --model labram --task lr

With the option --all instead, it will run all tasks against all models. The number of repetitions can be set via --reps (default: 5).

Available Tasks

Task CodeTask Class
pdParkinsonsClinicalTask
szSchizophreniaClinicalTask
mtbiMTBIClinicalTask
ocdOCDClinicalTask
epEpilepsyClinicalTask
abAbnormalClinicalTask
lrLeftHandvRightHandMITask
rfRightHandvFeetMITask
lrftLeftHandvRightHandvFeetvTongueMITask
5fFiveFingersMITask
sleep_stagesSleepStagesClinicalTask
seizureSeizureClinicalTask
binary_artifactArtifactBinaryClinicalTask
multiclass_artifactArtifactMulticlassClinicalTask

Available Models

Model CodeModel Class
ldaCSP or Brainfeatures with LDA
svmCSP or Brainfeatures with SVM
labramLaBra
bendrBENDR
neurogptNeuroGPT

📋 Results

The following table reports the balanced accuracy scores achieved by every task against every model.

TaskTypeSVMLDABENDRNeuro-GPTLaBraM
LH vs RHAll0.6650.6600.665 ± .0110.649 ± .0050.672 ± .007
LH vs RHHeld-Out0.7850.7620.722 ± .0350.518 ± .0210.735 ± .029
RH vs FeetAll0.5800.5690.746 ± .0040.644 ± .0070.738 ± .007
RH vs FeetHeld-Out0.5060.7140.745 ± .0110.508 ± .0240.718 ± .014
LH vs RH vs Feet vs TAll0.2870.2910.625 ± .0030.378 ± .0100.638 ± .002
Five FingersSingle0.2060.1960.340 ± .0080.2301 ± .0040.354 ± .007
AbnormalSingle0.7220.6770.717 ± .0030.696 ± .0050.838 ± .011
EpilepsySingle0.5310.5310.740 ± .0150.734 ± .0100.565 ± .017
PDAll0.6340.6120.561 ± .0240.637 ± .0240.662 ± .027
PDHeld-Out0.5960.6150.589 ± .0700.662 ± .0380.739 ± .035
OCDSingle0.6330.7170.513 ± .0510.703 ± .0820.740 ± .044
mTBISingle0.6230.8130.491 ± .0420.585 ± .0710.658 ± .064
SchizophreniaSingle0.6790.5470.471 ± .0550.545 ± .0420.543 ± .045
Binary ArtifactSingle0.7450.7050.535 ± .0030.711 ± .0040.756 ± .007
Multiclass ArtifactSingle0.4370.3250.192 ± .0020.226 ± .0060.430 ± .015
Sleep StagesSingle0.6520.6710.169 ± .0010.166 ± .0030.192 ± .001
SeizureSingle0.5720.5290.501 ± .0010.500 ± .0000.588 ± .011

➕ Adding Your Own Dataset

So far this benchmark supports two paradigms: Clinical and BCI (Motor Imagery). In Clinical one has to classify an entire recording whereas in BCI, one classifies a short sequence (trial). To add your dataset:

  1. Place your class in datasets/bci/ or datasets/clinical/
  2. Inherit from BaseBCIDataset or BaseClinicalDataset
  3. Implement the following methods:
    1. _download: Either download the dataset automatically or provide instructions for the user to do so manually. Pay attention that, if possible, _download does not re-download the dataset if it already exists locally.

    2. load_data: This method should populate the following attributes:

      • self.data with type np.ndarray | List [BaseRaw] and dim (n_samples, n_channels, n_sample_length)
      • self.labels with type np.ndarray | List[str] and dim (n_samples, ), or (n_samples, n_multi_labels) for multilabel datasets
      • self.meta: A dictionary that must contain at least sampling_frequency, channel_names and name
    3. If your dataset contains classes not yet part of the enum enums.BCIClasses or enums.ClinicalClasses please add them accordingly.

    4. For multi-label datasets, you currently also have to add your dataset name to the

       elif dataset_name in [<MULTILABEL_DATASET_NAMES>]:
      

      clause in eeg_bench/models/clinical/brainfeatures/feature_extraction_2.py:_prepare_data_cached().

    5. To speed up further runs of the load_data function, implement caching as in the existing dataset classes.

    6. All EEG signals should be standardized to the microvolt (µV ) scale. To reduce memory usage and computational overhead, signals with sampling rate more than 250 Hz typically resampled to 250 Hz.

🧪 Adding Your Own Task

Tasks constitute the central organizing principle of the benchmark, encapsulating paradigms, datasets, prediction classes, subject splits (i.e., training and test sets), and evaluation metrics. Each task class implements a get_data() method that returns training or testing data, along with the corresponding labels and metadata. These predefined splits ensure evaluation consistency and facilitate reproducibility. The tasks are split into Clinical and BCI as well.

Each tasks defines:

  • The datasets to use
  • Train/test subject splits
  • Target classes
  • Evaluation metrics

To add your own task:

  • For BCI tasks, add your class to tasks/bci/ and inherit from AbstractBCITask
  • For clinical tasks, add your class to tasks/clinical/ and inherit from AbstractClinicalTask

Implement the get_data() method to return training/testing splits with data, labels, and metadata.

For multi-label tasks, you must also add its name to the get_multilabel_tasks() method in eeg_bench/utils/utils.py. Additionally, if you have special channel requirements, you might also want to add an

elif task_name == <YOUR_TASK_NAME>:
    t_channels = <YOUR_CHANNEL_LIST>

clause to _prepare_data_cached() in eeg_bench/models/clinical/brainfeatures/feature_extraction_2.py.

🤖 Add Your Own Model

To integrate a new model, implement the AbstractModel interface and place your code in:

  • models/bci/ for Motor Imagery (BCI) models
  • models/clinical/ for Clinical models

Your model must implement:

def fit(self, X: List[np.ndarray | List [BaseRaw]], y: List[np.ndarray | List[str]], meta: List[Dict]) -> None:
    # Each list entry corresponds to one dataset
    pass

def predict(self, X: List[np.ndarray | List [BaseRaw]], meta: List[Dict]) -> np.ndarray:
    # Predict on each dataset separately, return concatenated predictions
    pass

Run Your Model

Register your model in benchmark_console.py to run:

python benchmark_console.py --model mymodel --task <YOUR_DESIRED_TASK>

📊 Evaluation & Reproducibility

All experiments:

  • Use fixed subject-level splits
  • Support held-out dataset generalization
  • Report balanced accuracy and weighted F1-score
  • Use a fixed random seed for NumPy/PyTorch/random

Troubleshooting

Unfortunately, due to the many different packages and number of different models, there can be problems with the versions of libraries. Known problems with solutions are listed below:

  • RuntimeError: Failed to import transformers.training_args because of the following error (look up to see its traceback): No module named 'torch._six' or ModuleNotFoundError: No module named 'torch._six': One has to delete
    • the line 18 from torch._six import inf in conda_envs/eeg_bench/lib/python3.10/site-packages/deepspeed/runtime/utils.py
    • the line 9 from torch._six import inf in conda_envs/eeg_bench/lib/python3.10/site-packages/deepspeed/runtime/zero/stage2.py

b-ausoj/unified_eeg_benchmark

Python

0

120 commits

updated Sep 15, 2025

See the code

README

🧠 EEG-Bench

A standardized and extensible benchmark for evaluating classical and foundation models across clinical and BCI EEG decoding tasks.

This benchmark supports rigorous cross-subject and cross-dataset evaluation across 25 datasets. It includes clinical classification tasks (e.g. epilepsy, Parkinson’s, schizophrenia) and motor imagery paradigms (e.g. left vs. right hand, 5-finger decoding), and provides baselines from CSP to foundation models like BENDR, LaBraM, and NeuroGPT.

📦 Installation

Setup Environment

conda env create -f environment.yml
conda activate eeg_bench

Configure Paths

If you want to override the default settings, update the following:

  • In eeg_bench/config.json, modify the "data", "cache", and "chkpt" paths to point to your preferred directories.
  • In your MNE configuration, adjust the MOABB download paths accordingly.

📁 Project Structure

eeg_bench/
├── datasets/         # EEG dataset loaders (BCI & clinical)
├── models/           # All model implementations (CSP, LaBraM, etc.)
├── tasks/            # Benchmark tasks (MI, clinical diagnosis)
└── utils/            # Helpers
benchmark_console.py  # CLI interface to run experiments

🚀 Running a Benchmark

The benchmark can be run via the script benchmark_console.py with the arguments --model and --task.

python benchmark_console.py --model labram --task lr

With the option --all instead, it will run all tasks against all models. The number of repetitions can be set via --reps (default: 5).

Available Tasks

Task CodeTask Class
pdParkinsonsClinicalTask
szSchizophreniaClinicalTask
mtbiMTBIClinicalTask
ocdOCDClinicalTask
epEpilepsyClinicalTask
abAbnormalClinicalTask
lrLeftHandvRightHandMITask
rfRightHandvFeetMITask
lrftLeftHandvRightHandvFeetvTongueMITask
5fFiveFingersMITask
sleep_stagesSleepStagesClinicalTask
seizureSeizureClinicalTask
binary_artifactArtifactBinaryClinicalTask
multiclass_artifactArtifactMulticlassClinicalTask

Available Models

Model CodeModel Class
ldaCSP or Brainfeatures with LDA
svmCSP or Brainfeatures with SVM
labramLaBra
bendrBENDR
neurogptNeuroGPT

📋 Results

The following table reports the balanced accuracy scores achieved by every task against every model.

TaskTypeSVMLDABENDRNeuro-GPTLaBraM
LH vs RHAll0.6650.6600.665 ± .0110.649 ± .0050.672 ± .007
LH vs RHHeld-Out0.7850.7620.722 ± .0350.518 ± .0210.735 ± .029
RH vs FeetAll0.5800.5690.746 ± .0040.644 ± .0070.738 ± .007
RH vs FeetHeld-Out0.5060.7140.745 ± .0110.508 ± .0240.718 ± .014
LH vs RH vs Feet vs TAll0.2870.2910.625 ± .0030.378 ± .0100.638 ± .002
Five FingersSingle0.2060.1960.340 ± .0080.2301 ± .0040.354 ± .007
AbnormalSingle0.7220.6770.717 ± .0030.696 ± .0050.838 ± .011
EpilepsySingle0.5310.5310.740 ± .0150.734 ± .0100.565 ± .017
PDAll0.6340.6120.561 ± .0240.637 ± .0240.662 ± .027
PDHeld-Out0.5960.6150.589 ± .0700.662 ± .0380.739 ± .035
OCDSingle0.6330.7170.513 ± .0510.703 ± .0820.740 ± .044
mTBISingle0.6230.8130.491 ± .0420.585 ± .0710.658 ± .064
SchizophreniaSingle0.6790.5470.471 ± .0550.545 ± .0420.543 ± .045
Binary ArtifactSingle0.7450.7050.535 ± .0030.711 ± .0040.756 ± .007
Multiclass ArtifactSingle0.4370.3250.192 ± .0020.226 ± .0060.430 ± .015
Sleep StagesSingle0.6520.6710.169 ± .0010.166 ± .0030.192 ± .001
SeizureSingle0.5720.5290.501 ± .0010.500 ± .0000.588 ± .011

➕ Adding Your Own Dataset

So far this benchmark supports two paradigms: Clinical and BCI (Motor Imagery). In Clinical one has to classify an entire recording whereas in BCI, one classifies a short sequence (trial). To add your dataset:

  1. Place your class in datasets/bci/ or datasets/clinical/
  2. Inherit from BaseBCIDataset or BaseClinicalDataset
  3. Implement the following methods:
    1. _download: Either download the dataset automatically or provide instructions for the user to do so manually. Pay attention that, if possible, _download does not re-download the dataset if it already exists locally.

    2. load_data: This method should populate the following attributes:

      • self.data with type np.ndarray | List [BaseRaw] and dim (n_samples, n_channels, n_sample_length)
      • self.labels with type np.ndarray | List[str] and dim (n_samples, ), or (n_samples, n_multi_labels) for multilabel datasets
      • self.meta: A dictionary that must contain at least sampling_frequency, channel_names and name
    3. If your dataset contains classes not yet part of the enum enums.BCIClasses or enums.ClinicalClasses please add them accordingly.

    4. For multi-label datasets, you currently also have to add your dataset name to the

       elif dataset_name in [<MULTILABEL_DATASET_NAMES>]:
      

      clause in eeg_bench/models/clinical/brainfeatures/feature_extraction_2.py:_prepare_data_cached().

    5. To speed up further runs of the load_data function, implement caching as in the existing dataset classes.

    6. All EEG signals should be standardized to the microvolt (µV ) scale. To reduce memory usage and computational overhead, signals with sampling rate more than 250 Hz typically resampled to 250 Hz.

🧪 Adding Your Own Task

Tasks constitute the central organizing principle of the benchmark, encapsulating paradigms, datasets, prediction classes, subject splits (i.e., training and test sets), and evaluation metrics. Each task class implements a get_data() method that returns training or testing data, along with the corresponding labels and metadata. These predefined splits ensure evaluation consistency and facilitate reproducibility. The tasks are split into Clinical and BCI as well.

Each tasks defines:

  • The datasets to use
  • Train/test subject splits
  • Target classes
  • Evaluation metrics

To add your own task:

  • For BCI tasks, add your class to tasks/bci/ and inherit from AbstractBCITask
  • For clinical tasks, add your class to tasks/clinical/ and inherit from AbstractClinicalTask

Implement the get_data() method to return training/testing splits with data, labels, and metadata.

For multi-label tasks, you must also add its name to the get_multilabel_tasks() method in eeg_bench/utils/utils.py. Additionally, if you have special channel requirements, you might also want to add an

elif task_name == <YOUR_TASK_NAME>:
    t_channels = <YOUR_CHANNEL_LIST>

clause to _prepare_data_cached() in eeg_bench/models/clinical/brainfeatures/feature_extraction_2.py.

🤖 Add Your Own Model

To integrate a new model, implement the AbstractModel interface and place your code in:

  • models/bci/ for Motor Imagery (BCI) models
  • models/clinical/ for Clinical models

Your model must implement:

def fit(self, X: List[np.ndarray | List [BaseRaw]], y: List[np.ndarray | List[str]], meta: List[Dict]) -> None:
    # Each list entry corresponds to one dataset
    pass

def predict(self, X: List[np.ndarray | List [BaseRaw]], meta: List[Dict]) -> np.ndarray:
    # Predict on each dataset separately, return concatenated predictions
    pass

Run Your Model

Register your model in benchmark_console.py to run:

python benchmark_console.py --model mymodel --task <YOUR_DESIRED_TASK>

📊 Evaluation & Reproducibility

All experiments:

  • Use fixed subject-level splits
  • Support held-out dataset generalization
  • Report balanced accuracy and weighted F1-score
  • Use a fixed random seed for NumPy/PyTorch/random

Troubleshooting

Unfortunately, due to the many different packages and number of different models, there can be problems with the versions of libraries. Known problems with solutions are listed below:

  • RuntimeError: Failed to import transformers.training_args because of the following error (look up to see its traceback): No module named 'torch._six' or ModuleNotFoundError: No module named 'torch._six': One has to delete
    • the line 18 from torch._six import inf in conda_envs/eeg_bench/lib/python3.10/site-packages/deepspeed/runtime/utils.py
    • the line 9 from torch._six import inf in conda_envs/eeg_bench/lib/python3.10/site-packages/deepspeed/runtime/zero/stage2.py