braindecode 1.8.1.dev174295099__tar.gz → 1.8.1.dev174425547__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.dev174295099/braindecode.egg-info → braindecode-1.8.1.dev174425547}/PKG-INFO +1 -1
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/functional/functions.py +19 -8
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/axon.py +42 -8
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/brainmodule.py +13 -1
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegnex.py +6 -2
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegpt.py +12 -8
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/medformer.py +47 -20
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/patchedtransformer.py +3 -7
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/reve.py +3 -2
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/signal_jepa.py +81 -3
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/zuna.py +9 -8
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/attention.py +47 -5
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/dance_modules.py +13 -1
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/layers.py +34 -1
- braindecode-1.8.1.dev174425547/braindecode/version.py +1 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/whats_new.rst +18 -0
- braindecode-1.8.1.dev174295099/braindecode/version.py +0 -1
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/LICENSE.txt +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/MANIFEST.in +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/NOTICE.txt +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/README.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/augmentation/base.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/augmentation/functional.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/classifier.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/_notebook_viewer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/base.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/hub.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/collate.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/datautil/util.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/functional/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/functional/_real_dft.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/barista.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/base.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/bendr.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/biot.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/brainbert.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/brainomni.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/brant.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/codebrain.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/config.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/csbrain.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/dance.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/deep4.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/diver1.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eeg_clip.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eeginception_mi.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegitnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegsym.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/guetschel2026.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/labram.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/luna.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/mapa.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/meta_neuromotor.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/mirepnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/mscformer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/msvtnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/mvpformer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/neuropose.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/neurorvq.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/neurorvq_tokenizer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/popt.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/seizure_transformer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sensingdynamics.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sleepfm.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/steegformer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/summary.csv +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/tcn.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/tfm_tokenizer.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/tmsanet.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/usleep.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/util.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/vemg2pose.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/activation.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/blocks.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/channels.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/filter.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/linear.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/linear_attention.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/popt_modules.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/quantization.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/stats.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/util.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/regressor.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/samplers/base.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/training/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/training/losses.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/training/scoring.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/util.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/activations.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode.egg-info/SOURCES.txt +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/Makefile +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/agent_docs.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/api.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/cite.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/conf.py +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/help.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/index.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/install/install.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/install/install_pip.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/install/install_source.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/models.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/models_table.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/docs/user_guide/channel_strategies.rst +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/pyproject.toml +0 -0
- {braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/setup.cfg +0 -0
{braindecode-1.8.1.dev174295099/braindecode.egg-info → braindecode-1.8.1.dev174425547}/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.dev174425547
|
|
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>
|
|
@@ -91,16 +91,27 @@ def fft_conv1d(
|
|
|
91
91
|
n_fft = 2 * _next_fast_len((n_times + kernel_size) // 2)
|
|
92
92
|
xs = spectral_input(x)
|
|
93
93
|
# conv1d is a cross-correlation: a convolution with the flipped kernel
|
|
94
|
-
x_f = torch.fft.rfft(xs, n=n_fft)
|
|
94
|
+
x_f = torch.fft.rfft(xs, n=n_fft) # (batch, in, freq)
|
|
95
95
|
w_f = torch.fft.rfft(weight.to(xs).flip(-1), n=n_fft).permute(2, 1, 0)
|
|
96
96
|
# Per frequency (batch, in) @ (in, out) in complex numbers, as one real
|
|
97
|
-
# matmul (a complex one is slower on CPU)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
# matmul (a complex one is slower on CPU).
|
|
98
|
+
needs_grad = torch.is_grad_enabled() and (x.requires_grad or weight.requires_grad)
|
|
99
|
+
if needs_grad or x.shape[0] >= 2 * x.shape[1]:
|
|
100
|
+
# [Re x, Im x] @ [[Re w, Im w], [-Im w, Re w]] (rows of i * w): the
|
|
101
|
+
# output columns are the (Re, Im) pairs of the result, read as complex
|
|
102
|
+
# without a copy (and without a costly backward).
|
|
103
|
+
w_mat = torch.view_as_real(torch.stack([w_f, w_f * 1j], 1)).flatten(1, 2)
|
|
104
|
+
y_f = torch.view_as_real(x_f).permute(2, 0, 3, 1).flatten(2) @ w_mat.flatten(2)
|
|
105
|
+
y_f = torch.view_as_complex(y_f.unflatten(-1, [-1, 2]))
|
|
106
|
+
else:
|
|
107
|
+
# Forward only with fewer rows than twice the input channels: the
|
|
108
|
+
# doubled weight costs more than combining [Re x; Im x] @ [Re w, Im w].
|
|
109
|
+
x_f = x_f.permute(2, 0, 1)
|
|
110
|
+
p = torch.cat([x_f.real, x_f.imag], 1) @ torch.view_as_real(w_f).flatten(2)
|
|
111
|
+
p = p.unflatten(1, [2, x.shape[0]]).unflatten(3, [-1, 2])
|
|
112
|
+
y_f = torch.complex(
|
|
113
|
+
p[:, 0, :, :, 0] - p[:, 1, :, :, 1], p[:, 0, :, :, 1] + p[:, 1, :, :, 0]
|
|
114
|
+
)
|
|
104
115
|
y = torch.fft.irfft(y_f.permute(1, 2, 0), n=n_fft)
|
|
105
116
|
# "same" output t is sample t + kernel_size // 2 of the full convolution
|
|
106
117
|
y = y[..., kernel_size // 2 : kernel_size // 2 + n_times]
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/axon.py
RENAMED
|
@@ -14,6 +14,7 @@ from torch import nn
|
|
|
14
14
|
from braindecode.functional import sinusoidal_positional_encoding
|
|
15
15
|
from braindecode.models.base import EEGModuleMixin
|
|
16
16
|
from braindecode.models.util import warn_if_sfreq_differs
|
|
17
|
+
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
17
18
|
from braindecode.modules.channels import _resolve
|
|
18
19
|
|
|
19
20
|
|
|
@@ -462,11 +463,33 @@ class _AxisAttention(nn.Module):
|
|
|
462
463
|
.unbind(0)
|
|
463
464
|
)
|
|
464
465
|
drop = self.att_drop_prob if self.training else 0.0
|
|
465
|
-
attended =
|
|
466
|
+
attended = _scaled_dot_product_attention(
|
|
466
467
|
query, key, value, attn_mask=mask, dropout_p=drop
|
|
467
468
|
)
|
|
468
469
|
return attended.transpose(1, 2).reshape(n_seq, length, -1)
|
|
469
470
|
|
|
471
|
+
def _attend_two_scales(
|
|
472
|
+
self, projected: torch.Tensor, band: torch.Tensor, weights: torch.Tensor
|
|
473
|
+
) -> torch.Tensor:
|
|
474
|
+
"""Banded and full attention from one score matrix, mixed per query.
|
|
475
|
+
|
|
476
|
+
``weights`` (N, L, 2) are the scale-gate weights. Both softmaxes share
|
|
477
|
+
the scores and one product with the values serves the mixture.
|
|
478
|
+
"""
|
|
479
|
+
n_seq, length, _ = projected.shape
|
|
480
|
+
query, key, value = (
|
|
481
|
+
projected.reshape(n_seq, length, 3, self.num_heads, self.head_dim)
|
|
482
|
+
.permute(2, 0, 3, 1, 4)
|
|
483
|
+
.unbind(0)
|
|
484
|
+
)
|
|
485
|
+
scores = torch.matmul(query, key.transpose(-2, -1)) * self.head_dim**-0.5
|
|
486
|
+
weights = weights.reshape(n_seq, 1, length, 2)
|
|
487
|
+
probs = weights[..., 0:1] * scores.masked_fill(~band, float("-inf")).softmax(
|
|
488
|
+
-1
|
|
489
|
+
) + weights[..., 1:2] * scores.softmax(-1)
|
|
490
|
+
attended = torch.matmul(probs, value)
|
|
491
|
+
return attended.transpose(1, 2).reshape(n_seq, length, -1)
|
|
492
|
+
|
|
470
493
|
def forward(self, x: torch.Tensor, band: Optional[torch.Tensor]) -> torch.Tensor:
|
|
471
494
|
batch, n_chans, n_patches, dim = x.shape
|
|
472
495
|
axis_weights = self.axis_gate(x) # (B, C, P, 2)
|
|
@@ -476,15 +499,26 @@ class _AxisAttention(nn.Module):
|
|
|
476
499
|
# the restricted branch is the full one; attend once unless the two
|
|
477
500
|
# branches draw their own attention dropout.
|
|
478
501
|
per_channel = self.temporal_qkv(x.reshape(batch * n_chans, n_patches, dim))
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
502
|
+
dropout = self.training and self.att_drop_prob > 0
|
|
503
|
+
if band is not None and not dropout:
|
|
504
|
+
# The scale-gate weights sum to one, so the two branches share the
|
|
505
|
+
# scores, the product with the values and temporal_out.
|
|
506
|
+
attended = self._attend_two_scales(per_channel, band, scale_weights)
|
|
507
|
+
temporal = self.temporal_out(attended).reshape(
|
|
485
508
|
batch, n_chans, n_patches, dim
|
|
486
509
|
)
|
|
487
|
-
|
|
510
|
+
else:
|
|
511
|
+
restricted = self.temporal_out(self._attend(per_channel, band)).reshape(
|
|
512
|
+
batch, n_chans, n_patches, dim
|
|
513
|
+
)
|
|
514
|
+
full = restricted
|
|
515
|
+
if dropout:
|
|
516
|
+
full = self.temporal_out(self._attend(per_channel, None)).reshape(
|
|
517
|
+
batch, n_chans, n_patches, dim
|
|
518
|
+
)
|
|
519
|
+
temporal = (
|
|
520
|
+
scale_weights[..., 0:1] * restricted + scale_weights[..., 1:2] * full
|
|
521
|
+
)
|
|
488
522
|
|
|
489
523
|
# Spatial path: one sequence of C tokens per time step.
|
|
490
524
|
per_step = self.spatial_qkv(
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/brainmodule.py
RENAMED
|
@@ -20,6 +20,7 @@ from torch.nn import functional as F
|
|
|
20
20
|
from braindecode.models.base import EEGModuleMixin
|
|
21
21
|
from braindecode.models.util import has_valid_locations, positions_from_chs_info
|
|
22
22
|
from braindecode.modules import ChannelMerger
|
|
23
|
+
from braindecode.modules.attention import _hpu_lazy
|
|
23
24
|
from braindecode.modules.layers import SubjectLayers
|
|
24
25
|
|
|
25
26
|
__all__ = ["BrainModule"]
|
|
@@ -712,7 +713,7 @@ class _ConvSequence(nn.Module):
|
|
|
712
713
|
nn.Conv1d(
|
|
713
714
|
chout, 2 * chout, 1 + 2 * glu_context, padding=glu_context
|
|
714
715
|
),
|
|
715
|
-
|
|
716
|
+
_GLU(dim=1),
|
|
716
717
|
)
|
|
717
718
|
)
|
|
718
719
|
else:
|
|
@@ -727,6 +728,17 @@ class _ConvSequence(nn.Module):
|
|
|
727
728
|
return x
|
|
728
729
|
|
|
729
730
|
|
|
731
|
+
class _GLU(nn.GLU):
|
|
732
|
+
"""``nn.GLU`` that Intel Gaudi's lazy mode can compile."""
|
|
733
|
+
|
|
734
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
735
|
+
if not torch.jit.is_scripting() and x.device.type == "hpu" and _hpu_lazy():
|
|
736
|
+
# Its graph compiler fails on ``aten.glu`` after a convolution.
|
|
737
|
+
a, b = x.chunk(2, dim=self.dim)
|
|
738
|
+
return a * torch.sigmoid(b)
|
|
739
|
+
return F.glu(x, self.dim)
|
|
740
|
+
|
|
741
|
+
|
|
730
742
|
def _pad_multiple(x: torch.Tensor, base: int) -> torch.Tensor:
|
|
731
743
|
"""Pad tensor to be a multiple of base."""
|
|
732
744
|
length = x.shape[-1]
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegnex.py
RENAMED
|
@@ -358,10 +358,14 @@ class EEGNeX(EEGModuleMixin, nn.Module):
|
|
|
358
358
|
class _TemporalConv(nn.Conv2d):
|
|
359
359
|
"""``(1, k)`` Conv2d with "same" output length; on CPU and HPU, through
|
|
360
360
|
:func:`~braindecode.functional.fft_conv1d` where
|
|
361
|
-
:func:`~braindecode.functional.prefer_fft_conv` expects it to be faster.
|
|
361
|
+
:func:`~braindecode.functional.prefer_fft_conv` expects it to be faster.
|
|
362
|
+
On HPU only when gradients are computed: the FFT pays off in the
|
|
363
|
+
backward, and Gaudi's direct forward alone is cheaper."""
|
|
362
364
|
|
|
363
365
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
364
|
-
if not prefer_fft_conv(x, self.kernel_size[1])
|
|
366
|
+
if not prefer_fft_conv(x, self.kernel_size[1]) or (
|
|
367
|
+
x.device.type == "hpu" and not torch.is_grad_enabled()
|
|
368
|
+
):
|
|
365
369
|
return self._conv_forward(x, self.weight, self.bias)
|
|
366
370
|
b, _, h, t = x.shape # the conv1d of each of the b * h rows
|
|
367
371
|
x = x.transpose(1, 2).reshape(b * h, -1, t)
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/eegpt.py
RENAMED
|
@@ -13,6 +13,7 @@ from torch import nn
|
|
|
13
13
|
|
|
14
14
|
from braindecode.models.base import EEGModuleMixin
|
|
15
15
|
from braindecode.modules import DropPath
|
|
16
|
+
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
16
17
|
from braindecode.modules.convolution import Conv1dWithConstraint
|
|
17
18
|
from braindecode.modules.linear import LinearWithConstraint
|
|
18
19
|
from braindecode.util import resolve_montage_name
|
|
@@ -949,14 +950,17 @@ class _Attention(nn.Module):
|
|
|
949
950
|
# 3. Flash Attention
|
|
950
951
|
# Use PyTorch's optimized scaled_dot_product_attention (SDPA)
|
|
951
952
|
# which internally uses FlashAttention or EfficientAttention kernels.
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
953
|
+
dropout_p = self.attn_drop.p if self.training else 0.0
|
|
954
|
+
if self.is_causal:
|
|
955
|
+
y = torch.nn.functional.scaled_dot_product_attention(
|
|
956
|
+
q, k, v, attn_mask=None, dropout_p=dropout_p, is_causal=True
|
|
957
|
+
)
|
|
958
|
+
else:
|
|
959
|
+
# Gaudi's fused kernel also in lazy mode: faster for EEGPT's
|
|
960
|
+
# 23-token sequences except on the shortest inputs (256 samples).
|
|
961
|
+
y = _scaled_dot_product_attention(
|
|
962
|
+
q, k, v, dropout_p=dropout_p, fused_in_lazy=True
|
|
963
|
+
)
|
|
960
964
|
|
|
961
965
|
# Reshape back: (batch, num_heads, seq_len, head_dim) -> (batch, seq_len, embed_dim)
|
|
962
966
|
x = y.transpose(1, 2).flatten(2)
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/medformer.py
RENAMED
|
@@ -342,17 +342,13 @@ class _PositionalEmbedding(nn.Module):
|
|
|
342
342
|
|
|
343
343
|
|
|
344
344
|
class _CrossChannelTokenEmbedding(nn.Module):
|
|
345
|
-
def __init__(
|
|
346
|
-
self, c_in: int, l_patch: int, d_model: int, stride: Optional[int] = None
|
|
347
|
-
):
|
|
345
|
+
def __init__(self, c_in: int, l_patch: int, d_model: int):
|
|
348
346
|
super().__init__()
|
|
349
|
-
if stride is None:
|
|
350
|
-
stride = l_patch
|
|
351
347
|
self.token_conv = nn.Conv2d(
|
|
352
348
|
in_channels=1,
|
|
353
349
|
out_channels=d_model,
|
|
354
350
|
kernel_size=(c_in, l_patch),
|
|
355
|
-
stride=(1,
|
|
351
|
+
stride=(1, l_patch),
|
|
356
352
|
padding=0,
|
|
357
353
|
padding_mode="circular",
|
|
358
354
|
bias=False,
|
|
@@ -364,14 +360,18 @@ class _CrossChannelTokenEmbedding(nn.Module):
|
|
|
364
360
|
)
|
|
365
361
|
|
|
366
362
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
367
|
-
# The kernel spans all c_in rows
|
|
368
|
-
#
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
363
|
+
# The kernel spans all c_in rows and the patches do not overlap, so
|
|
364
|
+
# the conv is one GEMM of its weight with the patch-reshaped input
|
|
365
|
+
# (Gaudi2 rejects conv kernels taller than 256 rows, c_in = n_times).
|
|
366
|
+
l_patch = self.token_conv.kernel_size[1]
|
|
367
|
+
n_patches = x.shape[-1] // l_patch
|
|
368
|
+
x = x[:, 0, :, : n_patches * l_patch].reshape(
|
|
369
|
+
x.shape[0], x.shape[2], n_patches, l_patch
|
|
373
370
|
)
|
|
374
|
-
|
|
371
|
+
x = torch.matmul(
|
|
372
|
+
x.transpose(1, 2).flatten(2), self.token_conv.weight.flatten(1).t()
|
|
373
|
+
)
|
|
374
|
+
return x.transpose(1, 2).unsqueeze(2)
|
|
375
375
|
|
|
376
376
|
|
|
377
377
|
class _ListPatchEmbedding(nn.Module):
|
|
@@ -493,11 +493,32 @@ class _AttentionLayer(nn.Module):
|
|
|
493
493
|
_, key_len, _ = keys.shape
|
|
494
494
|
num_heads = self.num_heads
|
|
495
495
|
|
|
496
|
-
queries
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
496
|
+
if queries is keys and keys is values:
|
|
497
|
+
# Self-attention: one GEMM for the three projections.
|
|
498
|
+
weight = torch.cat(
|
|
499
|
+
[
|
|
500
|
+
self.query_projection.weight,
|
|
501
|
+
self.key_projection.weight,
|
|
502
|
+
self.value_projection.weight,
|
|
503
|
+
]
|
|
504
|
+
)
|
|
505
|
+
bias = torch.cat(
|
|
506
|
+
[
|
|
507
|
+
self.query_projection.bias,
|
|
508
|
+
self.key_projection.bias,
|
|
509
|
+
self.value_projection.bias,
|
|
510
|
+
]
|
|
511
|
+
)
|
|
512
|
+
queries, keys, values = nn.functional.linear(queries, weight, bias).chunk(
|
|
513
|
+
3, dim=-1
|
|
514
|
+
)
|
|
515
|
+
else:
|
|
516
|
+
queries = self.query_projection(queries)
|
|
517
|
+
keys = self.key_projection(keys)
|
|
518
|
+
values = self.value_projection(values)
|
|
519
|
+
queries = queries.view(batch_size, query_len, num_heads, -1) # multi-head
|
|
520
|
+
keys = keys.view(batch_size, key_len, num_heads, -1)
|
|
521
|
+
values = values.view(batch_size, key_len, num_heads, -1)
|
|
501
522
|
|
|
502
523
|
out, attn = self.inner_attention(
|
|
503
524
|
queries, keys, values, attn_mask, tau=tau, delta=delta
|
|
@@ -561,7 +582,13 @@ class _FullAttention(nn.Module):
|
|
|
561
582
|
else:
|
|
562
583
|
scale = self.scale
|
|
563
584
|
|
|
564
|
-
|
|
585
|
+
# (batch, heads, len, dim) operands: both products are batched GEMMs.
|
|
586
|
+
queries, keys, values = (
|
|
587
|
+
queries.transpose(1, 2),
|
|
588
|
+
keys.transpose(1, 2),
|
|
589
|
+
values.transpose(1, 2),
|
|
590
|
+
)
|
|
591
|
+
scores = torch.matmul(queries, keys.transpose(-2, -1))
|
|
565
592
|
|
|
566
593
|
if self.mask_flag:
|
|
567
594
|
if attn_mask is None:
|
|
@@ -577,7 +604,7 @@ class _FullAttention(nn.Module):
|
|
|
577
604
|
attention_weights = self.dropout(
|
|
578
605
|
torch.softmax(scale * scores, dim=-1)
|
|
579
606
|
) # Scaled Dot-Product Attention
|
|
580
|
-
output_values = torch.
|
|
607
|
+
output_values = torch.matmul(attention_weights, values).transpose(1, 2)
|
|
581
608
|
|
|
582
609
|
if self.output_attention:
|
|
583
610
|
return output_values.contiguous(), attention_weights
|
|
@@ -12,6 +12,7 @@ import torch.nn as nn
|
|
|
12
12
|
import torch.nn.functional as F
|
|
13
13
|
|
|
14
14
|
from braindecode.models.base import EEGModuleMixin
|
|
15
|
+
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class PBT(EEGModuleMixin, nn.Module):
|
|
@@ -411,13 +412,8 @@ class _MHSA(nn.Module):
|
|
|
411
412
|
k = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
|
|
412
413
|
v = v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
|
|
413
414
|
|
|
414
|
-
y =
|
|
415
|
-
q,
|
|
416
|
-
k,
|
|
417
|
-
v,
|
|
418
|
-
attn_mask=None,
|
|
419
|
-
dropout_p=self.drop_prob if self.training else 0.0,
|
|
420
|
-
is_causal=False,
|
|
415
|
+
y = _scaled_dot_product_attention(
|
|
416
|
+
q, k, v, dropout_p=self.drop_prob if self.training else 0.0
|
|
421
417
|
)
|
|
422
418
|
|
|
423
419
|
# re-assemble heads -> (B, T, C)
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/reve.py
RENAMED
|
@@ -15,10 +15,10 @@ import torch
|
|
|
15
15
|
import torch.nn.functional as F
|
|
16
16
|
from mne.datasets.utils import _get_path
|
|
17
17
|
from torch import nn
|
|
18
|
-
from torch.nn import RMSNorm
|
|
19
18
|
|
|
20
19
|
from braindecode.models.base import EEGModuleMixin
|
|
21
20
|
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
21
|
+
from braindecode.modules.layers import RMSNorm
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
@@ -565,7 +565,8 @@ class Attention(nn.Module):
|
|
|
565
565
|
q = q.unflatten(-1, (self.heads, -1)).transpose(1, 2)
|
|
566
566
|
k = k.unflatten(-1, (self.heads, -1)).transpose(1, 2)
|
|
567
567
|
v = v.unflatten(-1, (self.heads, -1)).transpose(1, 2)
|
|
568
|
-
|
|
568
|
+
# ~700-token sequences: the fused kernel also beats lazy mode's graph.
|
|
569
|
+
out = _scaled_dot_product_attention(q, k, v, fused_in_lazy=True)
|
|
569
570
|
return self.to_out(out.transpose(1, 2).flatten(2))
|
|
570
571
|
|
|
571
572
|
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/signal_jepa.py
RENAMED
|
@@ -13,6 +13,10 @@ from einops.layers.torch import Rearrange
|
|
|
13
13
|
from torch import nn
|
|
14
14
|
|
|
15
15
|
from braindecode.models.base import EEGModuleMixin
|
|
16
|
+
from braindecode.modules.attention import (
|
|
17
|
+
_hpu_lazy,
|
|
18
|
+
_scaled_dot_product_attention,
|
|
19
|
+
)
|
|
16
20
|
|
|
17
21
|
_DEFAULT_CONV_LAYER_SPEC = ( # downsampling: 128Hz -> 1Hz, receptive field 1.1875s, stride 1s
|
|
18
22
|
(8, 32, 8),
|
|
@@ -329,6 +333,56 @@ class _BaseSignalJEPA(EEGModuleMixin, nn.Module):
|
|
|
329
333
|
batch_first=True,
|
|
330
334
|
)
|
|
331
335
|
|
|
336
|
+
def _encode(self, x: torch.Tensor) -> torch.Tensor:
|
|
337
|
+
if self.training and not torch.jit.is_scripting():
|
|
338
|
+
return self._encode_train(x)
|
|
339
|
+
return self.transformer.encoder(x) # type: ignore
|
|
340
|
+
|
|
341
|
+
@torch.jit.unused
|
|
342
|
+
def _encode_train(self, x: torch.Tensor) -> torch.Tensor:
|
|
343
|
+
"""``self.transformer.encoder(x)`` in training, without SDPA's ``_safe_softmax``.
|
|
344
|
+
|
|
345
|
+
With dropout, torch's attention takes SDPA's math path, whose
|
|
346
|
+
``_safe_softmax`` adds four passes over the (batch, heads, tokens,
|
|
347
|
+
tokens) scores to zero the rows a mask hides; there is no mask here.
|
|
348
|
+
Ops, layouts and dropout draws are otherwise torch's: outputs and
|
|
349
|
+
gradients are bit-identical on CPU.
|
|
350
|
+
"""
|
|
351
|
+
encoder = self.transformer.encoder # type: ignore
|
|
352
|
+
for layer in encoder.layers:
|
|
353
|
+
x = layer.norm1(x + layer.dropout1(_self_attention(layer.self_attn, x)))
|
|
354
|
+
h = layer.dropout(layer.activation(layer.linear1(x)))
|
|
355
|
+
x = layer.norm2(x + layer.dropout2(layer.linear2(h)))
|
|
356
|
+
if encoder.norm is not None:
|
|
357
|
+
x = encoder.norm(x)
|
|
358
|
+
return x
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _self_attention(mha: nn.MultiheadAttention, x: torch.Tensor) -> torch.Tensor:
|
|
362
|
+
"""``mha(x, x, x, need_weights=False)[0]`` (batch first, no mask) as
|
|
363
|
+
``F.multi_head_attention_forward`` runs it, with the attention itself
|
|
364
|
+
through braindecode's SDPA helper (no ``_safe_softmax`` pass)."""
|
|
365
|
+
x = x.transpose(1, 0)
|
|
366
|
+
n_tokens, batch_size, emb_dim = x.shape
|
|
367
|
+
n_heads = mha.num_heads
|
|
368
|
+
head_dim = emb_dim // n_heads
|
|
369
|
+
qkv = nn.functional.linear(x, mha.in_proj_weight, mha.in_proj_bias)
|
|
370
|
+
qkv = qkv.unflatten(-1, (3, emb_dim)).unsqueeze(0).transpose(0, -2)
|
|
371
|
+
qkv = qkv.squeeze(-2).contiguous()
|
|
372
|
+
q, k, v = (
|
|
373
|
+
t.view(n_tokens, batch_size * n_heads, head_dim)
|
|
374
|
+
.transpose(0, 1)
|
|
375
|
+
.view(batch_size, n_heads, n_tokens, head_dim)
|
|
376
|
+
for t in (qkv[0], qkv[1], qkv[2])
|
|
377
|
+
)
|
|
378
|
+
out = _scaled_dot_product_attention(
|
|
379
|
+
q, k, v, dropout_p=mha.dropout if mha.training else 0.0
|
|
380
|
+
)
|
|
381
|
+
out = out.permute(2, 0, 1, 3).contiguous()
|
|
382
|
+
out = out.view(batch_size * n_tokens, emb_dim)
|
|
383
|
+
out = nn.functional.linear(out, mha.out_proj.weight, mha.out_proj.bias)
|
|
384
|
+
return out.view(n_tokens, batch_size, emb_dim).transpose(1, 0)
|
|
385
|
+
|
|
332
386
|
|
|
333
387
|
class SignalJEPA(_BaseSignalJEPA):
|
|
334
388
|
r"""Architecture introduced in signal-JEPA for self-supervised pre-training, Guetschel, P et al (2024) [1]_
|
|
@@ -490,7 +544,7 @@ class SignalJEPA(_BaseSignalJEPA):
|
|
|
490
544
|
local_features = self.feature_encoder(X) # type: ignore
|
|
491
545
|
pos_encoding = self.pos_encoder(local_features) # type: ignore
|
|
492
546
|
local_features += pos_encoding # type: ignore
|
|
493
|
-
contextual_features = self.
|
|
547
|
+
contextual_features = self._encode(local_features) # type: ignore
|
|
494
548
|
if return_features:
|
|
495
549
|
out: Dict[str, Optional[torch.Tensor]] = {
|
|
496
550
|
"features": contextual_features,
|
|
@@ -677,7 +731,7 @@ class SignalJEPA_Contextual(_BaseSignalJEPA):
|
|
|
677
731
|
local_features = self.feature_encoder(X) # type: ignore
|
|
678
732
|
pos_encoding = self.pos_encoder(local_features) # type: ignore
|
|
679
733
|
local_features += pos_encoding # type: ignore
|
|
680
|
-
contextual_features = self.
|
|
734
|
+
contextual_features = self._encode(local_features) # type: ignore
|
|
681
735
|
if return_features:
|
|
682
736
|
out: Dict[str, Optional[torch.Tensor]] = {
|
|
683
737
|
"features": contextual_features,
|
|
@@ -1351,7 +1405,7 @@ class _ConvFeatureEncoder(nn.Sequential):
|
|
|
1351
1405
|
return nn.Sequential(
|
|
1352
1406
|
conv,
|
|
1353
1407
|
nn.Dropout(p=drop_prob),
|
|
1354
|
-
|
|
1408
|
+
_GroupNorm(output_channels, output_channels, affine=True),
|
|
1355
1409
|
activation(),
|
|
1356
1410
|
)
|
|
1357
1411
|
else:
|
|
@@ -1361,6 +1415,30 @@ class _ConvFeatureEncoder(nn.Sequential):
|
|
|
1361
1415
|
return _n_times_out(self.conv_layers_spec, n_times)
|
|
1362
1416
|
|
|
1363
1417
|
|
|
1418
|
+
class _GroupNorm(nn.GroupNorm):
|
|
1419
|
+
"""``nn.GroupNorm``; in HPU eager mode written out with mean and variance.
|
|
1420
|
+
|
|
1421
|
+
In HPU eager mode the backward of torch's group norm compiles new graphs at
|
|
1422
|
+
every call (about 0.2 s per call on the feature encoder's first block);
|
|
1423
|
+
the same normalisation from mean/var ops runs from cached kernels.
|
|
1424
|
+
Elsewhere (CPU, CUDA, HPU lazy) this is ``F.group_norm``.
|
|
1425
|
+
"""
|
|
1426
|
+
|
|
1427
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
1428
|
+
if not torch.jit.is_scripting() and x.device.type == "hpu" and not _hpu_lazy():
|
|
1429
|
+
xg = x.reshape(x.shape[0], self.num_groups, -1)
|
|
1430
|
+
mean = xg.mean(-1, keepdim=True)
|
|
1431
|
+
var = xg.var(-1, unbiased=False, keepdim=True)
|
|
1432
|
+
x = ((xg - mean) * torch.rsqrt(var + self.eps)).reshape(x.shape)
|
|
1433
|
+
if self.affine:
|
|
1434
|
+
shape = (1, -1) + (1,) * (x.dim() - 2)
|
|
1435
|
+
x = x * self.weight.reshape(shape) + self.bias.reshape(shape)
|
|
1436
|
+
return x
|
|
1437
|
+
return nn.functional.group_norm(
|
|
1438
|
+
x, self.num_groups, self.weight, self.bias, self.eps
|
|
1439
|
+
)
|
|
1440
|
+
|
|
1441
|
+
|
|
1364
1442
|
class _ChannelEmbedding(nn.Embedding):
|
|
1365
1443
|
r"""Embedding layer for EEG channels.
|
|
1366
1444
|
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/models/zuna.py
RENAMED
|
@@ -12,12 +12,12 @@ import torch
|
|
|
12
12
|
from einops import rearrange
|
|
13
13
|
from einops.layers.torch import Rearrange
|
|
14
14
|
from torch import nn
|
|
15
|
-
from torch.nn import RMSNorm
|
|
16
15
|
|
|
17
16
|
from braindecode.models.base import EEGModuleMixin
|
|
18
17
|
from braindecode.models.util import extract_channel_locations_from_chs_info
|
|
19
18
|
from braindecode.modules import PatchTokenizer
|
|
20
19
|
from braindecode.modules.attention import _scaled_dot_product_attention
|
|
20
|
+
from braindecode.modules.layers import RMSNorm
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ZUNA(EEGModuleMixin, nn.Module, license="apache-2.0"):
|
|
@@ -404,18 +404,16 @@ class _RotaryPositionEmbedding(nn.Module):
|
|
|
404
404
|
|
|
405
405
|
batch_size, sequence_length, num_heads, head_dim = query.shape
|
|
406
406
|
work = torch.promote_types(query.dtype, torch.float32)
|
|
407
|
+
# Each (real, imaginary) pair (a, b) rotates to (-b, a): swap the pair
|
|
408
|
+
# here; the sign is in ``rotary_sine`` (see _ZUNAEncoder).
|
|
407
409
|
query_pairs = query.to(work).reshape(
|
|
408
410
|
batch_size, sequence_length, num_heads, head_dim // 2, 2
|
|
409
411
|
)
|
|
410
412
|
key_pairs = key.to(work).reshape(
|
|
411
413
|
batch_size, sequence_length, num_heads, head_dim // 2, 2
|
|
412
414
|
)
|
|
413
|
-
rotated_query = self.merge_rotary_pairs(
|
|
414
|
-
|
|
415
|
-
)
|
|
416
|
-
rotated_key = self.merge_rotary_pairs(
|
|
417
|
-
torch.stack((-key_pairs[..., 1], key_pairs[..., 0]), dim=-1)
|
|
418
|
-
)
|
|
415
|
+
rotated_query = self.merge_rotary_pairs(query_pairs.flip(-1))
|
|
416
|
+
rotated_key = self.merge_rotary_pairs(key_pairs.flip(-1))
|
|
419
417
|
|
|
420
418
|
query = (query.to(work) * rotary_cosine + rotated_query * rotary_sine).type_as(
|
|
421
419
|
query
|
|
@@ -607,9 +605,12 @@ class _ZUNAEncoder(nn.Module):
|
|
|
607
605
|
rotary_frequencies.cos(),
|
|
608
606
|
persistent=False,
|
|
609
607
|
)
|
|
608
|
+
# Sine with the pair rotation's sign folded in: -sin on the real, +sin
|
|
609
|
+
# on the imaginary component of every pair (exact, saves a negation).
|
|
610
|
+
pair_sign = torch.tensor([-1.0, 1.0]).repeat(rotary_frequencies.shape[-1] // 2)
|
|
610
611
|
self.register_buffer(
|
|
611
612
|
"rotary_sine",
|
|
612
|
-
rotary_frequencies.sin(),
|
|
613
|
+
rotary_frequencies.sin() * pair_sign,
|
|
613
614
|
persistent=False,
|
|
614
615
|
)
|
|
615
616
|
|
{braindecode-1.8.1.dev174295099 → braindecode-1.8.1.dev174425547}/braindecode/modules/attention.py
RENAMED
|
@@ -836,31 +836,61 @@ class CATLite(nn.Module):
|
|
|
836
836
|
|
|
837
837
|
@torch.jit.unused
|
|
838
838
|
def _hpu_eager_fused_sdpa(
|
|
839
|
-
query: Tensor,
|
|
839
|
+
query: Tensor,
|
|
840
|
+
key: Tensor,
|
|
841
|
+
value: Tensor,
|
|
842
|
+
scale: Optional[float] = None,
|
|
843
|
+
fused_in_lazy: bool = False,
|
|
840
844
|
) -> Optional[Tensor]:
|
|
841
|
-
# Lazy mode: None, the graph compiler already fuses torch's SDPA there
|
|
845
|
+
# Lazy mode: None, the graph compiler already fuses torch's SDPA there
|
|
846
|
+
# (unless the caller measured the fused kernel faster for its shapes).
|
|
842
847
|
from habana_frameworks.torch.utils.internal import is_lazy
|
|
843
848
|
|
|
844
|
-
if is_lazy():
|
|
849
|
+
if is_lazy() and not fused_in_lazy:
|
|
845
850
|
return None
|
|
846
851
|
from habana_frameworks.torch.hpex.kernels import FusedSDPA
|
|
847
852
|
|
|
853
|
+
# Strided q/k/v views (heads split off a projection) give wrong outputs and
|
|
854
|
+
# gradients at some shapes; contiguous copies give the right result.
|
|
855
|
+
query, key, value = query.contiguous(), key.contiguous(), value.contiguous()
|
|
848
856
|
return FusedSDPA.apply(query, key, value, None, 0.0, False, scale, "None", False)
|
|
849
857
|
|
|
850
858
|
|
|
859
|
+
@torch.jit.unused
|
|
860
|
+
def _hpu_lazy() -> bool:
|
|
861
|
+
from habana_frameworks.torch.utils.internal import is_lazy
|
|
862
|
+
|
|
863
|
+
return is_lazy()
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def _sdpa_math_dropout(
|
|
867
|
+
query: Tensor, key: Tensor, value: Tensor, dropout_p: float
|
|
868
|
+
) -> Tensor:
|
|
869
|
+
# torch's math path for SDPA with dropout, op for op, without the pass of
|
|
870
|
+
# ``_safe_softmax`` that zeroes fully masked rows (none without a mask):
|
|
871
|
+
# same values, same dropout draws.
|
|
872
|
+
scale = math.sqrt(1.0 / math.sqrt(query.size(-1)))
|
|
873
|
+
attn = torch.matmul(query * scale, key.transpose(-2, -1) * scale)
|
|
874
|
+
attn = F.dropout(attn.softmax(-1), dropout_p, True)
|
|
875
|
+
return torch.matmul(attn, value)
|
|
876
|
+
|
|
877
|
+
|
|
851
878
|
def _scaled_dot_product_attention(
|
|
852
879
|
query: Tensor,
|
|
853
880
|
key: Tensor,
|
|
854
881
|
value: Tensor,
|
|
855
882
|
attn_mask: Optional[Tensor] = None,
|
|
856
883
|
dropout_p: float = 0.0,
|
|
884
|
+
fused_in_lazy: bool = False,
|
|
857
885
|
) -> Tensor:
|
|
858
886
|
"""``F.scaled_dot_product_attention``; Gaudi's fused kernel on HPU.
|
|
859
887
|
|
|
860
888
|
In HPU eager mode, torch's SDPA runs op by op (the full
|
|
861
889
|
``(batch, heads, seq, seq)`` scores in device memory), 2-3.5x slower than
|
|
862
890
|
the bridge's fused kernel on long sequences. The fused kernel is used
|
|
863
|
-
without a mask or dropout (its dropout would draw other masks).
|
|
891
|
+
without a mask or dropout (its dropout would draw other masks). In lazy
|
|
892
|
+
mode only with ``fused_in_lazy=True``: the graph compiler fuses torch's
|
|
893
|
+
SDPA too, faster on long sequences, slower on short ones.
|
|
864
894
|
"""
|
|
865
895
|
if (
|
|
866
896
|
not torch.jit.is_scripting()
|
|
@@ -869,9 +899,21 @@ def _scaled_dot_product_attention(
|
|
|
869
899
|
and dropout_p == 0.0
|
|
870
900
|
and (query.dtype == torch.float32 or query.dtype == torch.bfloat16)
|
|
871
901
|
):
|
|
872
|
-
out = _hpu_eager_fused_sdpa(query, key, value)
|
|
902
|
+
out = _hpu_eager_fused_sdpa(query, key, value, fused_in_lazy=fused_in_lazy)
|
|
873
903
|
if out is not None:
|
|
874
904
|
return out
|
|
905
|
+
if (
|
|
906
|
+
not torch.jit.is_scripting()
|
|
907
|
+
and dropout_p > 0.0
|
|
908
|
+
and attn_mask is None
|
|
909
|
+
and (query.dtype == torch.float32 or query.dtype == torch.float64)
|
|
910
|
+
and (
|
|
911
|
+
query.device.type == "cpu"
|
|
912
|
+
or (query.device.type == "hpu" and not _hpu_lazy())
|
|
913
|
+
)
|
|
914
|
+
):
|
|
915
|
+
# CPU and HPU eager run SDPA with dropout through the math path.
|
|
916
|
+
return _sdpa_math_dropout(query, key, value, dropout_p)
|
|
875
917
|
return F.scaled_dot_product_attention(
|
|
876
918
|
query, key, value, attn_mask=attn_mask, dropout_p=dropout_p
|
|
877
919
|
)
|
|
@@ -28,7 +28,19 @@ class _ConvBlock(nn.Module):
|
|
|
28
28
|
self.act = None if last else activation()
|
|
29
29
|
|
|
30
30
|
def forward(self, x):
|
|
31
|
-
|
|
31
|
+
# Taps more than (T - 1) // dilation steps off-centre only ever read the
|
|
32
|
+
# zero padding: drop them (the same sum without its zero terms, fewer
|
|
33
|
+
# multiply-adds at long dilations).
|
|
34
|
+
d = self.conv.dilation[0]
|
|
35
|
+
half = self.conv.kernel_size[0] // 2
|
|
36
|
+
reach = (x.shape[-1] - 1) // d
|
|
37
|
+
if reach < half:
|
|
38
|
+
weight = self.conv.weight[:, :, half - reach : half + reach + 1]
|
|
39
|
+
x = nn.functional.conv1d(
|
|
40
|
+
x, weight, self.conv.bias, padding=reach * d, dilation=d
|
|
41
|
+
)
|
|
42
|
+
else:
|
|
43
|
+
x = self.conv(x)
|
|
32
44
|
if self.act is not None:
|
|
33
45
|
x = self.act(x)
|
|
34
46
|
return x
|