braindecode 1.7.0.dev182642410__tar.gz → 1.7.0.dev183981614__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.7.0.dev182642410/braindecode.egg-info → braindecode-1.7.0.dev183981614}/PKG-INFO +1 -1
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/classifier.py +7 -7
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/functional/__init__.py +4 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/functional/functions.py +39 -2
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/__init__.py +2 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/bendr.py +8 -1
- braindecode-1.7.0.dev183981614/braindecode/models/dance.py +368 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegminer.py +38 -62
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/reve.py +32 -33
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/summary.csv +1 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/tcn.py +5 -2
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/util.py +22 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/__init__.py +5 -2
- braindecode-1.7.0.dev183981614/braindecode/modules/dance_modules.py +333 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/filter.py +16 -10
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/regressor.py +7 -7
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/training/__init__.py +4 -1
- braindecode-1.7.0.dev183981614/braindecode/training/losses.py +284 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/training/scoring.py +46 -5
- braindecode-1.7.0.dev183981614/braindecode/version.py +1 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode.egg-info/SOURCES.txt +2 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/api.rst +2 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/whats_new.rst +31 -1
- braindecode-1.7.0.dev182642410/braindecode/training/losses.py +0 -105
- braindecode-1.7.0.dev182642410/braindecode/version.py +0 -1
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/LICENSE.txt +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/MANIFEST.in +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/NOTICE.txt +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/README.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/augmentation/base.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/base.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/collate.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/datautil/util.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/base.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/biot.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/brainmodule.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/codebrain.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/config.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/deep4.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eeginception_mi.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegnex.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegpt.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegsym.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/interpolated.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/labram.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/luna.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/medformer.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/meta_neuromotor.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/msvtnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/mvpformer.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/patchedtransformer.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/signal_jepa.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/steegformer.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/usleep.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/activation.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/attention.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/blocks.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/interpolation.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/layers.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/linear.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/stats.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/util.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/samplers/base.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/util.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/Makefile +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/cite.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/conf.py +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/help.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/index.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/install/install.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/install/install_pip.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/install/install_source.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/models.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/models_table.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/pyproject.toml +0 -0
- {braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/setup.cfg +0 -0
{braindecode-1.7.0.dev182642410/braindecode.egg-info → braindecode-1.7.0.dev183981614}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braindecode
|
|
3
|
-
Version: 1.7.0.
|
|
3
|
+
Version: 1.7.0.dev183981614
|
|
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>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Lukas Gemein <l.gemein@gmail.com>
|
|
4
4
|
# Bruno Aristimunha <b.aristimunha@gmail.com>
|
|
5
5
|
# Pierre Guetschel <pierre.guetschel@gmail.com>
|
|
6
|
+
# Sarthak Tayal <sarthaktayal2@gmail.com>
|
|
6
7
|
#
|
|
7
8
|
# License: BSD (3-clause)
|
|
8
9
|
|
|
@@ -195,9 +196,7 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
195
196
|
return self.predict_proba(X).argmax(1)
|
|
196
197
|
|
|
197
198
|
def predict_trials(self, X, return_targets=True):
|
|
198
|
-
"""Create trialwise predictions
|
|
199
|
-
|
|
200
|
-
labels from cropped dataset.
|
|
199
|
+
"""Create trialwise predictions from a cropped dataset.
|
|
201
200
|
|
|
202
201
|
Parameters
|
|
203
202
|
----------
|
|
@@ -212,10 +211,11 @@ class EEGClassifier(_EEGNeuralNet, NeuralNetClassifier):
|
|
|
212
211
|
3-dimensional array (n_trials x n_classes x n_predictions), where
|
|
213
212
|
the number of predictions depend on the chosen window size and the
|
|
214
213
|
receptive field of the network.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
214
|
+
trial_targets : np.ndarray
|
|
215
|
+
Ground-truth targets from the dataset in a 2-dimensional array
|
|
216
|
+
(n_trials x n_targets). Only returned when ``return_targets=True``.
|
|
217
|
+
The number of targets depends on the decoding paradigm and can be
|
|
218
|
+
either a single value, multiple values, or a sequence.
|
|
219
219
|
"""
|
|
220
220
|
if not self.cropped:
|
|
221
221
|
warnings.warn(
|
{braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/functional/__init__.py
RENAMED
|
@@ -5,6 +5,8 @@ from .functions import (
|
|
|
5
5
|
dwt_max_level,
|
|
6
6
|
hilbert_freq,
|
|
7
7
|
identity,
|
|
8
|
+
iou_1d,
|
|
9
|
+
pairwise_iou_1d,
|
|
8
10
|
plv_time,
|
|
9
11
|
safe_log,
|
|
10
12
|
sinusoidal_positional_encoding,
|
|
@@ -20,6 +22,8 @@ __all__ = [
|
|
|
20
22
|
"dwt_max_level",
|
|
21
23
|
"hilbert_freq",
|
|
22
24
|
"identity",
|
|
25
|
+
"iou_1d",
|
|
26
|
+
"pairwise_iou_1d",
|
|
23
27
|
"plv_time",
|
|
24
28
|
"safe_log",
|
|
25
29
|
"sinusoidal_positional_encoding",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Authors: Robin Schirrmeister <robintibor@gmail.com>
|
|
2
2
|
# Bruno Aristimunha <b.aristimunha@gmail.com>
|
|
3
|
+
# Sarthak Tayal <sarthaktayal2@gmail.com>
|
|
3
4
|
#
|
|
4
5
|
# License: BSD (3-clause)
|
|
5
6
|
|
|
@@ -109,7 +110,7 @@ def _get_gaussian_kernel1d(kernel_size: int, sigma: float) -> torch.Tensor:
|
|
|
109
110
|
return kernel1d
|
|
110
111
|
|
|
111
112
|
|
|
112
|
-
def hilbert_freq(x, forward_fourier=True):
|
|
113
|
+
def hilbert_freq(x: torch.Tensor, forward_fourier: bool = True) -> torch.Tensor:
|
|
113
114
|
r"""
|
|
114
115
|
Compute the Hilbert transform using PyTorch, separating the real and
|
|
115
116
|
imaginary parts.
|
|
@@ -178,7 +179,9 @@ def hilbert_freq(x, forward_fourier=True):
|
|
|
178
179
|
return x
|
|
179
180
|
|
|
180
181
|
|
|
181
|
-
def plv_time(
|
|
182
|
+
def plv_time(
|
|
183
|
+
x: torch.Tensor, forward_fourier: bool = True, epsilon: float = 1e-6
|
|
184
|
+
) -> torch.Tensor:
|
|
182
185
|
"""Compute the Phase Locking Value (PLV) metric in the time domain.
|
|
183
186
|
|
|
184
187
|
The Phase Locking Value (PLV) is a measure of the synchronization between
|
|
@@ -257,6 +260,40 @@ def plv_time(x, forward_fourier=True, epsilon: float = 1e-6):
|
|
|
257
260
|
return plv_matrix
|
|
258
261
|
|
|
259
262
|
|
|
263
|
+
# -----------------------------------------------------------------------------
|
|
264
|
+
# DANCE functional helpers
|
|
265
|
+
#
|
|
266
|
+
# Authors: Bruno Aristimunha <b.aristimunha@gmail.com>
|
|
267
|
+
#
|
|
268
|
+
# License: MIT
|
|
269
|
+
#
|
|
270
|
+
# Ported from the DANCE event-detection model (facebookresearch/dance, MIT).
|
|
271
|
+
# -----------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def iou_1d(s1, e1, s2, e2, eps: float = 1e-7):
|
|
275
|
+
"""ELEMENTWISE 1-D temporal IoU. All four inputs share the same shape S
|
|
276
|
+
(e.g. ``(B, Q)``); returns IoU of shape S. Used by ``DanceLoss`` on the
|
|
277
|
+
matched ``(B, Q)`` spans. For (Q,)x(T,) pairwise IoU use ``pairwise_iou_1d``.
|
|
278
|
+
"""
|
|
279
|
+
inter = (torch.minimum(e1, e2) - torch.maximum(s1, s2)).clamp(min=0)
|
|
280
|
+
union = (e1 - s1) + (e2 - s2) - inter
|
|
281
|
+
return inter / (union + eps)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def pairwise_iou_1d(s1, e1, s2, e2, eps: float = 1e-7):
|
|
285
|
+
"""PAIRWISE 1-D temporal IoU. ``s1,e1`` shape ``(Q,)``, ``s2,e2`` shape
|
|
286
|
+
``(T,)``; returns ``(Q, T)`` (broadcast ``[:, None]`` x ``[None, :]``).
|
|
287
|
+
Transcribed from ``dance/matcher.py:33-39`` (``_pairwise_iou``). Used by
|
|
288
|
+
``HungarianMatcher`` to build the ``(Q, n_targets)`` cost matrix.
|
|
289
|
+
"""
|
|
290
|
+
inter_start = torch.maximum(s1[:, None], s2[None, :])
|
|
291
|
+
inter_end = torch.minimum(e1[:, None], e2[None, :])
|
|
292
|
+
inter = (inter_end - inter_start).clamp(min=0)
|
|
293
|
+
union = (e1 - s1)[:, None] + (e2 - s2)[None, :] - inter
|
|
294
|
+
return inter / (union + eps)
|
|
295
|
+
|
|
296
|
+
|
|
260
297
|
def daubechies_filters(n_vanishing: int) -> torch.Tensor:
|
|
261
298
|
r"""Daubechies ``db<n_vanishing>`` wavelet decomposition filters.
|
|
262
299
|
|
{braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/__init__.py
RENAMED
|
@@ -11,6 +11,7 @@ from .cbramod import CBraMod
|
|
|
11
11
|
from .codebrain import CodeBrain
|
|
12
12
|
from .contrawr import ContraWR
|
|
13
13
|
from .ctnet import CTNet
|
|
14
|
+
from .dance import DANCE
|
|
14
15
|
from .deep4 import Deep4Net
|
|
15
16
|
from .deepsleepnet import DeepSleepNet
|
|
16
17
|
from .dgcnn import DGCNN
|
|
@@ -86,6 +87,7 @@ __all__ = [
|
|
|
86
87
|
"CodeBrain",
|
|
87
88
|
"ContraWR",
|
|
88
89
|
"CTNet",
|
|
90
|
+
"DANCE",
|
|
89
91
|
"Deep4Net",
|
|
90
92
|
"DeepSleepNet",
|
|
91
93
|
"BrainModule",
|
{braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/bendr.py
RENAMED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
# Authors: Bruno Aristimunha <b.aristimunha@gmail.com>
|
|
2
|
+
# Sarthak Tayal <sarthaktayal2@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# License: BSD (3-clause)
|
|
5
|
+
|
|
1
6
|
import copy
|
|
2
7
|
|
|
3
8
|
import numpy as np
|
|
@@ -435,7 +440,9 @@ class _ConvEncoderBENDR(nn.Module):
|
|
|
435
440
|
padding=width
|
|
436
441
|
// 2, # Correct padding for 'same' output length before stride
|
|
437
442
|
),
|
|
438
|
-
|
|
443
|
+
# channel-wise dropout (matches paper specification), on
|
|
444
|
+
# (batch, channels, times) activations
|
|
445
|
+
nn.Dropout1d(dropout),
|
|
439
446
|
nn.GroupNorm(
|
|
440
447
|
encoder_h // 2, encoder_h
|
|
441
448
|
), # Consider making num_groups configurable or ensure encoder_h is divisible by 2
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# Authors: Bruno Aristimunha <b.aristimunha@gmail.com>
|
|
2
|
+
# Meta Platforms, Inc. and affiliates (original DANCE)
|
|
3
|
+
#
|
|
4
|
+
# License: MIT
|
|
5
|
+
# Adapted from https://github.com/facebookresearch/dance (MIT).
|
|
6
|
+
"""``DANCE``: detect-and-classify EEG events (DETR for 1-D EEG)."""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
|
|
12
|
+
import torch
|
|
13
|
+
from torch import nn
|
|
14
|
+
|
|
15
|
+
from braindecode.models.base import EEGModuleMixin
|
|
16
|
+
from braindecode.models.util import (
|
|
17
|
+
_disable_batch_norm_training_if_batch_size_one, # decorates ``forward``
|
|
18
|
+
)
|
|
19
|
+
from braindecode.models.util import has_valid_locations as _has_valid_locations
|
|
20
|
+
from braindecode.models.util import positions_from_chs_info as _positions_from_chs_info
|
|
21
|
+
from braindecode.modules import ChannelMerger, Perceiver, SimpleConv
|
|
22
|
+
from braindecode.modules.dance_modules import DanceDetrDecoder
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DANCE(EEGModuleMixin, nn.Module):
|
|
26
|
+
r"""DANCE from Lévy et al (2026) [dance]_.
|
|
27
|
+
|
|
28
|
+
:bdg-success:`Convolution` :bdg-info:`Attention/Transformer` :bdg-dark-line:`Channel`
|
|
29
|
+
|
|
30
|
+
DANCE frames EEG decoding as event *set prediction*: a long, unaligned
|
|
31
|
+
window is mapped to a set of events ``(t_start, t_end, class)`` with
|
|
32
|
+
normalized ``[0, 1]`` spans -- DETR for 1-D EEG.
|
|
33
|
+
|
|
34
|
+
.. rubric:: Architecture Overview
|
|
35
|
+
|
|
36
|
+
``ChannelMerger`` (spatial Fourier attention over electrode positions) ->
|
|
37
|
+
``SimpleConv`` (dilated conv stack) -> ``Perceiver`` (cross-attention to a
|
|
38
|
+
fixed 256-latent grid) -> a dense head (``forward`` output) and a DETR
|
|
39
|
+
cross-attention decoder (``detect`` output).
|
|
40
|
+
|
|
41
|
+
.. rubric:: Macro Components
|
|
42
|
+
|
|
43
|
+
``DANCE.conv.merger`` (Spatial ChannelMerger)
|
|
44
|
+
**Operations.** :class:`~braindecode.modules.ChannelMerger` Fourier-embeds
|
|
45
|
+
each electrode's ``(x, y)`` position (:class:`~braindecode.modules.FourierEmb`)
|
|
46
|
+
and computes ``n_virtual_channels`` softmax-attention combinations of the
|
|
47
|
+
input channels, mapping ``(B, n_chans, T) -> (B, n_virtual_channels, T)``.
|
|
48
|
+
Nested inside ``self.conv`` (matching upstream); disabled (``None``) when
|
|
49
|
+
``chs_info`` has no usable locations. Controlled by ``merger_drop_prob``.
|
|
50
|
+
**Role.** Makes the model montage-agnostic by projecting any electrode
|
|
51
|
+
layout onto a fixed virtual-channel basis.
|
|
52
|
+
|
|
53
|
+
``DANCE.conv`` (SimpleConv dilated front-end)
|
|
54
|
+
**Operations.** :class:`~braindecode.modules.SimpleConv` runs the nested
|
|
55
|
+
merger, a ``1x1`` ``initial_linear`` projection, then ``conv_depth``
|
|
56
|
+
residual dilated ``Conv1d`` blocks (``nn.ReLU``, dilation accumulating as
|
|
57
|
+
``int(dilation * conv_dilation_growth)`` per block, same-padding preserves
|
|
58
|
+
``T``), mapping ``-> (B, embed_dim, T)``.
|
|
59
|
+
**Role.** Builds the temporal feature representation (Défossez lineage).
|
|
60
|
+
|
|
61
|
+
``DANCE.perceiver`` (Perceiver bottleneck)
|
|
62
|
+
**Operations.** :class:`~braindecode.modules.Perceiver` Fourier-encodes the
|
|
63
|
+
time axis, then cross-attends a fixed ``num_latents``-token learnable grid to
|
|
64
|
+
the conv features over ``perceiver_depth`` blocks, mapping
|
|
65
|
+
``(B, T, embed_dim) -> (B, num_latents, embed_dim)``.
|
|
66
|
+
**Role.** Makes the model length-agnostic (any ``T`` -> ``num_latents``
|
|
67
|
+
tokens) and forms the detection time-grid (``num_latents / duration``
|
|
68
|
+
tokens/s).
|
|
69
|
+
|
|
70
|
+
``DANCE.decoder`` (DETR cross-attention decoder)
|
|
71
|
+
**Operations.** ``DanceDetrDecoder`` projects the latents to
|
|
72
|
+
``decoder_dim``, then ``decoder_depth`` self-/cross-attention layers update
|
|
73
|
+
``n_queries`` learnable event queries; per-query heads emit class logits and
|
|
74
|
+
sigmoid ``start``/``end`` spans. Used only by :meth:`detect` (not
|
|
75
|
+
:meth:`forward`).
|
|
76
|
+
**Role.** Produces the event-set prediction ``{class, start, end}``.
|
|
77
|
+
|
|
78
|
+
``DANCE.final_layer`` (dense per-token head)
|
|
79
|
+
**Operations.** ``nn.Linear(embed_dim, n_outputs)`` applied to every latent
|
|
80
|
+
token, mapping ``(B, num_latents, embed_dim) -> (B, num_latents, n_outputs)``.
|
|
81
|
+
**Role.** The :meth:`forward` output (dense per-token class logits) and the
|
|
82
|
+
``dense`` term consumed by the consistency loss.
|
|
83
|
+
|
|
84
|
+
.. rubric:: Temporal, Spatial, and Spectral Encoding
|
|
85
|
+
|
|
86
|
+
Temporal context comes from the dilated conv stack; spatial structure from
|
|
87
|
+
the Fourier-position channel merge; the Perceiver fourier-encodes the time
|
|
88
|
+
axis before cross-attention.
|
|
89
|
+
|
|
90
|
+
.. rubric:: Additional Mechanisms
|
|
91
|
+
|
|
92
|
+
The DETR decoder uses learnable event queries and emits per-query class
|
|
93
|
+
logits plus sigmoid start/end spans. The dense head provides per-token
|
|
94
|
+
class logits used as ``forward``'s output and for the consistency loss.
|
|
95
|
+
|
|
96
|
+
.. versionadded:: 1.6.1
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
n_queries : int, optional
|
|
101
|
+
Number of learnable event queries. The default is ``100``.
|
|
102
|
+
use_channel_merger : bool, optional
|
|
103
|
+
Enable the spatial Fourier ChannelMerger. Auto-disabled if ``chs_info``
|
|
104
|
+
has no usable electrode locations. The default is ``True``.
|
|
105
|
+
n_virtual_channels : int, optional
|
|
106
|
+
Merger output channels. The default is ``270``.
|
|
107
|
+
fourier_emb_dim : int, optional
|
|
108
|
+
Fourier position embedding dim. The default is ``2048``.
|
|
109
|
+
merger_drop_prob : float, optional
|
|
110
|
+
Spatial dropout of the ChannelMerger ONLY (``self.conv.merger``);
|
|
111
|
+
bans whole channels within a random radius during training. The
|
|
112
|
+
default is ``0.2``.
|
|
113
|
+
embed_dim : int, optional
|
|
114
|
+
Conv/Perceiver feature dim. The default is ``128``.
|
|
115
|
+
conv_hidden : int, optional
|
|
116
|
+
SimpleConv hidden width. The default is ``512``.
|
|
117
|
+
conv_depth : int, optional
|
|
118
|
+
Number of dilated conv blocks. The default is ``10``.
|
|
119
|
+
conv_kernel_size : int, optional
|
|
120
|
+
Conv kernel size. The default is ``9``.
|
|
121
|
+
conv_dilation_growth : float, optional
|
|
122
|
+
Per-block dilation growth. The default is ``2.5``.
|
|
123
|
+
conv_initial_linear : int, optional
|
|
124
|
+
1x1 projection width. The default is ``256``.
|
|
125
|
+
conv_initial_depth : int, optional
|
|
126
|
+
Number of 1x1 projections. The default is ``1``.
|
|
127
|
+
conv_drop_prob : float, optional
|
|
128
|
+
Conv-block dropout knob, exposed for API symmetry. NOTE: the verified
|
|
129
|
+
DANCE config has NO conv dropout, so this maps to no upstream weights
|
|
130
|
+
and does not affect parity; kept for forward-compatibility. The
|
|
131
|
+
default is ``0.2`` (the paper value).
|
|
132
|
+
num_latents : int, optional
|
|
133
|
+
Perceiver latent count (detection token grid). The default is ``256``.
|
|
134
|
+
perceiver_depth : int, optional
|
|
135
|
+
Perceiver cross-attn blocks. The default is ``6``.
|
|
136
|
+
cross_attn_heads : int, optional
|
|
137
|
+
Perceiver cross-attn heads. The default is ``2``.
|
|
138
|
+
latent_attn_heads : int, optional
|
|
139
|
+
Perceiver self-attn heads. The default is ``2``.
|
|
140
|
+
cross_dim_head : int, optional
|
|
141
|
+
Perceiver cross-attn head dim. The default is ``64``.
|
|
142
|
+
latent_dim_head : int, optional
|
|
143
|
+
Perceiver self-attn head dim. The default is ``64``.
|
|
144
|
+
max_freq : float, optional
|
|
145
|
+
Perceiver fourier max frequency. The default is ``10.0``.
|
|
146
|
+
num_freq_bands : int, optional
|
|
147
|
+
Perceiver fourier bands. The default is ``6``.
|
|
148
|
+
decoder_dim : int, optional
|
|
149
|
+
DETR decoder dim. The default is ``256``.
|
|
150
|
+
decoder_depth : int, optional
|
|
151
|
+
DETR decoder layers. The default is ``4``.
|
|
152
|
+
decoder_heads : int, optional
|
|
153
|
+
DETR decoder heads. The default is ``4``.
|
|
154
|
+
activation : type[nn.Module], optional
|
|
155
|
+
Accepted for interface symmetry but currently INERT: it is forwarded to
|
|
156
|
+
``self.decoder`` yet the decoder feed-forward hardwires GEGLU
|
|
157
|
+
(:class:`~braindecode.modules.dance_modules._FeedForward`), and the
|
|
158
|
+
``SimpleConv`` front-end hardcodes ``nn.ReLU`` to match upstream. No
|
|
159
|
+
submodule reads it today. The default is ``nn.GELU``.
|
|
160
|
+
drop_prob : float, optional
|
|
161
|
+
Dropout applied to the raw input (``self.input_drop``) and inside the
|
|
162
|
+
DETR decoder (``self.decoder``). Does NOT touch the merger (use
|
|
163
|
+
``merger_drop_prob``) or the conv stack (no conv dropout upstream).
|
|
164
|
+
The default is ``0.1``.
|
|
165
|
+
|
|
166
|
+
References
|
|
167
|
+
----------
|
|
168
|
+
.. [dance] Lévy, Banville, Rapin, King, Moreau, d'Ascoli (2026). DANCE:
|
|
169
|
+
Detect and Classify Events in EEG. arXiv:2605.10688.
|
|
170
|
+
.. [defossez2023] Défossez et al. (2023). Decoding speech from
|
|
171
|
+
non-invasive brain recordings.
|
|
172
|
+
.. [perceiver2021] Jaegle et al. (2021). Perceiver: General perception
|
|
173
|
+
with iterative attention.
|
|
174
|
+
.. [detr2020] Carion et al. (2020). End-to-end object detection with
|
|
175
|
+
transformers.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
def __init__(
|
|
179
|
+
self,
|
|
180
|
+
n_outputs=None,
|
|
181
|
+
n_chans=None,
|
|
182
|
+
chs_info=None,
|
|
183
|
+
n_times=None,
|
|
184
|
+
input_window_seconds=None,
|
|
185
|
+
sfreq=None,
|
|
186
|
+
*,
|
|
187
|
+
n_queries: int = 100,
|
|
188
|
+
use_channel_merger: bool = True,
|
|
189
|
+
n_virtual_channels: int = 270,
|
|
190
|
+
fourier_emb_dim: int = 2048,
|
|
191
|
+
merger_drop_prob: float = 0.2,
|
|
192
|
+
embed_dim: int = 128,
|
|
193
|
+
conv_hidden: int = 512,
|
|
194
|
+
conv_depth: int = 10,
|
|
195
|
+
conv_kernel_size: int = 9,
|
|
196
|
+
conv_dilation_growth: float = 2.5,
|
|
197
|
+
conv_initial_linear: int = 256,
|
|
198
|
+
conv_initial_depth: int = 1,
|
|
199
|
+
conv_drop_prob: float = 0.2,
|
|
200
|
+
num_latents: int = 256,
|
|
201
|
+
perceiver_depth: int = 6,
|
|
202
|
+
cross_attn_heads: int = 2,
|
|
203
|
+
latent_attn_heads: int = 2,
|
|
204
|
+
cross_dim_head: int = 64,
|
|
205
|
+
latent_dim_head: int = 64,
|
|
206
|
+
max_freq: float = 10.0,
|
|
207
|
+
num_freq_bands: int = 6,
|
|
208
|
+
decoder_dim: int = 256,
|
|
209
|
+
decoder_depth: int = 4,
|
|
210
|
+
decoder_heads: int = 4,
|
|
211
|
+
activation: type[nn.Module] = nn.GELU,
|
|
212
|
+
drop_prob: float = 0.1,
|
|
213
|
+
):
|
|
214
|
+
super().__init__(
|
|
215
|
+
n_outputs=n_outputs,
|
|
216
|
+
n_chans=n_chans,
|
|
217
|
+
chs_info=chs_info,
|
|
218
|
+
n_times=n_times,
|
|
219
|
+
input_window_seconds=input_window_seconds,
|
|
220
|
+
sfreq=sfreq,
|
|
221
|
+
)
|
|
222
|
+
del n_outputs, n_chans, sfreq, n_times, input_window_seconds, chs_info
|
|
223
|
+
|
|
224
|
+
self.num_latents = num_latents
|
|
225
|
+
self.n_queries = n_queries
|
|
226
|
+
self.embed_dim = embed_dim
|
|
227
|
+
|
|
228
|
+
# Decide the merger up front (fall back to no-merger if no locations).
|
|
229
|
+
if use_channel_merger and not _has_valid_locations(self.chs_info):
|
|
230
|
+
warnings.warn(
|
|
231
|
+
"DANCE: chs_info has no usable electrode locations "
|
|
232
|
+
"('loc' missing or all-zero); disabling the ChannelMerger "
|
|
233
|
+
"(use_channel_merger=False).",
|
|
234
|
+
UserWarning,
|
|
235
|
+
)
|
|
236
|
+
use_channel_merger = False
|
|
237
|
+
# Public flag mirroring the fallback decision (``self.conv.merger`` is the
|
|
238
|
+
# source of truth in forward); kept for introspection/tests.
|
|
239
|
+
self.use_channel_merger = use_channel_merger
|
|
240
|
+
|
|
241
|
+
# The merger is NESTED INSIDE SimpleConv (self.conv.merger), matching
|
|
242
|
+
# upstream SimpleConvModel's call path and state_dict layout. There is
|
|
243
|
+
# NO top-level self.channel_merger. The (n_chans, 2) positions buffer is
|
|
244
|
+
# derived from chs_info and broadcast in forward; subject_ids is always
|
|
245
|
+
# None (braindecode has no subjects; per_subject=False).
|
|
246
|
+
merger = None
|
|
247
|
+
if use_channel_merger:
|
|
248
|
+
positions = _positions_from_chs_info(self.chs_info)
|
|
249
|
+
self.register_buffer(
|
|
250
|
+
"channel_positions",
|
|
251
|
+
torch.as_tensor(positions, dtype=torch.float32),
|
|
252
|
+
persistent=False,
|
|
253
|
+
)
|
|
254
|
+
merger = ChannelMerger(
|
|
255
|
+
out_channels=n_virtual_channels,
|
|
256
|
+
pos_dim=fourier_emb_dim,
|
|
257
|
+
dropout=merger_drop_prob,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
self.input_drop = nn.Dropout(drop_prob)
|
|
261
|
+
self.conv = SimpleConv(
|
|
262
|
+
in_channels=self.n_chans,
|
|
263
|
+
out_channels=embed_dim,
|
|
264
|
+
hidden=conv_hidden,
|
|
265
|
+
depth=conv_depth,
|
|
266
|
+
kernel_size=conv_kernel_size,
|
|
267
|
+
dilation_growth=conv_dilation_growth,
|
|
268
|
+
initial_linear=conv_initial_linear,
|
|
269
|
+
initial_depth=conv_initial_depth,
|
|
270
|
+
drop_prob=conv_drop_prob,
|
|
271
|
+
activation=nn.ReLU,
|
|
272
|
+
merger=merger,
|
|
273
|
+
)
|
|
274
|
+
# The conv stack is SAME-padded, so it preserves length for any T >= 1;
|
|
275
|
+
# the dilated receptive field is not an input-length requirement. The
|
|
276
|
+
# genuine minimum is one first-block kernel (dilation 1), so the guard
|
|
277
|
+
# uses ``conv_kernel_size`` rather than the full receptive field.
|
|
278
|
+
self._min_n_times = conv_kernel_size
|
|
279
|
+
self.perceiver = Perceiver(
|
|
280
|
+
input_dim=embed_dim,
|
|
281
|
+
num_latents=num_latents,
|
|
282
|
+
latent_dim=embed_dim,
|
|
283
|
+
depth=perceiver_depth,
|
|
284
|
+
cross_heads=cross_attn_heads,
|
|
285
|
+
latent_heads=latent_attn_heads,
|
|
286
|
+
cross_dim_head=cross_dim_head,
|
|
287
|
+
latent_dim_head=latent_dim_head,
|
|
288
|
+
max_freq=max_freq,
|
|
289
|
+
num_freq_bands=num_freq_bands,
|
|
290
|
+
)
|
|
291
|
+
self.decoder = DanceDetrDecoder(
|
|
292
|
+
input_dim=embed_dim,
|
|
293
|
+
dim=decoder_dim,
|
|
294
|
+
depth=decoder_depth,
|
|
295
|
+
heads=decoder_heads,
|
|
296
|
+
n_queries=n_queries,
|
|
297
|
+
n_outputs=self.n_outputs,
|
|
298
|
+
drop_prob=drop_prob,
|
|
299
|
+
activation=activation,
|
|
300
|
+
)
|
|
301
|
+
# final_layer LAST so it lands in the last two named_children(); init
|
|
302
|
+
# weights AFTER it exists so the dense head gets the custom init too.
|
|
303
|
+
self.final_layer = nn.Linear(embed_dim, self.n_outputs)
|
|
304
|
+
self.apply(self._init_weights)
|
|
305
|
+
|
|
306
|
+
@staticmethod
|
|
307
|
+
def _init_weights(module: nn.Module) -> None:
|
|
308
|
+
if isinstance(module, nn.Linear):
|
|
309
|
+
nn.init.trunc_normal_(module.weight, std=0.02)
|
|
310
|
+
if module.bias is not None:
|
|
311
|
+
nn.init.zeros_(module.bias)
|
|
312
|
+
elif isinstance(module, nn.LayerNorm):
|
|
313
|
+
nn.init.ones_(module.weight)
|
|
314
|
+
nn.init.zeros_(module.bias)
|
|
315
|
+
|
|
316
|
+
def _encode(self, x: torch.Tensor) -> torch.Tensor:
|
|
317
|
+
if x.ndim != 3 or x.shape[-2] != self.n_chans:
|
|
318
|
+
raise ValueError(
|
|
319
|
+
f"expected (batch, {self.n_chans}, T) input; got {tuple(x.shape)}."
|
|
320
|
+
)
|
|
321
|
+
if x.shape[-1] < self._min_n_times:
|
|
322
|
+
raise ValueError(
|
|
323
|
+
f"n_times={x.shape[-1]} is shorter than the minimum input "
|
|
324
|
+
f"length ({self._min_n_times} samples = one conv kernel)."
|
|
325
|
+
)
|
|
326
|
+
x = self.input_drop(x)
|
|
327
|
+
# The merger is nested inside self.conv; pass positions through it.
|
|
328
|
+
# subject_ids is always None (braindecode has no subjects).
|
|
329
|
+
if self.conv.merger is not None:
|
|
330
|
+
pos = self.channel_positions.unsqueeze(0).expand(x.size(0), -1, -1)
|
|
331
|
+
x = self.conv(x, positions=pos) # merger -> initial_linear -> blocks
|
|
332
|
+
else:
|
|
333
|
+
x = self.conv(x) # (B, embed_dim, T)
|
|
334
|
+
x = x.transpose(2, 1) # (B, T, embed_dim)
|
|
335
|
+
x = self.perceiver(x) # (B, num_latents, embed_dim)
|
|
336
|
+
return x
|
|
337
|
+
|
|
338
|
+
@_disable_batch_norm_training_if_batch_size_one
|
|
339
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
340
|
+
latents = self._encode(x) # (B, num_latents, embed_dim)
|
|
341
|
+
return self.final_layer(latents) # (B, num_latents, n_outputs)
|
|
342
|
+
|
|
343
|
+
@_disable_batch_norm_training_if_batch_size_one
|
|
344
|
+
def detect(self, x: torch.Tensor) -> dict:
|
|
345
|
+
latents = self._encode(x) # (B, num_latents, embed_dim)
|
|
346
|
+
events = self.decoder(latents) # {class, start, end}
|
|
347
|
+
events["dense"] = self.final_layer(latents) # (B, num_latents, n_outputs)
|
|
348
|
+
return events
|
|
349
|
+
|
|
350
|
+
def reset_head(self, n_outputs: int) -> None:
|
|
351
|
+
"""Replace the dense head and the DETR class head for a new ``n_outputs``."""
|
|
352
|
+
if n_outputs <= 0:
|
|
353
|
+
raise ValueError(f"n_outputs must be positive; got {n_outputs}.")
|
|
354
|
+
old = self.final_layer
|
|
355
|
+
self.final_layer = nn.Linear(old.in_features, n_outputs).to(
|
|
356
|
+
device=old.weight.device, dtype=old.weight.dtype
|
|
357
|
+
)
|
|
358
|
+
ch = self.decoder.class_head
|
|
359
|
+
self.decoder.class_head = nn.Linear(ch.in_features, n_outputs).to(
|
|
360
|
+
device=ch.weight.device, dtype=ch.weight.dtype
|
|
361
|
+
)
|
|
362
|
+
self._n_outputs = n_outputs
|
|
363
|
+
init_kwargs = getattr(self, "_braindecode_init_kwargs", None)
|
|
364
|
+
if init_kwargs is not None and "n_outputs" in init_kwargs:
|
|
365
|
+
init_kwargs["n_outputs"] = n_outputs
|
|
366
|
+
hub_config = getattr(self, "_hub_mixin_config", None)
|
|
367
|
+
if hub_config is not None and "n_outputs" in hub_config:
|
|
368
|
+
hub_config["n_outputs"] = n_outputs
|
{braindecode-1.7.0.dev182642410 → braindecode-1.7.0.dev183981614}/braindecode/models/eegminer.py
RENAMED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* https://www.ipo.gov.uk/p-ipsum/Case/ApplicationNumber/GB2113420.0
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
# Authors: Sarthak Tayal <sarthaktayal2@gmail.com>
|
|
9
9
|
|
|
10
10
|
import torch
|
|
11
|
-
from einops.layers.torch import Rearrange
|
|
12
11
|
from torch import nn
|
|
13
12
|
|
|
14
13
|
import braindecode.functional as F
|
|
@@ -19,6 +18,36 @@ from braindecode.modules import GeneralizedGaussianFilter
|
|
|
19
18
|
_eeg_miner_methods = ["mag", "corr", "plv"]
|
|
20
19
|
|
|
21
20
|
|
|
21
|
+
class _EEGMinerFeatures(nn.Module):
|
|
22
|
+
def __init__(self, method, n_chans, n_filters, n_times):
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.method = method
|
|
25
|
+
self.n_chans = n_chans
|
|
26
|
+
self.n_filters = n_filters
|
|
27
|
+
self.n_times = n_times
|
|
28
|
+
triu = torch.triu_indices(n_chans, n_chans, 1)
|
|
29
|
+
self.register_buffer("triu_row", triu[0], persistent=False)
|
|
30
|
+
self.register_buffer("triu_col", triu[1], persistent=False)
|
|
31
|
+
|
|
32
|
+
def forward(self, x):
|
|
33
|
+
if self.method == "mag":
|
|
34
|
+
return torch.sqrt((x * x).mean(dim=-1))
|
|
35
|
+
if self.method == "corr":
|
|
36
|
+
x = x.reshape(
|
|
37
|
+
x.shape[0], self.n_chans, self.n_filters, self.n_times
|
|
38
|
+
).transpose(-3, -2)
|
|
39
|
+
x = (x - x.mean(dim=-1, keepdim=True)) / torch.sqrt(
|
|
40
|
+
x.var(dim=-1, keepdim=True) + 1e-6
|
|
41
|
+
)
|
|
42
|
+
x = torch.matmul(x, x.transpose(-2, -1)) / x.shape[-1]
|
|
43
|
+
x = x.permute(0, 2, 3, 1).abs()
|
|
44
|
+
else:
|
|
45
|
+
x = x.transpose(-4, -3)
|
|
46
|
+
x = F.plv_time(x, forward_fourier=False)
|
|
47
|
+
x = x.permute(0, 2, 3, 1)
|
|
48
|
+
return x[:, self.triu_row, self.triu_col, :]
|
|
49
|
+
|
|
50
|
+
|
|
22
51
|
class EEGMiner(EEGModuleMixin, nn.Module):
|
|
23
52
|
r"""EEGMiner from Ludwig et al (2024) [eegminer]_.
|
|
24
53
|
|
|
@@ -168,24 +197,18 @@ class EEGMiner(EEGModuleMixin, nn.Module):
|
|
|
168
197
|
)
|
|
169
198
|
|
|
170
199
|
# Forward method
|
|
200
|
+
self.ensure_dim = nn.Identity()
|
|
171
201
|
if self.method == "mag":
|
|
172
|
-
self.method_forward = self._apply_mag_forward
|
|
173
202
|
self.n_features = self.n_chans * self.n_filters
|
|
174
|
-
self.ensure_dim = nn.Identity()
|
|
175
203
|
elif self.method == "corr":
|
|
176
|
-
self.method_forward = partial(
|
|
177
|
-
self._apply_corr_forward,
|
|
178
|
-
n_chans=self.n_chans,
|
|
179
|
-
n_filters=self.n_filters,
|
|
180
|
-
n_times=self.n_times,
|
|
181
|
-
)
|
|
182
204
|
self.n_features = self.n_filters * self.n_chans * (self.n_chans - 1) // 2
|
|
183
|
-
self.ensure_dim = nn.Identity()
|
|
184
205
|
elif self.method == "plv":
|
|
185
|
-
self.
|
|
186
|
-
self.ensure_dim = Rearrange("... d -> ... 1 d")
|
|
206
|
+
self.ensure_dim = nn.Unflatten(-1, (1, self.n_times))
|
|
187
207
|
self.n_features = (self.n_filters * self.n_chans * (self.n_chans - 1)) // 2
|
|
188
208
|
|
|
209
|
+
self.feature_layer = _EEGMinerFeatures(
|
|
210
|
+
self.method, self.n_chans, self.n_filters, self.n_times
|
|
211
|
+
)
|
|
189
212
|
self.flatten_layer = nn.Flatten()
|
|
190
213
|
# Classifier
|
|
191
214
|
self.batch_layer = nn.BatchNorm1d(self.n_features, affine=False)
|
|
@@ -195,64 +218,17 @@ class EEGMiner(EEGModuleMixin, nn.Module):
|
|
|
195
218
|
@_disable_batch_norm_training_if_batch_size_one
|
|
196
219
|
def forward(self, x):
|
|
197
220
|
"""x: (batch, electrodes, time)"""
|
|
198
|
-
batch = x.shape[0]
|
|
199
221
|
x = self.ensure_dim(x)
|
|
200
222
|
# Apply Gaussian filters in frequency domain
|
|
201
223
|
# x -> (batch, electrodes * filters, time)
|
|
202
224
|
x = self.filter(x)
|
|
203
225
|
|
|
204
|
-
x = self.
|
|
226
|
+
x = self.feature_layer(x)
|
|
205
227
|
# Classifier
|
|
206
228
|
# Note that the order of dimensions before flattening the feature vector is important
|
|
207
229
|
# for attributing feature weights during interpretation.
|
|
208
|
-
x = x.reshape(
|
|
230
|
+
x = x.reshape(x.shape[0], self.n_features)
|
|
209
231
|
x = self.batch_layer(x)
|
|
210
232
|
x = self.final_layer(x)
|
|
211
233
|
|
|
212
234
|
return x
|
|
213
|
-
|
|
214
|
-
@staticmethod
|
|
215
|
-
def _apply_mag_forward(x, batch=None):
|
|
216
|
-
# Signal magnitude
|
|
217
|
-
x = x * x
|
|
218
|
-
x = x.mean(dim=-1)
|
|
219
|
-
x = torch.sqrt(x)
|
|
220
|
-
return x
|
|
221
|
-
|
|
222
|
-
@staticmethod
|
|
223
|
-
def _apply_corr_forward(
|
|
224
|
-
x, batch, n_chans, n_filters, n_times, epilson: float = 1e-6
|
|
225
|
-
):
|
|
226
|
-
x = x.reshape(batch, n_chans, n_filters, n_times).transpose(-3, -2)
|
|
227
|
-
x = (x - x.mean(dim=-1, keepdim=True)) / torch.sqrt(
|
|
228
|
-
x.var(dim=-1, keepdim=True) + epilson
|
|
229
|
-
)
|
|
230
|
-
x = torch.matmul(x, x.transpose(-2, -1)) / x.shape[-1]
|
|
231
|
-
# Original tensor shape: [batch, n_filters, chans, chans]
|
|
232
|
-
x = x.permute(0, 2, 3, 1)
|
|
233
|
-
# New tensor shape: [batch, chans, chans, n_filters]
|
|
234
|
-
# move filter channels to the end
|
|
235
|
-
x = x.abs()
|
|
236
|
-
|
|
237
|
-
# Get upper triu of symmetric connectivity matrix
|
|
238
|
-
triu = torch.triu_indices(n_chans, n_chans, 1)
|
|
239
|
-
x = x[:, triu[0], triu[1], :]
|
|
240
|
-
|
|
241
|
-
return x
|
|
242
|
-
|
|
243
|
-
@staticmethod
|
|
244
|
-
def _apply_plv(x, n_chans, batch=None):
|
|
245
|
-
# Compute PLV connectivity
|
|
246
|
-
# x -> (batch, electrodes, electrodes, filters)
|
|
247
|
-
x = x.transpose(-4, -3) # swap electrodes and filters
|
|
248
|
-
# adjusting to compute the plv
|
|
249
|
-
x = F.plv_time(x, forward_fourier=False)
|
|
250
|
-
# batch, number of filters, connectivity matrix
|
|
251
|
-
# [batch, n_filters, chans, chans]
|
|
252
|
-
x = x.permute(0, 2, 3, 1)
|
|
253
|
-
# [batch, chans, chans, n_filters]
|
|
254
|
-
|
|
255
|
-
# Get upper triu of symmetric connectivity matrix
|
|
256
|
-
triu = torch.triu_indices(n_chans, n_chans, 1)
|
|
257
|
-
x = x[:, triu[0], triu[1], :]
|
|
258
|
-
return x
|