braindecode 1.8.1.dev173387713__tar.gz → 1.8.1.dev174857482__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.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/NOTICE.txt +2 -0
- {braindecode-1.8.1.dev173387713/braindecode.egg-info → braindecode-1.8.1.dev174857482}/PKG-INFO +3 -1
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/__init__.py +2 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/deep4.py +16 -8
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegpt.py +14 -2
- braindecode-1.8.1.dev174857482/braindecode/models/popt.py +320 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/shallow_fbcsp.py +15 -8
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/summary.csv +1 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/util.py +1 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/convolution.py +25 -14
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/layers.py +4 -2
- braindecode-1.8.1.dev174857482/braindecode/modules/popt_modules.py +187 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/losses.py +7 -2
- braindecode-1.8.1.dev174857482/braindecode/version.py +1 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482/braindecode.egg-info}/PKG-INFO +3 -1
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/SOURCES.txt +3 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/requires.txt +2 -0
- braindecode-1.8.1.dev174857482/docs/agent_docs.rst +60 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/api.rst +1 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/conf.py +7 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/whats_new.rst +46 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/pyproject.toml +2 -0
- braindecode-1.8.1.dev173387713/braindecode/version.py +0 -1
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/LICENSE.txt +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/MANIFEST.in +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/README.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/base.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/classifier.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/_notebook_viewer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/base.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/collate.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/datautil/util.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/_real_dft.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/functions.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/barista.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/base.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/bendr.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/biot.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/brainbert.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/brainmodule.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/brant.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/codebrain.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/config.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/dance.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/diver1.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eeginception_mi.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegnex.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegsym.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/interpolated.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/labram.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/luna.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/medformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/meta_neuromotor.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/mirepnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/mscformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/msvtnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/mvpformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/neuropose.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/patchedtransformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/reve.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sensingdynamics.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/signal_jepa.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/steegformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tcn.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/usleep.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/vemg2pose.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/zuna.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/activation.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/attention.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/blocks.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/dance_modules.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/filter.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/interpolation.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/linear.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/stats.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/util.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/regressor.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/samplers/base.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/training/scoring.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/util.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/activations.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/Makefile +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/cite.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/help.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/index.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/install/install.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/install/install_pip.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/install/install_source.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models_table.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/setup.cfg +0 -0
|
@@ -43,6 +43,8 @@ The following components are licensed under the MIT License:
|
|
|
43
43
|
- `braindecode/models/tcformer.py`
|
|
44
44
|
- `braindecode/models/ifnet.py`
|
|
45
45
|
- `braindecode/models/mirepnet.py`
|
|
46
|
+
- `braindecode/models/popt.py`
|
|
47
|
+
- `braindecode/modules/popt_modules.py`
|
|
46
48
|
|
|
47
49
|
### Apache-2.0 Licensed Files
|
|
48
50
|
|
{braindecode-1.8.1.dev173387713/braindecode.egg-info → braindecode-1.8.1.dev174857482}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braindecode
|
|
3
|
-
Version: 1.8.1.
|
|
3
|
+
Version: 1.8.1.dev174857482
|
|
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>
|
|
@@ -60,6 +60,8 @@ Provides-Extra: typing
|
|
|
60
60
|
Requires-Dist: exca>=0.5.10; extra == "typing"
|
|
61
61
|
Requires-Dist: numpydantic>=1.7; extra == "typing"
|
|
62
62
|
Provides-Extra: docs
|
|
63
|
+
Requires-Dist: pytest<9.1; extra == "docs"
|
|
64
|
+
Requires-Dist: sphinx-markdown-builder==0.6.11; extra == "docs"
|
|
63
65
|
Requires-Dist: sphinx_gallery>=0.17; extra == "docs"
|
|
64
66
|
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
65
67
|
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/__init__.py
RENAMED
|
@@ -48,6 +48,7 @@ from .msvtnet import MSVTNet
|
|
|
48
48
|
from .mvpformer import MVPFormer
|
|
49
49
|
from .neuropose import NeuroPose
|
|
50
50
|
from .patchedtransformer import PBT
|
|
51
|
+
from .popt import PopulationTransformer
|
|
51
52
|
from .reve import REVE
|
|
52
53
|
from .sccnet import SCCNet
|
|
53
54
|
from .sensingdynamics import SensingDynamics
|
|
@@ -144,6 +145,7 @@ __all__ = [
|
|
|
144
145
|
"MSVTNet",
|
|
145
146
|
"MVPFormer",
|
|
146
147
|
"PBT",
|
|
148
|
+
"PopulationTransformer",
|
|
147
149
|
"REVE",
|
|
148
150
|
"SCCNet",
|
|
149
151
|
"ShallowFBCSPNet",
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/deep4.py
RENAMED
|
@@ -186,13 +186,18 @@ class Deep4Net(EEGModuleMixin, nn.Sequential):
|
|
|
186
186
|
# When padronize all layers,
|
|
187
187
|
# add the old's parameters here
|
|
188
188
|
self.mapping = {
|
|
189
|
-
"conv_time.weight": "conv_time_spat.conv_time.weight",
|
|
190
|
-
"conv_spat.weight": "conv_time_spat.conv_spat.weight",
|
|
191
|
-
"conv_time.bias": "conv_time_spat.conv_time.bias",
|
|
192
|
-
"conv_spat.bias": "conv_time_spat.conv_spat.bias",
|
|
193
189
|
"conv_classifier.weight": "final_layer.conv_classifier.weight",
|
|
194
190
|
"conv_classifier.bias": "final_layer.conv_classifier.bias",
|
|
195
191
|
}
|
|
192
|
+
if self.split_first_layer:
|
|
193
|
+
self.mapping.update(
|
|
194
|
+
{
|
|
195
|
+
"conv_time.weight": "conv_time_spat.conv_time.weight",
|
|
196
|
+
"conv_spat.weight": "conv_time_spat.conv_spat.weight",
|
|
197
|
+
"conv_time.bias": "conv_time_spat.conv_time.bias",
|
|
198
|
+
"conv_spat.bias": "conv_time_spat.conv_spat.bias",
|
|
199
|
+
}
|
|
200
|
+
)
|
|
196
201
|
|
|
197
202
|
if self.stride_before_pool:
|
|
198
203
|
conv_stride = self.pool_time_stride
|
|
@@ -215,6 +220,7 @@ class Deep4Net(EEGModuleMixin, nn.Sequential):
|
|
|
215
220
|
filter_time_length=filter_time_length,
|
|
216
221
|
bias_time=True,
|
|
217
222
|
bias_spat=not self.batch_norm,
|
|
223
|
+
stride=(conv_stride, 1),
|
|
218
224
|
),
|
|
219
225
|
)
|
|
220
226
|
n_filters_conv = self.n_filters_spat
|
|
@@ -318,14 +324,16 @@ class Deep4Net(EEGModuleMixin, nn.Sequential):
|
|
|
318
324
|
|
|
319
325
|
# Initialization, xavier is same as in our paper...
|
|
320
326
|
# was default from lasagne
|
|
321
|
-
init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
|
|
322
|
-
# maybe no bias in case of no split layer and batch norm
|
|
323
|
-
if self.split_first_layer or (not self.batch_norm):
|
|
324
|
-
init.constant_(self.conv_time_spat.conv_time.bias, 0)
|
|
325
327
|
if self.split_first_layer:
|
|
328
|
+
init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
|
|
329
|
+
init.constant_(self.conv_time_spat.conv_time.bias, 0)
|
|
326
330
|
init.xavier_uniform_(self.conv_time_spat.conv_spat.weight, gain=1)
|
|
327
331
|
if not self.batch_norm:
|
|
328
332
|
init.constant_(self.conv_time_spat.conv_spat.bias, 0)
|
|
333
|
+
else:
|
|
334
|
+
init.xavier_uniform_(self.conv_time.weight, gain=1)
|
|
335
|
+
if not self.batch_norm:
|
|
336
|
+
init.constant_(self.conv_time.bias, 0)
|
|
329
337
|
if self.batch_norm:
|
|
330
338
|
init.constant_(self.bnorm.weight, 1)
|
|
331
339
|
init.constant_(self.bnorm.bias, 0)
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/eegpt.py
RENAMED
|
@@ -88,7 +88,8 @@ class EEGPT(EEGModuleMixin, nn.Module):
|
|
|
88
88
|
- *Role.* Maps raw spatio-temporal EEG patches into a sequence of latent tokens :math:`z`.
|
|
89
89
|
- `EEGPT.chans_id` **(Channel Identification)**
|
|
90
90
|
- *Operations.* A buffer containing channel indices mapped from the standard channel names provided
|
|
91
|
-
in ``chs_info`` [eegpt]_.
|
|
91
|
+
in ``chs_info`` [eegpt]_. It is rebuilt from the montage at construction and not stored in
|
|
92
|
+
checkpoints, so pretrained weights load on any montage.
|
|
92
93
|
- *Role.* Provides the spatial identity for each input channel, allowing the model to look up
|
|
93
94
|
the correct channel embedding vector :math:`\varsigma_i`.
|
|
94
95
|
- **Local Spatio-Temporal Embedding** (Input Processing)
|
|
@@ -338,8 +339,12 @@ class EEGPT(EEGModuleMixin, nn.Module):
|
|
|
338
339
|
else:
|
|
339
340
|
self.channel_names = None # type: ignore
|
|
340
341
|
|
|
342
|
+
# Derived from the montage above, so it is not saved in the state dict:
|
|
343
|
+
# loading a checkpoint must not replace the IDs of this model's channels.
|
|
341
344
|
self.register_buffer(
|
|
342
|
-
"chans_id",
|
|
345
|
+
"chans_id",
|
|
346
|
+
self.target_encoder.prepare_chan_ids(self.channel_names),
|
|
347
|
+
persistent=False,
|
|
343
348
|
)
|
|
344
349
|
|
|
345
350
|
self.flattened_encoder_output_dim = (
|
|
@@ -391,6 +396,13 @@ class EEGPT(EEGModuleMixin, nn.Module):
|
|
|
391
396
|
"embed_dim": self.embed_dim,
|
|
392
397
|
}
|
|
393
398
|
|
|
399
|
+
def _load_from_state_dict(self, state_dict, prefix, *args, **kwargs):
|
|
400
|
+
# Checkpoints saved before ``chans_id`` became non-persistent (including
|
|
401
|
+
# the released EEGPT weights) still carry it. Drop it, so the IDs keep
|
|
402
|
+
# matching this model's montage and a different montage can load.
|
|
403
|
+
state_dict.pop(prefix + "chans_id", None)
|
|
404
|
+
super()._load_from_state_dict(state_dict, prefix, *args, **kwargs)
|
|
405
|
+
|
|
394
406
|
def forward(self, x, return_features=False):
|
|
395
407
|
"""
|
|
396
408
|
Forward pass.
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# Authors: Christopher Wang, Geeling Chau (original implementation)
|
|
2
|
+
# Adam Mounir <am91ris@gmail.com> (braindecode adaptation)
|
|
3
|
+
#
|
|
4
|
+
# License: MIT
|
|
5
|
+
# Adapted from https://github.com/czlwang/PopulationTransformer
|
|
6
|
+
"""PopulationTransformer (PopT, Chau et al. 2024).
|
|
7
|
+
|
|
8
|
+
* paper: https://arxiv.org/abs/2406.03044
|
|
9
|
+
* code: https://github.com/czlwang/PopulationTransformer
|
|
10
|
+
* weights: https://huggingface.co/PopulationTransformer/popt_brainbert_stft
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import warnings
|
|
16
|
+
from collections import OrderedDict
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
import torch
|
|
20
|
+
import torch.nn as nn
|
|
21
|
+
|
|
22
|
+
from braindecode.models.base import EEGModuleMixin
|
|
23
|
+
from braindecode.models.util import extract_channel_locations_from_chs_info
|
|
24
|
+
from braindecode.modules.popt_modules import (
|
|
25
|
+
_PopTInputEmbedding,
|
|
26
|
+
_PopTSpecPredictionHead,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PopulationTransformer(EEGModuleMixin, nn.Module, license="mit"):
|
|
31
|
+
r"""PopulationTransformer (PopT) from Chau et al. (2024) [PopT2024]_.
|
|
32
|
+
|
|
33
|
+
:bdg-danger:`Foundation Model` :bdg-info:`Attention/Transformer`
|
|
34
|
+
|
|
35
|
+
PopT is a self-supervised **population** model for intracranial recordings
|
|
36
|
+
(sEEG/iEEG). It does not encode a raw time signal; instead each electrode is
|
|
37
|
+
represented by a feature vector — typically the frozen embedding of a
|
|
38
|
+
per-channel foundation model such as :class:`~braindecode.models.BrainBERT` —
|
|
39
|
+
and PopT aggregates across electrodes. Every electrode feature is linearly
|
|
40
|
+
projected and given a fixed sinusoidal **spatial** position encoding built
|
|
41
|
+
from its integer anatomical coordinates (one embedding per X/Y/Z axis plus a
|
|
42
|
+
sequence id). A ``CLS`` token is prepended, a stack of standard Transformer
|
|
43
|
+
encoder layers mixes the population, and the ``CLS`` output is the pooled
|
|
44
|
+
representation used for downstream decoding. Pre-training is by masked /
|
|
45
|
+
replaced-token modelling over the electrode population.
|
|
46
|
+
|
|
47
|
+
Following the braindecode convention, the per-electrode feature vector plays
|
|
48
|
+
the role of the ``n_times`` axis, so the model keeps the standard
|
|
49
|
+
``(batch, n_chans, n_times)`` input signature: ``n_chans`` is the number of
|
|
50
|
+
electrodes and ``n_times`` is the upstream feature dimension (768 for
|
|
51
|
+
BrainBERT ``stft`` features). Electrode coordinates are read from
|
|
52
|
+
``chs_info`` (their ``loc``) and discretised to absolute integer indices
|
|
53
|
+
inside the model, as upstream feeds them; when no positions are available
|
|
54
|
+
the electrodes fall back to distinct sequential indices.
|
|
55
|
+
|
|
56
|
+
The ``CLS`` output goes through a single linear layer, as in the upstream
|
|
57
|
+
fine-tuning model (``PtDownstreamModel.linear_out``, one logit trained with
|
|
58
|
+
binary cross-entropy there; ``n_outputs=1`` reproduces it).
|
|
59
|
+
|
|
60
|
+
The defaults are the released ``popt_brainbert_stft`` configuration:
|
|
61
|
+
``hidden_dim=512``, ``ffn_dim=2048``, ``n_heads=8``, ``n_layers=6``, used
|
|
62
|
+
on ``n_times=768`` BrainBERT features (~20M parameters).
|
|
63
|
+
|
|
64
|
+
.. important::
|
|
65
|
+
**Pre-trained weights available.** The official checkpoint is released by
|
|
66
|
+
the authors and loads directly::
|
|
67
|
+
|
|
68
|
+
model = PopulationTransformer.from_pretrained(
|
|
69
|
+
"braindecode/popt-pretrained", n_outputs=2
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
It uses the default configuration; ``n_chans`` and ``n_outputs``
|
|
73
|
+
may be changed freely, as the population is pooled through the ``CLS``
|
|
74
|
+
token and the classification head is task-specific (the checkpoint
|
|
75
|
+
carries no trained fine-tuning head).
|
|
76
|
+
|
|
77
|
+
.. warning::
|
|
78
|
+
**Evaluate with a time-blocked split.** The paper's downstream results
|
|
79
|
+
use a random 80/10/10 split over word-aligned 5 s windows. Words are a
|
|
80
|
+
fraction of a second apart, so almost every test window overlaps a
|
|
81
|
+
training window, and labels that drift slowly in time leak into
|
|
82
|
+
training. Re-running the paper setup (7 subjects, 3 seeds) with
|
|
83
|
+
contiguous blocks of time, and dropping training windows that overlap
|
|
84
|
+
the test set, pretrained PopT goes from 0.79 to 0.51 ROC-AUC on Pitch
|
|
85
|
+
(chance) and from 0.89 to 0.64 on Volume. Onset (0.86 to 0.84) and
|
|
86
|
+
Speech (0.90 to 0.84) hold up, and pretraining still beats training
|
|
87
|
+
from scratch on Onset, Speech and Volume. The model and weights are
|
|
88
|
+
not affected; the issue is only in the evaluation. When fine-tuning,
|
|
89
|
+
split by blocks of time.
|
|
90
|
+
|
|
91
|
+
.. versionadded:: 1.8.2
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
hidden_dim : int, optional
|
|
96
|
+
Transformer model width ``D``. Must be divisible by 8. Default 512, as
|
|
97
|
+
the released model.
|
|
98
|
+
ffn_dim : int, optional
|
|
99
|
+
Inner dimension of the Transformer feed-forward blocks. Default 2048, as
|
|
100
|
+
the released model.
|
|
101
|
+
n_layers : int, optional
|
|
102
|
+
Number of Transformer encoder layers. Default 6, as the released model.
|
|
103
|
+
n_heads : int, optional
|
|
104
|
+
Number of attention heads. Default 8, as the released model.
|
|
105
|
+
max_len : int, optional
|
|
106
|
+
Size of the coordinate table (largest addressable integer coordinate).
|
|
107
|
+
Default 5000, as upstream.
|
|
108
|
+
coord_units : {"m", "raw"}, optional
|
|
109
|
+
How ``chs_info`` positions become integer coordinates. ``"m"`` (default)
|
|
110
|
+
treats them as MNE metres and rounds them to millimetres. ``"raw"``
|
|
111
|
+
rounds the positions as they are: use it when ``x/y/z`` already hold the
|
|
112
|
+
Brain Treebank integer (left, inferior, posterior) coordinates, as NEMAR
|
|
113
|
+
nm000253 stores them. Either way the indices are absolute, not
|
|
114
|
+
shifted, as upstream feeds them (``pt_supervised_task_coords.py``).
|
|
115
|
+
They match the pretrained checkpoint only if the positions are already
|
|
116
|
+
in the upstream (left, inferior, posterior) space; MNE head-frame
|
|
117
|
+
positions (e.g. a standard montage) are **not** that space. Indices
|
|
118
|
+
outside ``[0, max_len - 1]`` are clamped, with a warning. You can also
|
|
119
|
+
pass ``coords`` to :meth:`forward` directly.
|
|
120
|
+
shift_coords : bool, optional
|
|
121
|
+
If ``True``, shift each axis so that its smallest index is 0. Default
|
|
122
|
+
``False``. Upstream does not shift, and the shift changes the position
|
|
123
|
+
encoding, and so the output of the pretrained model; use it only for
|
|
124
|
+
positions with negative values when training from scratch.
|
|
125
|
+
activation : type[nn.Module], optional
|
|
126
|
+
Feed-forward activation, given as a class. Default :class:`~torch.nn.GELU`.
|
|
127
|
+
drop_prob : float, optional
|
|
128
|
+
Dropout probability. Default 0.1.
|
|
129
|
+
|
|
130
|
+
References
|
|
131
|
+
----------
|
|
132
|
+
.. [PopT2024] Chau, G., Wang, C., Talukder, S., Subramaniam, V., Soedarmadji,
|
|
133
|
+
S., Yue, Y., Katz, B., & Barbu, A. (2024). Population Transformer:
|
|
134
|
+
Learning Population-level Representations of Neural Activity. arXiv
|
|
135
|
+
preprint arXiv:2406.03044.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
# braindecode parameters
|
|
141
|
+
n_outputs=None,
|
|
142
|
+
n_chans=None,
|
|
143
|
+
chs_info=None,
|
|
144
|
+
n_times=None,
|
|
145
|
+
input_window_seconds=None,
|
|
146
|
+
sfreq=None,
|
|
147
|
+
# model-specific parameters
|
|
148
|
+
*,
|
|
149
|
+
hidden_dim: int = 512,
|
|
150
|
+
ffn_dim: int = 2048,
|
|
151
|
+
n_layers: int = 6,
|
|
152
|
+
n_heads: int = 8,
|
|
153
|
+
max_len: int = 5000,
|
|
154
|
+
coord_units: str = "m",
|
|
155
|
+
shift_coords: bool = False,
|
|
156
|
+
activation: type[nn.Module] = nn.GELU,
|
|
157
|
+
drop_prob: float = 0.1,
|
|
158
|
+
):
|
|
159
|
+
super().__init__(
|
|
160
|
+
n_outputs=n_outputs,
|
|
161
|
+
n_chans=n_chans,
|
|
162
|
+
chs_info=chs_info,
|
|
163
|
+
n_times=n_times,
|
|
164
|
+
input_window_seconds=input_window_seconds,
|
|
165
|
+
sfreq=sfreq,
|
|
166
|
+
)
|
|
167
|
+
del n_outputs, n_chans, chs_info, n_times, input_window_seconds, sfreq
|
|
168
|
+
if coord_units not in ("m", "raw"):
|
|
169
|
+
raise ValueError(f"coord_units must be 'm' or 'raw', got {coord_units!r}.")
|
|
170
|
+
|
|
171
|
+
self.input_dim = self.n_times
|
|
172
|
+
self.hidden_dim = hidden_dim
|
|
173
|
+
self.ffn_dim = ffn_dim
|
|
174
|
+
self.n_layers = n_layers
|
|
175
|
+
self.n_heads = n_heads
|
|
176
|
+
self.max_len = max_len
|
|
177
|
+
self.coord_units = coord_units
|
|
178
|
+
self.shift_coords = shift_coords
|
|
179
|
+
|
|
180
|
+
self.input_embedding = _PopTInputEmbedding(
|
|
181
|
+
input_dim=self.input_dim,
|
|
182
|
+
hidden_dim=hidden_dim,
|
|
183
|
+
max_len=max_len,
|
|
184
|
+
drop_prob=drop_prob,
|
|
185
|
+
)
|
|
186
|
+
encoder_layer = nn.TransformerEncoderLayer(
|
|
187
|
+
d_model=hidden_dim,
|
|
188
|
+
nhead=n_heads,
|
|
189
|
+
dim_feedforward=ffn_dim,
|
|
190
|
+
activation=activation(),
|
|
191
|
+
dropout=drop_prob,
|
|
192
|
+
batch_first=True,
|
|
193
|
+
)
|
|
194
|
+
self.transformer_encoder = nn.TransformerEncoder(
|
|
195
|
+
encoder_layer, num_layers=n_layers
|
|
196
|
+
)
|
|
197
|
+
# Pre-training head, unused here; kept so the checkpoint loads strictly.
|
|
198
|
+
self.spec_prediction_head = _PopTSpecPredictionHead(hidden_dim, self.input_dim)
|
|
199
|
+
self.final_layer = nn.Linear(hidden_dim, self.n_outputs)
|
|
200
|
+
|
|
201
|
+
self.register_buffer(
|
|
202
|
+
"electrode_coords", self._coords_from_chs_info(), persistent=False
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def _coords_from_chs_info(self) -> torch.Tensor:
|
|
206
|
+
"""Integer ``(n_chans, 3)`` coordinates from ``chs_info``, or sequential ones."""
|
|
207
|
+
chs_info = getattr(self, "_chs_info", None)
|
|
208
|
+
loc = extract_channel_locations_from_chs_info(
|
|
209
|
+
chs_info, num_channels=self.n_chans
|
|
210
|
+
)
|
|
211
|
+
# The helper does not screen NaNs or partial montages.
|
|
212
|
+
if loc is None or loc.shape[0] != self.n_chans or not np.isfinite(loc).all():
|
|
213
|
+
idx = torch.arange(self.n_chans, dtype=torch.long)
|
|
214
|
+
coords = idx.unsqueeze(1).repeat(1, 3)
|
|
215
|
+
else:
|
|
216
|
+
loc_t = torch.as_tensor(loc, dtype=torch.float)
|
|
217
|
+
if self.coord_units == "m":
|
|
218
|
+
loc_t = loc_t * 1000.0
|
|
219
|
+
coords = loc_t.round().long()
|
|
220
|
+
if self.shift_coords:
|
|
221
|
+
coords = coords - coords.min(dim=0, keepdim=True).values
|
|
222
|
+
if bool(((coords < 0) | (coords >= self.max_len)).any()):
|
|
223
|
+
hint = (
|
|
224
|
+
"raise `max_len`"
|
|
225
|
+
if self.shift_coords
|
|
226
|
+
else "set `shift_coords=True` to train from scratch"
|
|
227
|
+
)
|
|
228
|
+
warnings.warn(
|
|
229
|
+
"Some electrode coordinates from chs_info fall outside "
|
|
230
|
+
f"[0, {self.max_len - 1}] and are clamped. The pretrained "
|
|
231
|
+
"model expects the non-negative integer (left, inferior, "
|
|
232
|
+
"posterior) indices of the upstream data; pass `coords` to "
|
|
233
|
+
f"forward, or {hint}.",
|
|
234
|
+
UserWarning,
|
|
235
|
+
stacklevel=4,
|
|
236
|
+
)
|
|
237
|
+
return coords.clamp(0, self.max_len - 1)
|
|
238
|
+
|
|
239
|
+
def load_state_dict(self, state_dict, *args, **kwargs):
|
|
240
|
+
"""Also accept the untrained ``final_layer.{norm,fc}`` head of the HF mirror."""
|
|
241
|
+
remapped = OrderedDict()
|
|
242
|
+
for key, value in state_dict.items():
|
|
243
|
+
if key.startswith("final_layer.norm."):
|
|
244
|
+
continue
|
|
245
|
+
if key.startswith("final_layer.fc."):
|
|
246
|
+
key = "final_layer." + key[len("final_layer.fc.") :]
|
|
247
|
+
remapped[key] = value
|
|
248
|
+
return super().load_state_dict(remapped, *args, **kwargs)
|
|
249
|
+
|
|
250
|
+
def reset_head(self, n_outputs: int) -> None:
|
|
251
|
+
"""Swap the classification head for a new number of outputs."""
|
|
252
|
+
old = self.final_layer.weight
|
|
253
|
+
self._set_n_outputs(n_outputs)
|
|
254
|
+
self.final_layer = nn.Linear(self.hidden_dim, n_outputs).to(
|
|
255
|
+
device=old.device, dtype=old.dtype
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def forward(
|
|
259
|
+
self,
|
|
260
|
+
x: torch.Tensor,
|
|
261
|
+
coords: torch.Tensor | None = None,
|
|
262
|
+
seq_id: torch.Tensor | None = None,
|
|
263
|
+
return_features: bool = False,
|
|
264
|
+
key_padding_mask: torch.Tensor | None = None,
|
|
265
|
+
):
|
|
266
|
+
"""Aggregate a population of electrode features.
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
x : torch.Tensor
|
|
271
|
+
Per-electrode features of shape ``(batch, n_chans, n_times)``, where
|
|
272
|
+
``n_times`` is the upstream feature dimension.
|
|
273
|
+
coords : torch.Tensor, optional
|
|
274
|
+
Integer coordinates of shape ``(batch, n_chans, 3)``. Defaults to the
|
|
275
|
+
coordinates derived from ``chs_info`` at construction, broadcast over
|
|
276
|
+
the batch.
|
|
277
|
+
seq_id : torch.Tensor, optional
|
|
278
|
+
Integer sequence ids of shape ``(batch, n_chans)``. Defaults to zero
|
|
279
|
+
(single population).
|
|
280
|
+
return_features : bool
|
|
281
|
+
If ``True``, return ``{"features": cls, "cls_token": cls}`` (the
|
|
282
|
+
pooled ``CLS`` representation) instead of the class logits.
|
|
283
|
+
key_padding_mask : torch.Tensor, optional
|
|
284
|
+
Boolean ``(batch, n_chans)`` mask, ``True`` for padded electrodes, so
|
|
285
|
+
recordings with different electrode sets can share a batch (upstream
|
|
286
|
+
``src_key_padding_mask``). The ``CLS`` token is never masked.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
torch.Tensor or dict
|
|
291
|
+
Class logits of shape ``(batch, n_outputs)``, or the feature dict
|
|
292
|
+
when ``return_features`` is set.
|
|
293
|
+
"""
|
|
294
|
+
batch_size, n_chans, _ = x.shape
|
|
295
|
+
if coords is None:
|
|
296
|
+
n_known = self.electrode_coords.shape[0]
|
|
297
|
+
if n_chans != n_known:
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"x has {n_chans} electrodes but the model has coordinates "
|
|
300
|
+
f"for {n_known}; pass `coords` of shape (batch, {n_chans}, 3)."
|
|
301
|
+
)
|
|
302
|
+
coords = self.electrode_coords.unsqueeze(0).expand(batch_size, -1, -1)
|
|
303
|
+
if seq_id is None:
|
|
304
|
+
seq_id = torch.zeros(batch_size, n_chans, dtype=torch.long, device=x.device)
|
|
305
|
+
|
|
306
|
+
h = self.input_embedding(x, coords, seq_id)
|
|
307
|
+
# New name (not reassigning the argument) keeps TorchScript's narrowing.
|
|
308
|
+
padded_mask: torch.Tensor | None = None
|
|
309
|
+
if key_padding_mask is not None:
|
|
310
|
+
cls_keep = torch.zeros(batch_size, 1, dtype=torch.bool, device=x.device)
|
|
311
|
+
padded_mask = torch.cat([cls_keep, key_padding_mask.to(torch.bool)], dim=1)
|
|
312
|
+
z = self.transformer_encoder(h, src_key_padding_mask=padded_mask)
|
|
313
|
+
cls_token = z[:, 0, :]
|
|
314
|
+
logits = self.final_layer(cls_token)
|
|
315
|
+
if return_features:
|
|
316
|
+
# Scripted forward stays monomorphic (same pattern as Brant).
|
|
317
|
+
if torch.jit.is_scripting():
|
|
318
|
+
return logits
|
|
319
|
+
return {"features": cls_token, "cls_token": cls_token}
|
|
320
|
+
return logits
|
|
@@ -124,13 +124,18 @@ class ShallowFBCSPNet(EEGModuleMixin, nn.Sequential):
|
|
|
124
124
|
self.drop_prob = drop_prob
|
|
125
125
|
|
|
126
126
|
self.mapping = {
|
|
127
|
-
"conv_time.weight": "conv_time_spat.conv_time.weight",
|
|
128
|
-
"conv_spat.weight": "conv_time_spat.conv_spat.weight",
|
|
129
|
-
"conv_time.bias": "conv_time_spat.conv_time.bias",
|
|
130
|
-
"conv_spat.bias": "conv_time_spat.conv_spat.bias",
|
|
131
127
|
"conv_classifier.weight": "final_layer.conv_classifier.weight",
|
|
132
128
|
"conv_classifier.bias": "final_layer.conv_classifier.bias",
|
|
133
129
|
}
|
|
130
|
+
if self.split_first_layer:
|
|
131
|
+
self.mapping.update(
|
|
132
|
+
{
|
|
133
|
+
"conv_time.weight": "conv_time_spat.conv_time.weight",
|
|
134
|
+
"conv_spat.weight": "conv_time_spat.conv_spat.weight",
|
|
135
|
+
"conv_time.bias": "conv_time_spat.conv_time.bias",
|
|
136
|
+
"conv_spat.bias": "conv_time_spat.conv_spat.bias",
|
|
137
|
+
}
|
|
138
|
+
)
|
|
134
139
|
|
|
135
140
|
self.add_module("ensuredims", Ensure4d())
|
|
136
141
|
pool_class = dict(max=nn.MaxPool2d, mean=nn.AvgPool2d)[self.pool_mode]
|
|
@@ -203,14 +208,16 @@ class ShallowFBCSPNet(EEGModuleMixin, nn.Sequential):
|
|
|
203
208
|
self.add_module("final_layer", module)
|
|
204
209
|
|
|
205
210
|
# Initialization, xavier is same as in paper...
|
|
206
|
-
init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
|
|
207
|
-
# maybe no bias in case of no split layer and batch norm
|
|
208
|
-
if self.split_first_layer or (not self.batch_norm):
|
|
209
|
-
init.constant_(self.conv_time_spat.conv_time.bias, 0)
|
|
210
211
|
if self.split_first_layer:
|
|
212
|
+
init.xavier_uniform_(self.conv_time_spat.conv_time.weight, gain=1)
|
|
213
|
+
init.constant_(self.conv_time_spat.conv_time.bias, 0)
|
|
211
214
|
init.xavier_uniform_(self.conv_time_spat.conv_spat.weight, gain=1)
|
|
212
215
|
if not self.batch_norm:
|
|
213
216
|
init.constant_(self.conv_time_spat.conv_spat.bias, 0)
|
|
217
|
+
else:
|
|
218
|
+
init.xavier_uniform_(self.conv_time.weight, gain=1)
|
|
219
|
+
if not self.batch_norm:
|
|
220
|
+
init.constant_(self.conv_time.bias, 0)
|
|
214
221
|
if self.batch_norm:
|
|
215
222
|
init.constant_(self.bnorm.weight, 1)
|
|
216
223
|
init.constant_(self.bnorm.bias, 0)
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/summary.csv
RENAMED
|
@@ -52,6 +52,7 @@ IFNet,Motor Imagery,Prediction,250,"n_chans, n_outputs, n_times, sfreq",9860,"IF
|
|
|
52
52
|
BrainModule,Speech Decoding,Prediction,250,"n_chans, n_outputs, n_times, sfreq",6186909,"BrainModule(n_chans=64, n_outputs=29, n_times=160, sfreq=1000)","Convolution","EEG, MEG"
|
|
53
53
|
Brant,Epilepsy,Prediction,250,"n_chans, n_outputs, n_times, sfreq",2902780,"Brant(n_chans=16, n_outputs=2, n_times=3000, sfreq=250)","Foundation Model,Attention/Transformer",iEEG
|
|
54
54
|
PBT,General,Prediction,250,"n_chans, n_outputs, n_times",818948,"PBT(n_chans=22, n_outputs=4, n_times=1000, sfreq=250)","Foundation Model",EEG
|
|
55
|
+
PopulationTransformer,General,"Prediction, Embedding",2048,"n_chans, n_outputs, n_times",19967746,"PopulationTransformer(n_chans=8, n_outputs=2, n_times=768)","Foundation Model,Attention/Transformer",iEEG
|
|
55
56
|
SSTDPN,Motor Imagery,Prediction,250,"n_chans, n_outputs, n_times",19502,"SSTDPN(n_chans=22, n_outputs=4, n_times=1000)","Convolution,Attention/Transformer",EEG
|
|
56
57
|
BENDR,General,"Prediction, Embedding",250,"n_chans, n_times, n_outputs",157141049,"BENDR(n_chans=22, n_outputs=4, n_times=1000)","Foundation Model,Convolution",EEG
|
|
57
58
|
LUNA,General,"Prediction, Embedding",128,"n_chans, n_times, sfreq, chs_info",7100731,"LUNA(n_chans=22, n_times=512, sfreq=128)","Convolution,Channel,Foundation Model",EEG
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/models/util.py
RENAMED
|
@@ -421,6 +421,7 @@ models_mandatory_parameters: list[
|
|
|
421
421
|
["n_chans", "n_outputs", "n_times"],
|
|
422
422
|
{"n_times": 2048, "sfreq": 2048.0}, # the pretraining rate, 1 s windows
|
|
423
423
|
),
|
|
424
|
+
("PopulationTransformer", ["n_chans", "n_outputs", "n_times"], None),
|
|
424
425
|
(
|
|
425
426
|
"InterpolatedBIOT",
|
|
426
427
|
["chs_info", "n_outputs", "sfreq", "n_times"],
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/convolution.py
RENAMED
|
@@ -48,6 +48,14 @@ class AvgPool2dWithConv(nn.Module):
|
|
|
48
48
|
# that initializes parameters or something
|
|
49
49
|
self._pool_weights = None
|
|
50
50
|
|
|
51
|
+
def _pool_weights_match_input(self, x, weight_shape):
|
|
52
|
+
return (
|
|
53
|
+
self._pool_weights is not None
|
|
54
|
+
and tuple(self._pool_weights.size()) == tuple(weight_shape)
|
|
55
|
+
and self._pool_weights.device == x.device
|
|
56
|
+
and self._pool_weights.dtype == x.dtype
|
|
57
|
+
)
|
|
58
|
+
|
|
51
59
|
def forward(self, x):
|
|
52
60
|
# Create weights for the convolution on demand:
|
|
53
61
|
# size or type of x changed...
|
|
@@ -58,17 +66,10 @@ class AvgPool2dWithConv(nn.Module):
|
|
|
58
66
|
self.kernel_size[0],
|
|
59
67
|
self.kernel_size[1],
|
|
60
68
|
)
|
|
61
|
-
if self.
|
|
62
|
-
(tuple(self._pool_weights.size()) != tuple(weight_shape))
|
|
63
|
-
or (self._pool_weights.is_cuda != x.is_cuda)
|
|
64
|
-
or (self._pool_weights.data.type() != x.data.type())
|
|
65
|
-
):
|
|
69
|
+
if not self._pool_weights_match_input(x, weight_shape):
|
|
66
70
|
n_pool = np.prod(self.kernel_size)
|
|
67
71
|
weights = np_to_th(np.ones(weight_shape, dtype=np.float32) / float(n_pool))
|
|
68
|
-
|
|
69
|
-
if x.is_cuda:
|
|
70
|
-
weights = weights.cuda()
|
|
71
|
-
self._pool_weights = weights
|
|
72
|
+
self._pool_weights = weights.to(device=x.device, dtype=x.dtype)
|
|
72
73
|
|
|
73
74
|
pooled = F.conv2d(
|
|
74
75
|
x,
|
|
@@ -143,6 +144,9 @@ class CombinedConv(nn.Module):
|
|
|
143
144
|
Whether to use bias in the temporal conv
|
|
144
145
|
bias_spat: bool
|
|
145
146
|
Whether to use bias in the spatial conv
|
|
147
|
+
stride: int | tuple[int, int]
|
|
148
|
+
Stride of the merged convolution. This is applied at the spatial
|
|
149
|
+
convolution stage, matching the unmerged temporal-then-spatial form.
|
|
146
150
|
|
|
147
151
|
Examples
|
|
148
152
|
--------
|
|
@@ -164,6 +168,7 @@ class CombinedConv(nn.Module):
|
|
|
164
168
|
filter_time_length=25,
|
|
165
169
|
bias_time=True,
|
|
166
170
|
bias_spat=True,
|
|
171
|
+
stride=1,
|
|
167
172
|
):
|
|
168
173
|
super().__init__()
|
|
169
174
|
self.bias_time = bias_time
|
|
@@ -172,7 +177,11 @@ class CombinedConv(nn.Module):
|
|
|
172
177
|
1, n_filters_time, (filter_time_length, 1), bias=bias_time, stride=1
|
|
173
178
|
)
|
|
174
179
|
self.conv_spat = nn.Conv2d(
|
|
175
|
-
n_filters_time,
|
|
180
|
+
n_filters_time,
|
|
181
|
+
n_filters_spat,
|
|
182
|
+
(1, in_chans),
|
|
183
|
+
bias=bias_spat,
|
|
184
|
+
stride=stride,
|
|
176
185
|
)
|
|
177
186
|
|
|
178
187
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
@@ -210,7 +219,9 @@ class CombinedConv(nn.Module):
|
|
|
210
219
|
|
|
211
220
|
bias = calculated_bias
|
|
212
221
|
|
|
213
|
-
return F.conv2d(
|
|
222
|
+
return F.conv2d(
|
|
223
|
+
x, weight=combined_weight, bias=bias, stride=self.conv_spat.stride
|
|
224
|
+
)
|
|
214
225
|
|
|
215
226
|
|
|
216
227
|
class CausalConv1d(nn.Conv1d):
|
|
@@ -274,16 +285,16 @@ class CausalConv1d(nn.Conv1d):
|
|
|
274
285
|
)
|
|
275
286
|
|
|
276
287
|
def forward(self, X):
|
|
277
|
-
|
|
288
|
+
X = F.pad(X, (self.padding[0], 0))
|
|
289
|
+
return F.conv1d(
|
|
278
290
|
X,
|
|
279
291
|
self.weight,
|
|
280
292
|
self.bias,
|
|
281
293
|
stride=self.stride,
|
|
282
|
-
padding=
|
|
294
|
+
padding=0,
|
|
283
295
|
dilation=self.dilation,
|
|
284
296
|
groups=self.groups,
|
|
285
297
|
)
|
|
286
|
-
return out[..., : -self.padding[0]]
|
|
287
298
|
|
|
288
299
|
|
|
289
300
|
class DepthwiseConv2d(torch.nn.Conv2d):
|
{braindecode-1.8.1.dev173387713 → braindecode-1.8.1.dev174857482}/braindecode/modules/layers.py
RENAMED
|
@@ -62,6 +62,8 @@ class Chomp1d(nn.Module):
|
|
|
62
62
|
return "chomp_size={}".format(self.chomp_size)
|
|
63
63
|
|
|
64
64
|
def forward(self, x):
|
|
65
|
+
if self.chomp_size == 0:
|
|
66
|
+
return x.contiguous()
|
|
65
67
|
return x[:, :, : -self.chomp_size].contiguous()
|
|
66
68
|
|
|
67
69
|
|
|
@@ -110,8 +112,8 @@ class TimeDistributed(nn.Module):
|
|
|
110
112
|
Shape (batch_size, seq_len, output_size).
|
|
111
113
|
"""
|
|
112
114
|
b, s, c, t = x.shape
|
|
113
|
-
out = self.module(x.
|
|
114
|
-
return out.
|
|
115
|
+
out = self.module(x.reshape(b * s, c, t))
|
|
116
|
+
return out.reshape(b, s, -1)
|
|
115
117
|
|
|
116
118
|
|
|
117
119
|
class DropPath(nn.Module):
|