Braindecode 1.5.0.dev985__tar.gz → 1.5.0.dev989__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {braindecode-1.5.0.dev985/braindecode.egg-info → braindecode-1.5.0.dev989}/PKG-INFO +1 -1
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/__init__.py +0 -2
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/signal_jepa.py +38 -446
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/summary.csv +0 -1
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/util.py +0 -5
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/preprocessing/eegprep_preprocess.py +6 -51
- braindecode-1.5.0.dev989/braindecode/version.py +1 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode.egg-info/SOURCES.txt +0 -1
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/api.rst +0 -3
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/whats_new.rst +0 -12
- braindecode-1.5.0.dev985/braindecode/models/codebrain.py +0 -977
- braindecode-1.5.0.dev985/braindecode/version.py +0 -1
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/LICENSE.txt +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/MANIFEST.in +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/NOTICE.txt +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/README.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/augmentation/base.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/classifier.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/base.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/datautil/util.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/functional/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/functional/functions.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/base.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/bendr.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/biot.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/brainmodule.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/config.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/deep4.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eeginception_mi.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegnex.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegpt.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegsym.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/labram.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/luna.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/medformer.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/msvtnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/patchedtransformer.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/reve.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/tcn.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/models/usleep.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/activation.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/attention.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/blocks.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/filter.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/layers.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/linear.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/stats.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/util.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/regressor.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/samplers/base.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/training/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/training/losses.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/training/scoring.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/util.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode/visualization/gradients.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/Makefile +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/cite.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/conf.py +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/help.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/index.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/install/install.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/install/install_pip.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/install/install_source.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/models.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/models_table.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/pyproject.toml +0 -0
- {braindecode-1.5.0.dev985 → braindecode-1.5.0.dev989}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braindecode
|
|
3
|
-
Version: 1.5.0.
|
|
3
|
+
Version: 1.5.0.dev989
|
|
4
4
|
Summary: Deep learning software to decode EEG, ECG or MEG signals
|
|
5
5
|
Author-email: Robin Tibor Schirrmeister <robintibor@gmail.com>, Bruno Aristimunha Pinto <b.aristimunha@gmail.com>, Alexandre Gramfort <agramfort@meta.com>
|
|
6
6
|
Maintainer-email: Alexandre Gramfort <agramfort@meta.com>, Bruno Aristimunha Pinto <b.aristimunha@gmail.com>, Robin Tibor Schirrmeister <robintibor@gmail.com>
|
|
@@ -8,7 +8,6 @@ from .bendr import BENDR
|
|
|
8
8
|
from .biot import BIOT
|
|
9
9
|
from .brainmodule import BrainModule
|
|
10
10
|
from .cbramod import CBraMod
|
|
11
|
-
from .codebrain import CodeBrain
|
|
12
11
|
from .contrawr import ContraWR
|
|
13
12
|
from .ctnet import CTNet
|
|
14
13
|
from .deep4 import Deep4Net
|
|
@@ -73,7 +72,6 @@ __all__ = [
|
|
|
73
72
|
"BIOT",
|
|
74
73
|
"BENDR",
|
|
75
74
|
"CBraMod",
|
|
76
|
-
"CodeBrain",
|
|
77
75
|
"ContraWR",
|
|
78
76
|
"CTNet",
|
|
79
77
|
"Deep4Net",
|
|
@@ -22,179 +22,12 @@ _DEFAULT_CONV_LAYER_SPEC = ( # downsampling: 128Hz -> 1Hz, receptive field 1.18
|
|
|
22
22
|
(64, 2, 2),
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
# The 62 channels used to pre-train Signal-JEPA. Derived from
|
|
26
|
-
# `moabb.datasets.Lee2019_SSVEP` subject 1 session '1' run '1train'
|
|
27
|
-
# (the first 62 ch_names; the last 5 of the dataset's 67 channels are
|
|
28
|
-
# 4 EMG + 1 stim). Positions are from MNE's `standard_1005` montage.
|
|
29
|
-
# Names and ORDER MUST match the _ChannelEmbedding row order of the
|
|
30
|
-
# published HuggingFace checkpoint `braindecode/signal-jepa`.
|
|
31
|
-
_PRETRAIN_CHS_INFO: list[dict] = [
|
|
32
|
-
{"ch_name": "Fp1", "loc": [-0.0294367, 0.0839171, -0.00699]},
|
|
33
|
-
{"ch_name": "Fp2", "loc": [0.0298723, 0.0848959, -0.00708]},
|
|
34
|
-
{"ch_name": "F7", "loc": [-0.0702629, 0.0424743, -0.01142]},
|
|
35
|
-
{"ch_name": "F3", "loc": [-0.0502438, 0.0531112, 0.042192]},
|
|
36
|
-
{"ch_name": "Fz", "loc": [0.0003122, 0.058512, 0.066462]},
|
|
37
|
-
{"ch_name": "F4", "loc": [0.0518362, 0.0543048, 0.040814]},
|
|
38
|
-
{"ch_name": "F8", "loc": [0.0730431, 0.0444217, -0.012]},
|
|
39
|
-
{"ch_name": "FC5", "loc": [-0.0772149, 0.0186433, 0.02446]},
|
|
40
|
-
{"ch_name": "FC1", "loc": [-0.0340619, 0.0260111, 0.079987]},
|
|
41
|
-
{"ch_name": "FC2", "loc": [0.0347841, 0.0264379, 0.078808]},
|
|
42
|
-
{"ch_name": "FC6", "loc": [0.0795341, 0.0199357, 0.024438]},
|
|
43
|
-
{"ch_name": "T7", "loc": [-0.0841611, -0.0160187, -0.009346]},
|
|
44
|
-
{"ch_name": "C3", "loc": [-0.0653581, -0.0116317, 0.064358]},
|
|
45
|
-
{"ch_name": "Cz", "loc": [0.0004009, -0.009167, 0.100244]},
|
|
46
|
-
{"ch_name": "C4", "loc": [0.0671179, -0.0109003, 0.06358]},
|
|
47
|
-
{"ch_name": "T8", "loc": [0.0850799, -0.0150203, -0.00949]},
|
|
48
|
-
{"ch_name": "TP9", "loc": [-0.0856192, -0.0465147, -0.045707]},
|
|
49
|
-
{"ch_name": "CP5", "loc": [-0.0795922, -0.0465507, 0.030949]},
|
|
50
|
-
{"ch_name": "CP1", "loc": [-0.0355131, -0.0472919, 0.091315]},
|
|
51
|
-
{"ch_name": "CP2", "loc": [0.0383838, -0.0470731, 0.090695]},
|
|
52
|
-
{"ch_name": "CP6", "loc": [0.0833218, -0.0461013, 0.031206]},
|
|
53
|
-
{"ch_name": "TP10", "loc": [0.0861618, -0.0470353, -0.045869]},
|
|
54
|
-
{"ch_name": "P7", "loc": [-0.0724343, -0.0734527, -0.002487]},
|
|
55
|
-
{"ch_name": "P3", "loc": [-0.0530073, -0.0787878, 0.05594]},
|
|
56
|
-
{"ch_name": "Pz", "loc": [0.0003247, -0.081115, 0.082615]},
|
|
57
|
-
{"ch_name": "P4", "loc": [0.0556667, -0.0785602, 0.056561]},
|
|
58
|
-
{"ch_name": "P8", "loc": [0.0730557, -0.0730683, -0.00254]},
|
|
59
|
-
{"ch_name": "PO9", "loc": [-0.0549104, -0.0980448, -0.035465]},
|
|
60
|
-
{"ch_name": "O1", "loc": [-0.0294134, -0.112449, 0.008839]},
|
|
61
|
-
{"ch_name": "Oz", "loc": [0.0001076, -0.114892, 0.014657]},
|
|
62
|
-
{"ch_name": "O2", "loc": [0.0298426, -0.112156, 0.0088]},
|
|
63
|
-
{"ch_name": "PO10", "loc": [0.0549876, -0.0980911, -0.035541]},
|
|
64
|
-
{"ch_name": "FC3", "loc": [-0.0601819, 0.0227162, 0.055544]},
|
|
65
|
-
{"ch_name": "FC4", "loc": [0.0622931, 0.0237228, 0.05563]},
|
|
66
|
-
{"ch_name": "C5", "loc": [-0.0802801, -0.0137597, 0.02916]},
|
|
67
|
-
{"ch_name": "C1", "loc": [-0.036158, -0.0099839, 0.089752]},
|
|
68
|
-
{"ch_name": "C2", "loc": [0.037672, -0.0096241, 0.088412]},
|
|
69
|
-
{"ch_name": "C6", "loc": [0.0834559, -0.0127763, 0.029208]},
|
|
70
|
-
{"ch_name": "CP3", "loc": [-0.0635562, -0.0470088, 0.065624]},
|
|
71
|
-
{"ch_name": "CPz", "loc": [0.0003858, -0.047318, 0.099432]},
|
|
72
|
-
{"ch_name": "CP4", "loc": [0.0666118, -0.0466372, 0.06558]},
|
|
73
|
-
{"ch_name": "P1", "loc": [-0.0286203, -0.0805249, 0.075436]},
|
|
74
|
-
{"ch_name": "P2", "loc": [0.0319197, -0.0804871, 0.076716]},
|
|
75
|
-
{"ch_name": "POz", "loc": [0.0002156, -0.102178, 0.050608]},
|
|
76
|
-
{"ch_name": "FT9", "loc": [-0.0840759, 0.0145673, -0.050429]},
|
|
77
|
-
{"ch_name": "FTT9h", "loc": [-0.084125, -0.0018467, -0.029794]},
|
|
78
|
-
{"ch_name": "TTP7h", "loc": [-0.0855651, -0.0306287, 0.011153]},
|
|
79
|
-
{"ch_name": "TP7", "loc": [-0.0848302, -0.0460217, -0.007056]},
|
|
80
|
-
{"ch_name": "TPP9h", "loc": [-0.0781602, -0.0607567, -0.023824]},
|
|
81
|
-
{"ch_name": "FT10", "loc": [0.0841131, 0.0143647, -0.050538]},
|
|
82
|
-
{"ch_name": "FTT10h", "loc": [0.084123, -0.0018083, -0.029638]},
|
|
83
|
-
{"ch_name": "TPP8h", "loc": [0.0785198, -0.0604323, 0.012902]},
|
|
84
|
-
{"ch_name": "TP8", "loc": [0.0855488, -0.0455453, -0.00713]},
|
|
85
|
-
{"ch_name": "TPP10h", "loc": [0.0789027, -0.0609553, -0.023805]},
|
|
86
|
-
{"ch_name": "F9", "loc": [-0.0701019, 0.0416523, -0.049952]},
|
|
87
|
-
{"ch_name": "F10", "loc": [0.0721141, 0.0420667, -0.050452]},
|
|
88
|
-
{"ch_name": "AF7", "loc": [-0.0548397, 0.0685722, -0.01059]},
|
|
89
|
-
{"ch_name": "AF3", "loc": [-0.0337007, 0.0768371, 0.021227]},
|
|
90
|
-
{"ch_name": "AF4", "loc": [0.0357123, 0.0777259, 0.021956]},
|
|
91
|
-
{"ch_name": "AF8", "loc": [0.0557433, 0.0696568, -0.010755]},
|
|
92
|
-
{"ch_name": "PO3", "loc": [-0.0365114, -0.1008529, 0.037167]},
|
|
93
|
-
{"ch_name": "PO4", "loc": [0.0367816, -0.1008491, 0.036397]},
|
|
94
|
-
]
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def _resolve_channel_embedding_config(
|
|
98
|
-
channel_embedding: str,
|
|
99
|
-
chs_info: list[dict] | None,
|
|
100
|
-
) -> tuple[list[dict], list[list[float] | None], torch.LongTensor]:
|
|
101
|
-
"""Resolve the construction parameters for ``_ChannelEmbedding``.
|
|
102
|
-
|
|
103
|
-
Parameters
|
|
104
|
-
----------
|
|
105
|
-
channel_embedding : {"scratch", "pretrain_aligned"}
|
|
106
|
-
How to initialize ``_ChannelEmbedding``. See
|
|
107
|
-
``SignalJEPA`` docstring.
|
|
108
|
-
chs_info : list of dict | None
|
|
109
|
-
User-supplied channel information. Each element must be a
|
|
110
|
-
mapping with at least the keys ``"ch_name"`` and ``"loc"``.
|
|
111
|
-
|
|
112
|
-
Returns
|
|
113
|
-
-------
|
|
114
|
-
effective_chs_info : list of dict
|
|
115
|
-
The ``chs_info`` that ``EEGModuleMixin`` should see. Equal to
|
|
116
|
-
``chs_info`` when provided, else ``_PRETRAIN_CHS_INFO``.
|
|
117
|
-
channel_locations : list of (list of float or None)
|
|
118
|
-
Locations used to initialize ``_ChannelEmbedding``. Length
|
|
119
|
-
equals ``num_embeddings`` (N in ``'scratch'`` mode, 62 in
|
|
120
|
-
``'pretrain_aligned'`` mode).
|
|
121
|
-
ch_idxs : torch.LongTensor, shape ``(len(effective_chs_info),)``
|
|
122
|
-
Mapping from the user channel order to embedding indices.
|
|
123
|
-
|
|
124
|
-
Raises
|
|
125
|
-
------
|
|
126
|
-
ValueError
|
|
127
|
-
If ``channel_embedding`` is not recognized, if
|
|
128
|
-
``channel_embedding='scratch'`` is combined with
|
|
129
|
-
``chs_info=None``, or if ``channel_embedding='pretrain_aligned'``
|
|
130
|
-
is combined with a ``chs_info`` that contains a channel name
|
|
131
|
-
absent from ``_PRETRAIN_CHS_INFO`` (case-insensitive match).
|
|
132
|
-
"""
|
|
133
|
-
if channel_embedding not in ("scratch", "pretrain_aligned"):
|
|
134
|
-
raise ValueError(
|
|
135
|
-
"channel_embedding must be 'scratch' or 'pretrain_aligned', "
|
|
136
|
-
f"got {channel_embedding!r}"
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
if channel_embedding == "scratch":
|
|
140
|
-
if chs_info is None:
|
|
141
|
-
raise ValueError("chs_info is required when channel_embedding='scratch'")
|
|
142
|
-
effective_chs_info = list(chs_info)
|
|
143
|
-
channel_locations = [ch["loc"] for ch in effective_chs_info]
|
|
144
|
-
ch_idxs = torch.arange(len(effective_chs_info), dtype=torch.long)
|
|
145
|
-
return effective_chs_info, channel_locations, ch_idxs
|
|
146
|
-
|
|
147
|
-
# channel_embedding == "pretrain_aligned"
|
|
148
|
-
pretrain_name_to_idx = {
|
|
149
|
-
ch["ch_name"].lower(): i for i, ch in enumerate(_PRETRAIN_CHS_INFO)
|
|
150
|
-
}
|
|
151
|
-
channel_locations = [ch["loc"] for ch in _PRETRAIN_CHS_INFO]
|
|
152
|
-
|
|
153
|
-
if chs_info is None:
|
|
154
|
-
effective_chs_info = [dict(ch) for ch in _PRETRAIN_CHS_INFO]
|
|
155
|
-
ch_idxs = torch.arange(len(_PRETRAIN_CHS_INFO), dtype=torch.long)
|
|
156
|
-
return effective_chs_info, channel_locations, ch_idxs
|
|
157
|
-
|
|
158
|
-
missing = [
|
|
159
|
-
ch["ch_name"]
|
|
160
|
-
for ch in chs_info
|
|
161
|
-
if ch["ch_name"].lower() not in pretrain_name_to_idx
|
|
162
|
-
]
|
|
163
|
-
if missing:
|
|
164
|
-
raise ValueError(
|
|
165
|
-
f"Channel(s) {missing} not in the Signal-JEPA pre-training set. "
|
|
166
|
-
"To load pretrained weights while keeping these channels, use:\n"
|
|
167
|
-
" channel_embedding='scratch'\n"
|
|
168
|
-
" checkpoint 'braindecode/signal-jepa_without-chans'\n"
|
|
169
|
-
" strict=False # on load_state_dict / from_pretrained\n"
|
|
170
|
-
"The pretrained channel embedding weights will not be loaded."
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
effective_chs_info = list(chs_info)
|
|
174
|
-
ch_idxs = torch.tensor(
|
|
175
|
-
[pretrain_name_to_idx[ch["ch_name"].lower()] for ch in chs_info],
|
|
176
|
-
dtype=torch.long,
|
|
177
|
-
)
|
|
178
|
-
return effective_chs_info, channel_locations, ch_idxs
|
|
179
|
-
|
|
180
25
|
|
|
181
26
|
class _BaseSignalJEPA(EEGModuleMixin, nn.Module):
|
|
182
27
|
r"""Base class for the SignalJEPA models
|
|
183
28
|
|
|
184
29
|
Parameters
|
|
185
30
|
----------
|
|
186
|
-
channel_embedding : {"scratch", "pretrain_aligned"}, default ``"scratch"``
|
|
187
|
-
How to initialize the :class:`_ChannelEmbedding` table.
|
|
188
|
-
|
|
189
|
-
* ``"scratch"``: table has ``len(chs_info)`` rows, initialized from
|
|
190
|
-
user locations. ``chs_info`` is required.
|
|
191
|
-
* ``"pretrain_aligned"``: table has 62 rows, initialized from the
|
|
192
|
-
pre-training locations. If ``chs_info`` is provided, every channel
|
|
193
|
-
name must match one in the pre-training set (case-insensitive);
|
|
194
|
-
``forward`` will then expect input with as many channels as
|
|
195
|
-
``chs_info`` has. If ``chs_info=None``, the model runs on the
|
|
196
|
-
full 62 channels in the pre-training order.
|
|
197
|
-
|
|
198
31
|
feature_encoder__conv_layers_spec: list of tuple
|
|
199
32
|
tuples have shape ``(dim, k, stride)`` where:
|
|
200
33
|
|
|
@@ -266,32 +99,19 @@ class _BaseSignalJEPA(EEGModuleMixin, nn.Module):
|
|
|
266
99
|
transformer__num_decoder_layers: int = 4,
|
|
267
100
|
transformer__nhead: int = 8,
|
|
268
101
|
# other
|
|
269
|
-
channel_embedding: str = "scratch",
|
|
270
102
|
_init_feature_encoder: bool,
|
|
271
103
|
_init_transformer: bool,
|
|
272
104
|
):
|
|
273
|
-
# Resolve channel embedding config before calling super().__init__
|
|
274
|
-
if _init_transformer:
|
|
275
|
-
effective_chs_info, channel_locations, ch_idxs = (
|
|
276
|
-
_resolve_channel_embedding_config(channel_embedding, chs_info)
|
|
277
|
-
)
|
|
278
|
-
else:
|
|
279
|
-
effective_chs_info = chs_info
|
|
280
|
-
channel_locations = None
|
|
281
|
-
ch_idxs = None
|
|
282
|
-
|
|
283
105
|
super().__init__(
|
|
284
106
|
n_outputs=n_outputs,
|
|
285
107
|
n_chans=n_chans,
|
|
286
|
-
chs_info=
|
|
108
|
+
chs_info=chs_info,
|
|
287
109
|
n_times=n_times,
|
|
288
110
|
input_window_seconds=input_window_seconds,
|
|
289
111
|
sfreq=sfreq,
|
|
290
112
|
)
|
|
291
113
|
del n_outputs, n_chans, chs_info, n_times, input_window_seconds, sfreq
|
|
292
114
|
|
|
293
|
-
self._channel_embedding = channel_embedding
|
|
294
|
-
|
|
295
115
|
self.feature_encoder = None
|
|
296
116
|
self.pos_encoder = None
|
|
297
117
|
self.transformer = None
|
|
@@ -306,13 +126,11 @@ class _BaseSignalJEPA(EEGModuleMixin, nn.Module):
|
|
|
306
126
|
)
|
|
307
127
|
|
|
308
128
|
if _init_transformer:
|
|
309
|
-
|
|
310
|
-
assert ch_idxs is not None
|
|
129
|
+
ch_locs = [ch["loc"] for ch in self.chs_info] # type: ignore
|
|
311
130
|
self.pos_encoder = _PosEncoder(
|
|
312
131
|
spat_dim=pos_encoder__spat_dim,
|
|
313
132
|
time_dim=pos_encoder__time_dim,
|
|
314
|
-
|
|
315
|
-
ch_idxs=ch_idxs,
|
|
133
|
+
ch_locs=ch_locs,
|
|
316
134
|
sfreq_features=pos_encoder__sfreq_features,
|
|
317
135
|
spat_kwargs=pos_encoder__spat_kwargs,
|
|
318
136
|
)
|
|
@@ -343,74 +161,6 @@ class SignalJEPA(_BaseSignalJEPA):
|
|
|
343
161
|
|
|
344
162
|
.. versionadded:: 0.9
|
|
345
163
|
|
|
346
|
-
.. rubric:: Pretrained Weights
|
|
347
|
-
|
|
348
|
-
Two checkpoint variants are published on HuggingFace:
|
|
349
|
-
|
|
350
|
-
- ``braindecode/signal-jepa``: full encoder + pre-trained channel embedding
|
|
351
|
-
table (62 rows, one per pre-training channel). Use when your channel
|
|
352
|
-
names are a subset of the pre-training set (``channel_embedding='pretrain_aligned'``).
|
|
353
|
-
- ``braindecode/signal-jepa_without-chans``: same encoder, channel
|
|
354
|
-
embedding weights stripped. Use when your channel set differs from
|
|
355
|
-
pre-training; the table is freshly initialized from your channel
|
|
356
|
-
locations (``channel_embedding='scratch'``, the default).
|
|
357
|
-
|
|
358
|
-
.. important::
|
|
359
|
-
**Pre-trained Weights Available**
|
|
360
|
-
|
|
361
|
-
.. code-block:: python
|
|
362
|
-
|
|
363
|
-
from braindecode.models import SignalJEPA
|
|
364
|
-
|
|
365
|
-
# Load encoder + pre-trained channel embeddings (62 channels):
|
|
366
|
-
model = SignalJEPA.from_pretrained("braindecode/signal-jepa")
|
|
367
|
-
|
|
368
|
-
# Select a subset of the 62 pre-training channels:
|
|
369
|
-
model = SignalJEPA.from_pretrained(
|
|
370
|
-
"braindecode/signal-jepa",
|
|
371
|
-
chs_info=[{"ch_name": "Fp1", "loc": [...]}, {"ch_name": "Cz", "loc": [...]}],
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
# Arbitrary channel set (channel embedding re-initialized from your locs):
|
|
375
|
-
model = SignalJEPA.from_pretrained(
|
|
376
|
-
"braindecode/signal-jepa_without-chans",
|
|
377
|
-
chs_info=[{"ch_name": "A", "loc": [...]}, ...],
|
|
378
|
-
strict=False,
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
To push your own trained model to the Hub:
|
|
382
|
-
|
|
383
|
-
.. code-block:: python
|
|
384
|
-
|
|
385
|
-
model.push_to_hub(
|
|
386
|
-
repo_id="username/my-sjepa-model",
|
|
387
|
-
commit_message="Upload trained SignalJEPA model",
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
Requires installing ``braindecode[hub]`` for Hub integration.
|
|
391
|
-
|
|
392
|
-
.. rubric:: Usage
|
|
393
|
-
|
|
394
|
-
.. code-block:: python
|
|
395
|
-
|
|
396
|
-
from braindecode.models import SignalJEPA
|
|
397
|
-
|
|
398
|
-
model = SignalJEPA(
|
|
399
|
-
chs_info=[{"ch_name": "Fp1", "loc": [...]}, ...],
|
|
400
|
-
input_window_seconds=16.0,
|
|
401
|
-
sfreq=128,
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
# Forward: (batch, n_chans, n_times) -> (batch, n_chans * n_patches, emb_dim)
|
|
405
|
-
features = model(eeg_data)
|
|
406
|
-
|
|
407
|
-
.. warning::
|
|
408
|
-
|
|
409
|
-
Pre-trained at **128 Hz** on EEG bandpass-filtered between
|
|
410
|
-
**0.5 and 40 Hz** and rescaled by a factor of :math:`10^{6}`
|
|
411
|
-
(volts to microvolts). Apply the same preprocessing to your
|
|
412
|
-
data to match the pre-training distribution.
|
|
413
|
-
|
|
414
164
|
References
|
|
415
165
|
----------
|
|
416
166
|
.. [1] Guetschel, P., Moreau, T., & Tangermann, M. (2024).
|
|
@@ -445,8 +195,6 @@ class SignalJEPA(_BaseSignalJEPA):
|
|
|
445
195
|
transformer__num_encoder_layers: int = 8,
|
|
446
196
|
transformer__num_decoder_layers: int = 4,
|
|
447
197
|
transformer__nhead: int = 8,
|
|
448
|
-
# other
|
|
449
|
-
channel_embedding: str = "scratch",
|
|
450
198
|
):
|
|
451
199
|
super().__init__(
|
|
452
200
|
n_outputs=n_outputs,
|
|
@@ -468,16 +216,15 @@ class SignalJEPA(_BaseSignalJEPA):
|
|
|
468
216
|
transformer__num_encoder_layers=transformer__num_encoder_layers,
|
|
469
217
|
transformer__num_decoder_layers=transformer__num_decoder_layers,
|
|
470
218
|
transformer__nhead=transformer__nhead,
|
|
471
|
-
channel_embedding=channel_embedding,
|
|
472
219
|
_init_feature_encoder=True,
|
|
473
220
|
_init_transformer=True,
|
|
474
221
|
)
|
|
475
222
|
del n_outputs, n_chans, chs_info, n_times, input_window_seconds, sfreq
|
|
476
223
|
self.final_layer = nn.Identity()
|
|
477
224
|
|
|
478
|
-
def forward(self, X, return_features=False): # type: ignore
|
|
225
|
+
def forward(self, X, ch_idxs: torch.Tensor | None = None, return_features=False): # type: ignore
|
|
479
226
|
local_features = self.feature_encoder(X) # type: ignore
|
|
480
|
-
pos_encoding = self.pos_encoder(local_features) # type: ignore
|
|
227
|
+
pos_encoding = self.pos_encoder(local_features, ch_idxs=ch_idxs) # type: ignore
|
|
481
228
|
local_features += pos_encoding # type: ignore
|
|
482
229
|
contextual_features = self.transformer.encoder(local_features) # type: ignore
|
|
483
230
|
if return_features:
|
|
@@ -500,67 +247,6 @@ class SignalJEPA_Contextual(_BaseSignalJEPA):
|
|
|
500
247
|
|
|
501
248
|
.. versionadded:: 0.9
|
|
502
249
|
|
|
503
|
-
.. rubric:: Pretrained Weights
|
|
504
|
-
|
|
505
|
-
Loads the pre-trained SSL encoder (feature encoder + transformer +
|
|
506
|
-
channel embedding) from the shared checkpoints. Two hub IDs are
|
|
507
|
-
available:
|
|
508
|
-
|
|
509
|
-
- ``braindecode/signal-jepa``: with pre-trained channel embeddings
|
|
510
|
-
(62 channels, ``channel_embedding='pretrain_aligned'``).
|
|
511
|
-
- ``braindecode/signal-jepa_without-chans``: without channel embeddings.
|
|
512
|
-
The embedding table is re-initialized from your channel locations
|
|
513
|
-
(``channel_embedding='scratch'``, the default).
|
|
514
|
-
|
|
515
|
-
.. important::
|
|
516
|
-
**Pre-trained Weights Available**
|
|
517
|
-
|
|
518
|
-
.. code-block:: python
|
|
519
|
-
|
|
520
|
-
from braindecode.models import SignalJEPA_Contextual
|
|
521
|
-
|
|
522
|
-
# Use pre-trained channel embeddings (subset of the 62 channels):
|
|
523
|
-
model = SignalJEPA_Contextual.from_pretrained(
|
|
524
|
-
"braindecode/signal-jepa",
|
|
525
|
-
n_times=2048, # required: the SSL checkpoint is n_times-agnostic
|
|
526
|
-
n_outputs=4,
|
|
527
|
-
strict=False, # classifier head is not in the SSL checkpoint
|
|
528
|
-
)
|
|
529
|
-
|
|
530
|
-
# Arbitrary channel set:
|
|
531
|
-
model = SignalJEPA_Contextual.from_pretrained(
|
|
532
|
-
"braindecode/signal-jepa_without-chans",
|
|
533
|
-
chs_info=[{"ch_name": "A", "loc": [...]}, ...],
|
|
534
|
-
n_times=2048,
|
|
535
|
-
n_outputs=4,
|
|
536
|
-
strict=False,
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
Requires installing ``braindecode[hub]`` for Hub integration.
|
|
540
|
-
|
|
541
|
-
.. rubric:: Usage
|
|
542
|
-
|
|
543
|
-
.. code-block:: python
|
|
544
|
-
|
|
545
|
-
from braindecode.models import SignalJEPA_Contextual
|
|
546
|
-
|
|
547
|
-
model = SignalJEPA_Contextual(
|
|
548
|
-
chs_info=[{"ch_name": "Fp1", "loc": [...]}, ...],
|
|
549
|
-
input_window_seconds=16.0,
|
|
550
|
-
sfreq=128,
|
|
551
|
-
n_outputs=4, # e.g., 4-class classification
|
|
552
|
-
)
|
|
553
|
-
|
|
554
|
-
# Forward: (batch, n_chans, n_times) -> (batch, n_outputs)
|
|
555
|
-
output = model(eeg_data)
|
|
556
|
-
|
|
557
|
-
.. warning::
|
|
558
|
-
|
|
559
|
-
Pre-trained at **128 Hz** on EEG bandpass-filtered between
|
|
560
|
-
**0.5 and 40 Hz** and rescaled by a factor of :math:`10^{6}`
|
|
561
|
-
(volts to microvolts). Apply the same preprocessing to your
|
|
562
|
-
data to match the pre-training distribution.
|
|
563
|
-
|
|
564
250
|
Parameters
|
|
565
251
|
----------
|
|
566
252
|
n_spat_filters : int
|
|
@@ -602,7 +288,6 @@ class SignalJEPA_Contextual(_BaseSignalJEPA):
|
|
|
602
288
|
transformer__num_decoder_layers: int = 4,
|
|
603
289
|
transformer__nhead: int = 8,
|
|
604
290
|
# other
|
|
605
|
-
channel_embedding: str = "scratch",
|
|
606
291
|
_init_feature_encoder: bool = True,
|
|
607
292
|
_init_transformer: bool = True,
|
|
608
293
|
):
|
|
@@ -626,7 +311,6 @@ class SignalJEPA_Contextual(_BaseSignalJEPA):
|
|
|
626
311
|
transformer__num_encoder_layers=transformer__num_encoder_layers,
|
|
627
312
|
transformer__num_decoder_layers=transformer__num_decoder_layers,
|
|
628
313
|
transformer__nhead=transformer__nhead,
|
|
629
|
-
channel_embedding=channel_embedding,
|
|
630
314
|
_init_feature_encoder=_init_feature_encoder,
|
|
631
315
|
_init_transformer=_init_transformer,
|
|
632
316
|
)
|
|
@@ -651,9 +335,9 @@ class SignalJEPA_Contextual(_BaseSignalJEPA):
|
|
|
651
335
|
n_spat_filters=self._clf_n_spat_filters,
|
|
652
336
|
)
|
|
653
337
|
|
|
654
|
-
def forward(self, X, return_features=False): # type: ignore
|
|
338
|
+
def forward(self, X, ch_idxs: torch.Tensor | None = None, return_features=False): # type: ignore
|
|
655
339
|
local_features = self.feature_encoder(X) # type: ignore
|
|
656
|
-
pos_encoding = self.pos_encoder(local_features) # type: ignore
|
|
340
|
+
pos_encoding = self.pos_encoder(local_features, ch_idxs=ch_idxs) # type: ignore
|
|
657
341
|
local_features += pos_encoding # type: ignore
|
|
658
342
|
contextual_features = self.transformer.encoder(local_features) # type: ignore
|
|
659
343
|
if return_features:
|
|
@@ -734,14 +418,9 @@ class SignalJEPA_Contextual(_BaseSignalJEPA):
|
|
|
734
418
|
new_model.pos_encoder = deepcopy(pos_encoder)
|
|
735
419
|
new_model.transformer = deepcopy(transformer)
|
|
736
420
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
"pretrain_aligned", chs_info
|
|
741
|
-
)
|
|
742
|
-
new_model.pos_encoder.register_buffer(
|
|
743
|
-
"default_ch_idxs", new_ch_idxs.to(torch.long), persistent=False
|
|
744
|
-
)
|
|
421
|
+
if chs_info is not None:
|
|
422
|
+
ch_names = [ch["ch_name"] for ch in chs_info]
|
|
423
|
+
new_model.pos_encoder.set_fixed_ch_names(ch_names)
|
|
745
424
|
|
|
746
425
|
return new_model
|
|
747
426
|
|
|
@@ -760,54 +439,6 @@ class SignalJEPA_PostLocal(_BaseSignalJEPA):
|
|
|
760
439
|
|
|
761
440
|
.. versionadded:: 0.9
|
|
762
441
|
|
|
763
|
-
.. rubric:: Pretrained Weights
|
|
764
|
-
|
|
765
|
-
Only the feature encoder weights are reused from the shared
|
|
766
|
-
SSL checkpoints. This model has no channel embedding nor transformer,
|
|
767
|
-
so ``strict=False`` is required at load time to skip the unused keys.
|
|
768
|
-
Either hub variant works; the ``_without-chans`` one is slightly
|
|
769
|
-
smaller.
|
|
770
|
-
|
|
771
|
-
.. important::
|
|
772
|
-
**Pre-trained Weights Available**
|
|
773
|
-
|
|
774
|
-
.. code-block:: python
|
|
775
|
-
|
|
776
|
-
from braindecode.models import SignalJEPA_PostLocal
|
|
777
|
-
|
|
778
|
-
model = SignalJEPA_PostLocal.from_pretrained(
|
|
779
|
-
"braindecode/signal-jepa_without-chans",
|
|
780
|
-
n_chans=22,
|
|
781
|
-
input_window_seconds=16.0,
|
|
782
|
-
n_outputs=4,
|
|
783
|
-
strict=False,
|
|
784
|
-
)
|
|
785
|
-
|
|
786
|
-
Requires installing ``braindecode[hub]`` for Hub integration.
|
|
787
|
-
|
|
788
|
-
.. rubric:: Usage
|
|
789
|
-
|
|
790
|
-
.. code-block:: python
|
|
791
|
-
|
|
792
|
-
from braindecode.models import SignalJEPA_PostLocal
|
|
793
|
-
|
|
794
|
-
model = SignalJEPA_PostLocal(
|
|
795
|
-
n_chans=22,
|
|
796
|
-
input_window_seconds=16.0,
|
|
797
|
-
sfreq=128,
|
|
798
|
-
n_outputs=4, # e.g., 4-class classification
|
|
799
|
-
)
|
|
800
|
-
|
|
801
|
-
# Forward: (batch, n_chans, n_times) -> (batch, n_outputs)
|
|
802
|
-
output = model(eeg_data)
|
|
803
|
-
|
|
804
|
-
.. warning::
|
|
805
|
-
|
|
806
|
-
Pre-trained at **128 Hz** on EEG bandpass-filtered between
|
|
807
|
-
**0.5 and 40 Hz** and rescaled by a factor of :math:`10^{6}`
|
|
808
|
-
(volts to microvolts). Apply the same preprocessing to your
|
|
809
|
-
data to match the pre-training distribution.
|
|
810
|
-
|
|
811
442
|
Parameters
|
|
812
443
|
----------
|
|
813
444
|
n_spat_filters : int
|
|
@@ -976,62 +607,32 @@ class SignalJEPA_PreLocal(_BaseSignalJEPA):
|
|
|
976
607
|
|
|
977
608
|
.. versionadded:: 0.9
|
|
978
609
|
|
|
979
|
-
.. rubric:: Pretrained Weights
|
|
980
|
-
|
|
981
|
-
Only the feature encoder weights are reused from the shared
|
|
982
|
-
SSL checkpoints. This model has no channel embedding nor transformer,
|
|
983
|
-
so ``strict=False`` is required at load time to skip the unused keys.
|
|
984
|
-
Either hub variant works; the ``_without-chans`` one is slightly
|
|
985
|
-
smaller.
|
|
986
|
-
|
|
987
610
|
.. important::
|
|
988
611
|
**Pre-trained Weights Available**
|
|
989
612
|
|
|
613
|
+
This model has pre-trained weights available on the Hugging Face Hub.
|
|
614
|
+
You can load them using:
|
|
615
|
+
|
|
990
616
|
.. code-block:: python
|
|
991
617
|
|
|
992
618
|
from braindecode.models import SignalJEPA_PreLocal
|
|
993
619
|
|
|
620
|
+
# Load pre-trained model from Hugging Face Hub
|
|
994
621
|
model = SignalJEPA_PreLocal.from_pretrained(
|
|
995
|
-
"braindecode/
|
|
996
|
-
n_chans=22,
|
|
997
|
-
input_window_seconds=16.0,
|
|
998
|
-
n_outputs=4,
|
|
999
|
-
strict=False,
|
|
622
|
+
"braindecode/SignalJEPA-PreLocal-pretrained"
|
|
1000
623
|
)
|
|
1001
624
|
|
|
1002
625
|
To push your own trained model to the Hub:
|
|
1003
626
|
|
|
1004
627
|
.. code-block:: python
|
|
1005
628
|
|
|
629
|
+
# After training your model
|
|
1006
630
|
model.push_to_hub(
|
|
1007
631
|
repo_id="username/my-sjepa-model",
|
|
1008
632
|
commit_message="Upload trained SignalJEPA model",
|
|
1009
633
|
)
|
|
1010
634
|
|
|
1011
|
-
Requires installing ``braindecode[
|
|
1012
|
-
|
|
1013
|
-
.. rubric:: Usage
|
|
1014
|
-
|
|
1015
|
-
.. code-block:: python
|
|
1016
|
-
|
|
1017
|
-
from braindecode.models import SignalJEPA_PreLocal
|
|
1018
|
-
|
|
1019
|
-
model = SignalJEPA_PreLocal(
|
|
1020
|
-
n_chans=22,
|
|
1021
|
-
input_window_seconds=16.0,
|
|
1022
|
-
sfreq=128,
|
|
1023
|
-
n_outputs=4, # e.g., 4-class classification
|
|
1024
|
-
)
|
|
1025
|
-
|
|
1026
|
-
# Forward: (batch, n_chans, n_times) -> (batch, n_outputs)
|
|
1027
|
-
output = model(eeg_data)
|
|
1028
|
-
|
|
1029
|
-
.. warning::
|
|
1030
|
-
|
|
1031
|
-
Pre-trained at **128 Hz** on EEG bandpass-filtered between
|
|
1032
|
-
**0.5 and 40 Hz** and rescaled by a factor of :math:`10^{6}`
|
|
1033
|
-
(volts to microvolts). Apply the same preprocessing to your
|
|
1034
|
-
data to match the pre-training distribution.
|
|
635
|
+
Requires installing ``braindecode[hug]`` for Hub integration.
|
|
1035
636
|
|
|
1036
637
|
Parameters
|
|
1037
638
|
----------
|
|
@@ -1334,7 +935,11 @@ class _ChannelEmbedding(nn.Embedding):
|
|
|
1334
935
|
self.coordinate_ranges = [
|
|
1335
936
|
(min(coords), max(coords))
|
|
1336
937
|
for coords in zip(
|
|
1337
|
-
*[
|
|
938
|
+
*[
|
|
939
|
+
loc[3:6] if len(loc) == 12 else loc
|
|
940
|
+
for loc in channel_locations
|
|
941
|
+
if loc is not None
|
|
942
|
+
]
|
|
1338
943
|
)
|
|
1339
944
|
]
|
|
1340
945
|
channel_mins, channel_maxs = zip(*self.coordinate_ranges)
|
|
@@ -1380,14 +985,8 @@ class _PosEncoder(nn.Module):
|
|
|
1380
985
|
i.e. the EEG channel.
|
|
1381
986
|
time_dim: int
|
|
1382
987
|
Number of dimensions to use to encode the temporal position of the patch.
|
|
1383
|
-
|
|
1384
|
-
List of the n-
|
|
1385
|
-
embedding table. Length equals the number of embedding rows
|
|
1386
|
-
(N for ``'scratch'`` mode, 62 for ``'pretrain_aligned'`` mode).
|
|
1387
|
-
ch_idxs: torch.LongTensor, shape ``(n_chans,)``
|
|
1388
|
-
Default mapping from the model's channel order to rows of the
|
|
1389
|
-
channel embedding table. Stored as a non-persistent buffer
|
|
1390
|
-
(device-aware but excluded from ``state_dict``).
|
|
988
|
+
ch_locs: list of list of float or 2d array
|
|
989
|
+
List of the n-dimensions locations of the EEG channels.
|
|
1391
990
|
sfreq_features: float
|
|
1392
991
|
The "downsampled" sampling frequency returned by the feature encoder.
|
|
1393
992
|
spat_kwargs: dict
|
|
@@ -1401,8 +1000,7 @@ class _PosEncoder(nn.Module):
|
|
|
1401
1000
|
self,
|
|
1402
1001
|
spat_dim: int,
|
|
1403
1002
|
time_dim: int,
|
|
1404
|
-
|
|
1405
|
-
ch_idxs: torch.LongTensor,
|
|
1003
|
+
ch_locs,
|
|
1406
1004
|
sfreq_features: float,
|
|
1407
1005
|
spat_kwargs: dict | None = None,
|
|
1408
1006
|
max_seconds: float = 600.0, # 10 minutes
|
|
@@ -1415,17 +1013,9 @@ class _PosEncoder(nn.Module):
|
|
|
1415
1013
|
|
|
1416
1014
|
# Positional encoder for the spatial dimension:
|
|
1417
1015
|
self.pos_encoder_spat = _ChannelEmbedding(
|
|
1418
|
-
|
|
1016
|
+
ch_locs, spat_dim, **spat_kwargs
|
|
1419
1017
|
) # (batch_size, n_channels, spat_dim)
|
|
1420
1018
|
|
|
1421
|
-
# Default channel index mapping. Registered as a non-persistent
|
|
1422
|
-
# buffer so it follows .to(device) but is NOT saved to state_dict.
|
|
1423
|
-
# The mapping is fully determined by chs_info + channel_embedding
|
|
1424
|
-
# at __init__, so persisting it would duplicate config.json.
|
|
1425
|
-
self.register_buffer(
|
|
1426
|
-
"default_ch_idxs", ch_idxs.to(torch.long), persistent=False
|
|
1427
|
-
)
|
|
1428
|
-
|
|
1429
1019
|
# Pre-computed tensor for positional encoding on the time dimension:
|
|
1430
1020
|
self.encoding_time = torch.zeros(0, dtype=torch.float32, requires_grad=False)
|
|
1431
1021
|
|
|
@@ -1443,22 +1033,24 @@ class _PosEncoder(nn.Module):
|
|
|
1443
1033
|
"""
|
|
1444
1034
|
Parameters
|
|
1445
1035
|
----------
|
|
1446
|
-
local_features
|
|
1447
|
-
ch_idxs
|
|
1448
|
-
Indices of the channels
|
|
1449
|
-
|
|
1450
|
-
across the batch.
|
|
1036
|
+
* local_features: (batch_size, n_chans * n_times_out, emb_dim)
|
|
1037
|
+
* ch_idxs: (batch_size, n_chans) | None
|
|
1038
|
+
Indices of the channels to use in the ``ch_names`` list passed
|
|
1039
|
+
as argument plus one. Index 0 is reserved for an unknown channel.
|
|
1451
1040
|
|
|
1452
1041
|
Returns
|
|
1453
1042
|
-------
|
|
1454
|
-
pos_encoding
|
|
1455
|
-
The first ``spat_dim`` dimensions encode the
|
|
1456
|
-
|
|
1457
|
-
temporal positional encoding.
|
|
1043
|
+
pos_encoding: (batch_size, n_chans * n_times_out, emb_dim)
|
|
1044
|
+
The first ``spat_dim`` dimensions encode the channels positional encoding
|
|
1045
|
+
and the following ``time_dim`` dimensions encode the temporal positional encoding.
|
|
1458
1046
|
"""
|
|
1459
1047
|
batch_size, n_chans_times, emb_dim = local_features.shape
|
|
1460
1048
|
if ch_idxs is None:
|
|
1461
|
-
ch_idxs =
|
|
1049
|
+
ch_idxs = torch.arange(
|
|
1050
|
+
0,
|
|
1051
|
+
self.pos_encoder_spat.num_embeddings,
|
|
1052
|
+
device=local_features.device,
|
|
1053
|
+
).repeat(batch_size, 1)
|
|
1462
1054
|
|
|
1463
1055
|
batch_size_chs, n_chans = ch_idxs.shape
|
|
1464
1056
|
assert emb_dim >= self.spat_dim + self.time_dim
|