braindecode 1.6.1.dev1065__tar.gz → 1.6.1.dev1073__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.6.1.dev1065/braindecode.egg-info → braindecode-1.6.1.dev1073}/PKG-INFO +1 -1
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/augmentation/base.py +67 -19
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/augmentation/functional.py +44 -30
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/hub.py +41 -1
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/__init__.py +2 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegitnet.py +33 -12
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegsym.py +9 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/msvtnet.py +7 -3
- braindecode-1.6.1.dev1073/braindecode/models/steegformer.py +660 -0
- braindecode-1.6.1.dev1073/braindecode/models/summary.csv +61 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/util.py +1 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/blocks.py +100 -25
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/filter.py +8 -5
- braindecode-1.6.1.dev1073/braindecode/version.py +1 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073/braindecode.egg-info}/PKG-INFO +1 -1
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode.egg-info/SOURCES.txt +1 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/api.rst +3 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/models_table.rst +4 -2
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/whats_new.rst +45 -0
- braindecode-1.6.1.dev1065/braindecode/models/summary.csv +0 -60
- braindecode-1.6.1.dev1065/braindecode/version.py +0 -1
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/LICENSE.txt +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/MANIFEST.in +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/NOTICE.txt +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/README.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/augmentation/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/augmentation/transforms.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/classifier.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/base.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bbci.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bcicomp.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/datasets.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/format.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/hub_format.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/hub_io.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/hub_validation.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/bids/iterable.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/chb_mit.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/mne.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/moabb.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/nmt.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/registry.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/siena.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/sleep_physionet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/tuh.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/utils.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datasets/xy.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datautil/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datautil/channel_utils.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datautil/hub_formats.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datautil/serialization.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/datautil/util.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/eegneuralnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/functional/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/functional/functions.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/functional/initialization.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/atcnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/attentionbasenet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/attn_sleep.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/base.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/bendr.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/biot.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/brainmodule.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/cbramod.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/codebrain.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/config.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/contrawr.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/ctnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/deep4.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/deepsleepnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/dgcnn.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegconformer.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegdino.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eeginception_erp.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eeginception_mi.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegminer.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegnex.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegpt.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegsimpleconv.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/eegtcnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/emg2qwerty.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/fbcnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/fblightconvnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/fbmsnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/hybrid.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/ifnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/interpolated.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/labram.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/luna.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/medformer.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/meta_neuromotor.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/patchedtransformer.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/reve.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/sccnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/shallow_fbcsp.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/signal_jepa.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/sinc_shallow.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/sparcnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/sstdpn.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/syncnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/tcformer.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/tcn.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/tidnet.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/tsinception.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/models/usleep.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/activation.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/attention.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/convolution.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/interpolation.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/layers.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/linear.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/parametrization.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/stats.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/util.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/modules/wrapper.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/preprocessing/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/preprocessing/mne_preprocess.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/preprocessing/preprocess.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/preprocessing/util.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/preprocessing/windowers.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/regressor.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/samplers/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/samplers/base.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/samplers/ssl.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/training/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/training/callbacks.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/training/losses.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/training/scoring.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/util.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/__init__.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/attribution.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/confusion_matrices.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/frequency.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/metrics.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/sanity.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/visualization/topology.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode.egg-info/dependency_links.txt +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode.egg-info/requires.txt +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode.egg-info/top_level.txt +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/Makefile +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/_templates/autosummary/class.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/_templates/autosummary/function.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/cite.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/conf.py +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/help.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/index.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/install/install.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/install/install_pip.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/install/install_source.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/attention.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/channel.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/convolution.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/filterbank.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/gnn.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/interpretable.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/lbm.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/recurrent.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/categorization/spd.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/models.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/models_categorization.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/models/models_visualization.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/docs/sg_execution_times.rst +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/pyproject.toml +0 -0
- {braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braindecode
|
|
3
|
-
Version: 1.6.1.
|
|
3
|
+
Version: 1.6.1.dev1073
|
|
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>
|
|
@@ -105,7 +105,15 @@ class Transform(torch.nn.Module):
|
|
|
105
105
|
mask = self._get_mask(out_X.shape[0], out_X.device)
|
|
106
106
|
num_valid = mask.sum().long()
|
|
107
107
|
|
|
108
|
-
if num_valid
|
|
108
|
+
if num_valid == out_X.shape[0]:
|
|
109
|
+
# Fast path: every sample is augmented (e.g. ``probability=1.0``,
|
|
110
|
+
# the common case). Operate on the whole batch and skip the boolean
|
|
111
|
+
# gather/scatter (``out_X[mask]`` then ``out_X[mask] = ...``), which
|
|
112
|
+
# would otherwise copy the batch twice.
|
|
113
|
+
out_X, out_y = self.operation(
|
|
114
|
+
out_X, out_y, **self.get_augmentation_params(out_X, out_y)
|
|
115
|
+
)
|
|
116
|
+
elif num_valid > 0:
|
|
109
117
|
# Uses the mask to define the output
|
|
110
118
|
out_X[mask, ...], tr_y = self.operation(
|
|
111
119
|
out_X[mask, ...],
|
|
@@ -168,23 +176,47 @@ class Compose(Transform):
|
|
|
168
176
|
return X, y
|
|
169
177
|
|
|
170
178
|
|
|
171
|
-
|
|
172
|
-
"""
|
|
173
|
-
|
|
174
|
-
with device control.
|
|
175
|
-
"""
|
|
179
|
+
class _AugmentationCollate:
|
|
180
|
+
"""Collate that applies a transform to each batch, with optional expansion.
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
A picklable callable (so it works with ``num_workers > 0``, unlike a
|
|
183
|
+
closure) that collates the batch once and then either augments it in place
|
|
184
|
+
or grows it.
|
|
180
185
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
Parameters
|
|
187
|
+
----------
|
|
188
|
+
transform : Transform
|
|
189
|
+
Transform applied to the collated ``(X, y)``.
|
|
190
|
+
device : str | torch.device | None, optional
|
|
191
|
+
Device the batch is moved to before transforming. Defaults to None.
|
|
192
|
+
n_augmentation : int, optional
|
|
193
|
+
``0`` (default) applies the transform in place (batch size unchanged).
|
|
194
|
+
``> 0`` keeps the clean originals and appends ``n_augmentation``
|
|
195
|
+
independently transformed copies, returning ``(X, y)`` of
|
|
196
|
+
``(1 + n_augmentation)`` times the original size.
|
|
197
|
+
"""
|
|
186
198
|
|
|
187
|
-
|
|
199
|
+
def __init__(self, transform, device=None, n_augmentation=0):
|
|
200
|
+
self.transform = transform
|
|
201
|
+
self.device = device
|
|
202
|
+
self.n_augmentation = n_augmentation
|
|
203
|
+
|
|
204
|
+
def __call__(self, batch):
|
|
205
|
+
collated = default_collate(batch)
|
|
206
|
+
X, y = collated[:2]
|
|
207
|
+
if self.device is not None:
|
|
208
|
+
X = X.to(self.device)
|
|
209
|
+
y = y.to(self.device)
|
|
210
|
+
if self.n_augmentation == 0:
|
|
211
|
+
return (*self.transform(X, y), *collated[2:])
|
|
212
|
+
# Fixed expansion: keep the clean originals, then append
|
|
213
|
+
# ``n_augmentation`` independently transformed copies.
|
|
214
|
+
xs, ys = [X], [y]
|
|
215
|
+
for _ in range(self.n_augmentation):
|
|
216
|
+
aug_X, aug_y = self.transform(X, y)
|
|
217
|
+
xs.append(aug_X)
|
|
218
|
+
ys.append(aug_y)
|
|
219
|
+
return torch.cat(xs), torch.cat(ys)
|
|
188
220
|
|
|
189
221
|
|
|
190
222
|
class AugmentedDataLoader(DataLoader):
|
|
@@ -198,28 +230,44 @@ class AugmentedDataLoader(DataLoader):
|
|
|
198
230
|
Transform or sequence of Transform to be applied to each batch.
|
|
199
231
|
device : str | torch.device | None, optional
|
|
200
232
|
Device on which to transform the data. Defaults to None.
|
|
233
|
+
n_augmentation : int, optional
|
|
234
|
+
Number of augmented copies to append to each batch (fixed expansion).
|
|
235
|
+
When ``0`` (default) the transforms are applied in place and the batch
|
|
236
|
+
keeps its size (stochastic-per-epoch augmentation, backwards-compatible).
|
|
237
|
+
When ``> 0`` each batch becomes ``(1 + n_augmentation)`` times larger:
|
|
238
|
+
the clean originals are kept and ``n_augmentation`` independently
|
|
239
|
+
transformed copies are appended. This expresses augmentations defined as
|
|
240
|
+
a fixed set-expansion (e.g. the EEG-Inception MI 6x training set,
|
|
241
|
+
``n_augmentation=5``). In this mode batches are returned as ``(X, y)``.
|
|
201
242
|
**kwargs : dict, optional
|
|
202
243
|
keyword arguments to pass to standard DataLoader class.
|
|
203
244
|
"""
|
|
204
245
|
|
|
205
|
-
def __init__(
|
|
246
|
+
def __init__(
|
|
247
|
+
self, dataset, transforms=None, device=None, n_augmentation=0, **kwargs
|
|
248
|
+
):
|
|
206
249
|
if "collate_fn" in kwargs:
|
|
207
250
|
raise ValueError(
|
|
208
251
|
"collate_fn cannot be used in this context because it is used "
|
|
209
252
|
"to pass transform"
|
|
210
253
|
)
|
|
254
|
+
if n_augmentation < 0:
|
|
255
|
+
raise ValueError("n_augmentation must be a non-negative integer.")
|
|
211
256
|
if transforms is None or (
|
|
212
257
|
isinstance(transforms, list) and len(transforms) == 0
|
|
213
258
|
):
|
|
214
|
-
|
|
259
|
+
transform = IdentityTransform()
|
|
215
260
|
elif isinstance(transforms, (Transform, nn.Module)):
|
|
216
|
-
|
|
261
|
+
transform = transforms
|
|
217
262
|
elif isinstance(transforms, list):
|
|
218
|
-
|
|
263
|
+
transform = Compose(transforms)
|
|
219
264
|
else:
|
|
220
265
|
raise TypeError(
|
|
221
266
|
"transforms can be either a Transform object "
|
|
222
267
|
"or a list of Transform objects."
|
|
223
268
|
)
|
|
224
269
|
|
|
270
|
+
self.collated_tr = _AugmentationCollate(
|
|
271
|
+
transform, device=device, n_augmentation=n_augmentation
|
|
272
|
+
)
|
|
225
273
|
super().__init__(dataset, collate_fn=self.collated_tr, **kwargs)
|
{braindecode-1.6.1.dev1065 → braindecode-1.6.1.dev1073}/braindecode/augmentation/functional.py
RENAMED
|
@@ -18,7 +18,7 @@ from mne.filter import notch_filter
|
|
|
18
18
|
from scipy.interpolate import Rbf
|
|
19
19
|
from sklearn.utils import check_random_state
|
|
20
20
|
from torch.fft import fft, ifft
|
|
21
|
-
from torch.nn.functional import
|
|
21
|
+
from torch.nn.functional import pad
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def identity(X: torch.Tensor, y: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
|
@@ -253,25 +253,34 @@ def channels_dropout(
|
|
|
253
253
|
return X * mask.unsqueeze(-1), y
|
|
254
254
|
|
|
255
255
|
|
|
256
|
-
def
|
|
256
|
+
def _make_channel_permutation(
|
|
257
257
|
X: torch.Tensor, mask: torch.Tensor, random_state: int | np.random.Generator | None
|
|
258
258
|
) -> torch.Tensor:
|
|
259
|
+
"""Per-sample channel permutation that shuffles only the masked channels.
|
|
260
|
+
|
|
261
|
+
Returns a ``(batch, n_channels)`` index permutation: output channel ``i`` is
|
|
262
|
+
taken from input channel ``perm[b, i]``. Masked channels are permuted among
|
|
263
|
+
their own positions; unmasked channels keep their place.
|
|
264
|
+
|
|
265
|
+
The per-sample ``rng.permutation`` is kept (rather than a vectorized random
|
|
266
|
+
draw) so the output is bit-for-bit identical to the previous one-hot/matmul
|
|
267
|
+
implementation — vectorizing the draw would change seeded results. The win
|
|
268
|
+
is downstream: the caller gathers with this index instead of building
|
|
269
|
+
``(batch, n_channels, n_channels)`` one-hot matrices and a matmul.
|
|
270
|
+
"""
|
|
259
271
|
rng = check_random_state(random_state)
|
|
260
272
|
batch_size, n_channels, _ = X.shape
|
|
261
|
-
hard_mask = mask.round()
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
channels_to_shuffle = torch.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
273
|
+
hard_mask = mask.round().bool()
|
|
274
|
+
channels = torch.arange(n_channels, device=X.device)
|
|
275
|
+
perm = channels.repeat(batch_size, 1)
|
|
276
|
+
for b in range(batch_size):
|
|
277
|
+
channels_to_shuffle = channels[hard_mask[b]]
|
|
278
|
+
perm[b, channels_to_shuffle] = torch.tensor(
|
|
279
|
+
rng.permutation(channels_to_shuffle.cpu()),
|
|
280
|
+
device=X.device,
|
|
281
|
+
dtype=perm.dtype,
|
|
270
282
|
)
|
|
271
|
-
|
|
272
|
-
channels_permutation[channels_to_shuffle] = reordered_channels
|
|
273
|
-
batch_permutations[b, ...] = one_hot(channels_permutation)
|
|
274
|
-
return batch_permutations
|
|
283
|
+
return perm
|
|
275
284
|
|
|
276
285
|
|
|
277
286
|
def channels_shuffle(
|
|
@@ -314,8 +323,11 @@ def channels_shuffle(
|
|
|
314
323
|
if p_shuffle == 0:
|
|
315
324
|
return X, y
|
|
316
325
|
mask = _pick_channels_randomly(X, 1 - p_shuffle, random_state)
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
perm = _make_channel_permutation(X, mask, random_state)
|
|
327
|
+
# Gather channels (output[b, i] = X[b, perm[b, i]]) instead of building
|
|
328
|
+
# one-hot permutation matrices and a (batch, C, C) @ (batch, C, T) matmul.
|
|
329
|
+
perm = perm.unsqueeze(-1).expand(-1, -1, X.shape[-1])
|
|
330
|
+
return torch.gather(X, 1, perm), y
|
|
319
331
|
|
|
320
332
|
|
|
321
333
|
def gaussian_noise(
|
|
@@ -1188,19 +1200,21 @@ def mask_encoding(
|
|
|
1188
1200
|
32 (2024): 875-886.
|
|
1189
1201
|
"""
|
|
1190
1202
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1203
|
+
# All channels are zeroed at the same time positions, so build a single
|
|
1204
|
+
# (batch, n_times) time-mask and broadcast it over channels, vectorized
|
|
1205
|
+
# (no Python loop over batch * n_segments).
|
|
1206
|
+
device = X.device
|
|
1207
|
+
batch_indices = torch.arange(X.shape[0], device=device).repeat_interleave(
|
|
1208
|
+
n_segments * segment_length
|
|
1209
|
+
)
|
|
1210
|
+
seg_indices = (
|
|
1211
|
+
time_start.flatten().to(device)[:, None]
|
|
1212
|
+
+ torch.arange(segment_length, device=device)
|
|
1213
|
+
).flatten()
|
|
1214
|
+
time_mask = torch.zeros(X.shape[0], X.shape[-1], dtype=torch.bool, device=device)
|
|
1215
|
+
time_mask[batch_indices, seg_indices] = True
|
|
1216
|
+
|
|
1217
|
+
return X.masked_fill(time_mask.unsqueeze(1), 0), y
|
|
1204
1218
|
|
|
1205
1219
|
|
|
1206
1220
|
def channels_rereference(
|
|
@@ -327,6 +327,7 @@ class HubDatasetMixin:
|
|
|
327
327
|
|
|
328
328
|
n_recordings = len(self.datasets)
|
|
329
329
|
first_ds = self.datasets[0]
|
|
330
|
+
descriptions = [ds.description for ds in self.datasets]
|
|
330
331
|
|
|
331
332
|
# Get dataset-specific info based on type using registry
|
|
332
333
|
dataset_type = get_dataset_type(first_ds)
|
|
@@ -358,6 +359,9 @@ class HubDatasetMixin:
|
|
|
358
359
|
raise TypeError(f"Unsupported dataset type: {dataset_type}")
|
|
359
360
|
|
|
360
361
|
# Create README content and save
|
|
362
|
+
license_value, license_needs_update = _infer_license_from_descriptions(
|
|
363
|
+
descriptions
|
|
364
|
+
)
|
|
361
365
|
readme_content = _generate_readme_content(
|
|
362
366
|
format_info=format_info,
|
|
363
367
|
n_recordings=n_recordings,
|
|
@@ -366,6 +370,8 @@ class HubDatasetMixin:
|
|
|
366
370
|
data_type=data_type,
|
|
367
371
|
n_windows=n_windows,
|
|
368
372
|
total_duration=total_duration,
|
|
373
|
+
license=license_value,
|
|
374
|
+
license_needs_update=license_needs_update,
|
|
369
375
|
)
|
|
370
376
|
|
|
371
377
|
# Save README
|
|
@@ -967,6 +973,8 @@ def _generate_readme_content(
|
|
|
967
973
|
n_windows: int,
|
|
968
974
|
total_duration: float | None = None,
|
|
969
975
|
format: str = "zarr",
|
|
976
|
+
license: str = "please-specify",
|
|
977
|
+
license_needs_update: bool = True,
|
|
970
978
|
):
|
|
971
979
|
"""Generate README.md content for a dataset uploaded to the Hub.
|
|
972
980
|
|
|
@@ -988,6 +996,10 @@ def _generate_readme_content(
|
|
|
988
996
|
Total duration in seconds across all recordings.
|
|
989
997
|
format : str
|
|
990
998
|
Storage format (default: "zarr").
|
|
999
|
+
license : str
|
|
1000
|
+
Dataset license string to write in YAML front matter.
|
|
1001
|
+
license_needs_update : bool
|
|
1002
|
+
Whether to append a reminder comment next to the license field.
|
|
991
1003
|
|
|
992
1004
|
Returns
|
|
993
1005
|
-------
|
|
@@ -1003,6 +1015,10 @@ def _generate_readme_content(
|
|
|
1003
1015
|
str(timedelta(seconds=int(total_duration))) if total_duration else "N/A"
|
|
1004
1016
|
)
|
|
1005
1017
|
|
|
1018
|
+
license_line = f"license: {license}"
|
|
1019
|
+
if license_needs_update:
|
|
1020
|
+
license_line += " # Please update this field to reflect your dataset's license"
|
|
1021
|
+
|
|
1006
1022
|
return f"""---
|
|
1007
1023
|
tags:
|
|
1008
1024
|
- braindecode
|
|
@@ -1010,7 +1026,7 @@ tags:
|
|
|
1010
1026
|
- neuroscience
|
|
1011
1027
|
- brain-computer-interface
|
|
1012
1028
|
- deep-learning
|
|
1013
|
-
|
|
1029
|
+
{license_line}
|
|
1014
1030
|
---
|
|
1015
1031
|
|
|
1016
1032
|
# EEG Dataset
|
|
@@ -1106,3 +1122,27 @@ if hasattr(dataset.datasets[0], "bids_channels"):
|
|
|
1106
1122
|
|
|
1107
1123
|
*Created with [braindecode](https://braindecode.org)*
|
|
1108
1124
|
"""
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
def _infer_license_from_descriptions(
|
|
1128
|
+
descriptions: list[pd.Series | None],
|
|
1129
|
+
) -> tuple[str, bool]:
|
|
1130
|
+
"""Infer a single dataset license from descriptions.
|
|
1131
|
+
|
|
1132
|
+
Returns inferred license and whether a manual update reminder is needed.
|
|
1133
|
+
"""
|
|
1134
|
+
licenses = set()
|
|
1135
|
+
for description in descriptions:
|
|
1136
|
+
if description is None:
|
|
1137
|
+
continue
|
|
1138
|
+
raw_license = description.get("license", None)
|
|
1139
|
+
if raw_license is None:
|
|
1140
|
+
continue
|
|
1141
|
+
normalized = str(raw_license).strip()
|
|
1142
|
+
if normalized and normalized.lower() != "nan":
|
|
1143
|
+
licenses.add(normalized)
|
|
1144
|
+
|
|
1145
|
+
if len(licenses) == 1:
|
|
1146
|
+
return next(iter(licenses)), False
|
|
1147
|
+
|
|
1148
|
+
return "please-specify", True
|
|
@@ -54,6 +54,7 @@ from .sleep_stager_blanco_2020 import SleepStagerBlanco2020
|
|
|
54
54
|
from .sleep_stager_chambon_2018 import SleepStagerChambon2018
|
|
55
55
|
from .sparcnet import SPARCNet
|
|
56
56
|
from .sstdpn import SSTDPN
|
|
57
|
+
from .steegformer import STEEGFormer
|
|
57
58
|
from .syncnet import SyncNet
|
|
58
59
|
from .tcformer import TCFormer
|
|
59
60
|
from .tcn import BDTCN, TCN
|
|
@@ -129,6 +130,7 @@ __all__ = [
|
|
|
129
130
|
"SleepStagerBlanco2020",
|
|
130
131
|
"SleepStagerChambon2018",
|
|
131
132
|
"SPARCNet",
|
|
133
|
+
"STEEGFormer",
|
|
132
134
|
"SyncNet",
|
|
133
135
|
"BDTCN",
|
|
134
136
|
"TCFormer",
|
|
@@ -4,9 +4,16 @@
|
|
|
4
4
|
# License: BSD-3
|
|
5
5
|
from einops.layers.torch import Rearrange
|
|
6
6
|
from torch import nn
|
|
7
|
+
from torch.nn.utils.parametrize import register_parametrization
|
|
7
8
|
|
|
8
9
|
from braindecode.models.base import EEGModuleMixin
|
|
9
|
-
from braindecode.modules import
|
|
10
|
+
from braindecode.modules import (
|
|
11
|
+
DepthwiseConv2d,
|
|
12
|
+
Ensure4d,
|
|
13
|
+
InceptionBlock,
|
|
14
|
+
LinearWithConstraint,
|
|
15
|
+
MaxNormParametrize,
|
|
16
|
+
)
|
|
10
17
|
|
|
11
18
|
|
|
12
19
|
class EEGITNet(EEGModuleMixin, nn.Sequential):
|
|
@@ -95,7 +102,9 @@ class EEGITNet(EEGModuleMixin, nn.Sequential):
|
|
|
95
102
|
sfreq=sfreq,
|
|
96
103
|
)
|
|
97
104
|
self.mapping = {
|
|
98
|
-
|
|
105
|
+
# final_layer is max-norm constrained, so its weight lives under the
|
|
106
|
+
# parametrization buffer rather than ``final_layer.weight``.
|
|
107
|
+
"classification.1.weight": "final_layer.parametrizations.weight.original",
|
|
99
108
|
"classification.1.bias": "final_layer.bias",
|
|
100
109
|
}
|
|
101
110
|
|
|
@@ -185,8 +194,10 @@ class EEGITNet(EEGModuleMixin, nn.Sequential):
|
|
|
185
194
|
self.add_module(
|
|
186
195
|
"dim_reduction",
|
|
187
196
|
nn.Sequential(
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
# The authors' DR layer keeps the width (14 filters, paper III-C),
|
|
198
|
+
# it does not double it.
|
|
199
|
+
nn.Conv2d(tcn_in_channel, tcn_in_channel, kernel_size=(1, 1)),
|
|
200
|
+
nn.BatchNorm2d(tcn_in_channel),
|
|
190
201
|
activation(),
|
|
191
202
|
nn.AvgPool2d((1, tcn_kernel_size)),
|
|
192
203
|
nn.Dropout(drop_prob),
|
|
@@ -198,7 +209,11 @@ class EEGITNet(EEGModuleMixin, nn.Sequential):
|
|
|
198
209
|
|
|
199
210
|
num_features = self.get_output_shape()[-1]
|
|
200
211
|
|
|
201
|
-
|
|
212
|
+
# The authors constrain the final dense with ``max_norm(0.25)``.
|
|
213
|
+
self.add_module(
|
|
214
|
+
"final_layer",
|
|
215
|
+
LinearWithConstraint(num_features, self.n_outputs, max_norm=0.25),
|
|
216
|
+
)
|
|
202
217
|
|
|
203
218
|
@staticmethod
|
|
204
219
|
def _get_inception_branch(
|
|
@@ -208,6 +223,18 @@ class EEGITNet(EEGModuleMixin, nn.Sequential):
|
|
|
208
223
|
depth_multiplier=1,
|
|
209
224
|
activation: type[nn.Module] = nn.ELU,
|
|
210
225
|
):
|
|
226
|
+
# The authors constrain the spatial depthwise conv with
|
|
227
|
+
# ``depthwise_constraint = MaxNorm(max_value=1)``.
|
|
228
|
+
spatial_depthwise = DepthwiseConv2d(
|
|
229
|
+
out_channels,
|
|
230
|
+
kernel_size=(in_channels, 1),
|
|
231
|
+
depth_multiplier=depth_multiplier,
|
|
232
|
+
bias=False,
|
|
233
|
+
padding="valid",
|
|
234
|
+
)
|
|
235
|
+
register_parametrization(
|
|
236
|
+
spatial_depthwise, "weight", MaxNormParametrize(max_norm=1.0)
|
|
237
|
+
)
|
|
211
238
|
return nn.Sequential(
|
|
212
239
|
nn.Conv2d(
|
|
213
240
|
1,
|
|
@@ -217,13 +244,7 @@ class EEGITNet(EEGModuleMixin, nn.Sequential):
|
|
|
217
244
|
bias=False,
|
|
218
245
|
),
|
|
219
246
|
nn.BatchNorm2d(out_channels),
|
|
220
|
-
|
|
221
|
-
out_channels,
|
|
222
|
-
kernel_size=(in_channels, 1),
|
|
223
|
-
depth_multiplier=depth_multiplier,
|
|
224
|
-
bias=False,
|
|
225
|
-
padding="valid",
|
|
226
|
-
),
|
|
247
|
+
spatial_depthwise,
|
|
227
248
|
nn.BatchNorm2d(out_channels),
|
|
228
249
|
activation(),
|
|
229
250
|
)
|
|
@@ -504,6 +504,11 @@ class _InceptionBlock(nn.Module):
|
|
|
504
504
|
out_channels=filters_per_branch * len(scales_samples),
|
|
505
505
|
kernel_size=(1, 1, ncha),
|
|
506
506
|
padding=(0, 0, 0),
|
|
507
|
+
# depthwise/grouped spatial conv with no bias, as in
|
|
508
|
+
# the authors' ``unit_dconv`` (per-filter spatial
|
|
509
|
+
# filtering, not dense mixing across filters).
|
|
510
|
+
groups=filters_per_branch * len(scales_samples),
|
|
511
|
+
bias=False,
|
|
507
512
|
),
|
|
508
513
|
nn.BatchNorm3d(filters_per_branch * len(scales_samples)),
|
|
509
514
|
activation,
|
|
@@ -621,6 +626,10 @@ class _ResidualBlock(nn.Module):
|
|
|
621
626
|
out_channels=filters,
|
|
622
627
|
kernel_size=(1, 1, ncha), # Spatial convolution
|
|
623
628
|
padding=(0, 0, 0),
|
|
629
|
+
# depthwise/grouped spatial conv with no bias, as in
|
|
630
|
+
# the authors' ``unit_dconv``.
|
|
631
|
+
groups=filters,
|
|
632
|
+
bias=False,
|
|
624
633
|
),
|
|
625
634
|
nn.BatchNorm3d(filters),
|
|
626
635
|
activation,
|
|
@@ -186,9 +186,13 @@ class MSVTNet(EEGModuleMixin, nn.Module):
|
|
|
186
186
|
|
|
187
187
|
x = self.final_layer(x)
|
|
188
188
|
if self.return_features:
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
|
|
189
|
+
# Return BOTH the main-head logits and the stacked branch logits, as
|
|
190
|
+
# in the authors' implementation (``return x, bx``). This is required
|
|
191
|
+
# for the paper's joint deep-supervision loss
|
|
192
|
+
# ``lambda * CE(main) + (1 - lambda) * sum_i CE(branch_i)`` and lets
|
|
193
|
+
# callers score on the main head.
|
|
194
|
+
# x: [batch_size, n_classes]; branches: [n_branches, batch_size, n_classes]
|
|
195
|
+
return x, torch.stack(branch_preds)
|
|
192
196
|
return x
|
|
193
197
|
|
|
194
198
|
|