Braindecode 1.3.2.dev179983996__tar.gz → 1.3.2.dev181313884__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.
Files changed (163) hide show
  1. {braindecode-1.3.2.dev179983996/braindecode.egg-info → braindecode-1.3.2.dev181313884}/PKG-INFO +3 -3
  2. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/hub.py +7 -2
  3. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/__init__.py +2 -0
  4. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/base.py +244 -93
  5. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/bendr.py +17 -6
  6. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/biot.py +23 -5
  7. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/cbramod.py +28 -6
  8. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/config.py +29 -29
  9. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegpt.py +15 -2
  10. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegsym.py +1 -1
  11. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/labram.py +15 -1
  12. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/luna.py +8 -7
  13. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/patchedtransformer.py +5 -3
  14. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/reve.py +59 -23
  15. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/shallow_fbcsp.py +16 -8
  16. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/signal_jepa.py +52 -13
  17. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/sstdpn.py +1 -1
  18. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/util.py +133 -2
  19. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/__init__.py +2 -1
  20. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/activation.py +20 -0
  21. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/preprocessing/mne_preprocess.py +2 -1
  22. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/util.py +34 -4
  23. braindecode-1.3.2.dev181313884/braindecode/version.py +1 -0
  24. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884/braindecode.egg-info}/PKG-INFO +3 -3
  25. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode.egg-info/requires.txt +2 -2
  26. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/api.rst +10 -9
  27. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/conf.py +18 -13
  28. braindecode-1.3.2.dev181313884/docs/models/categorization/spd.rst +103 -0
  29. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/models_categorization.rst +58 -57
  30. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/whats_new.rst +66 -6
  31. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/pyproject.toml +11 -2
  32. braindecode-1.3.2.dev179983996/braindecode/version.py +0 -1
  33. braindecode-1.3.2.dev179983996/docs/models/categorization/spd.rst +0 -35
  34. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/LICENSE.txt +0 -0
  35. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/MANIFEST.in +0 -0
  36. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/NOTICE.txt +0 -0
  37. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/README.rst +0 -0
  38. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/__init__.py +0 -0
  39. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/augmentation/__init__.py +0 -0
  40. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/augmentation/base.py +0 -0
  41. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/augmentation/functional.py +0 -0
  42. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/augmentation/transforms.py +0 -0
  43. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/classifier.py +0 -0
  44. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/__init__.py +0 -0
  45. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/base.py +0 -0
  46. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bbci.py +0 -0
  47. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bcicomp.py +0 -0
  48. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/__init__.py +0 -0
  49. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/datasets.py +0 -0
  50. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/format.py +0 -0
  51. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/hub_format.py +0 -0
  52. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/hub_io.py +0 -0
  53. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/hub_validation.py +0 -0
  54. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/bids/iterable.py +0 -0
  55. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/chb_mit.py +0 -0
  56. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/mne.py +0 -0
  57. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/moabb.py +0 -0
  58. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/nmt.py +0 -0
  59. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/registry.py +0 -0
  60. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/siena.py +0 -0
  61. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
  62. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/sleep_physionet.py +0 -0
  63. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/tuh.py +0 -0
  64. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/utils.py +0 -0
  65. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datasets/xy.py +0 -0
  66. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datautil/__init__.py +0 -0
  67. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datautil/channel_utils.py +0 -0
  68. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datautil/hub_formats.py +0 -0
  69. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datautil/serialization.py +0 -0
  70. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/datautil/util.py +0 -0
  71. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/eegneuralnet.py +0 -0
  72. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/functional/__init__.py +0 -0
  73. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/functional/functions.py +0 -0
  74. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/functional/initialization.py +0 -0
  75. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/atcnet.py +0 -0
  76. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/attentionbasenet.py +0 -0
  77. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/attn_sleep.py +0 -0
  78. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/brainmodule.py +0 -0
  79. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/contrawr.py +0 -0
  80. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/ctnet.py +0 -0
  81. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/deep4.py +0 -0
  82. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/deepsleepnet.py +0 -0
  83. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/dgcnn.py +0 -0
  84. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegconformer.py +0 -0
  85. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eeginception_erp.py +0 -0
  86. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eeginception_mi.py +0 -0
  87. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegitnet.py +0 -0
  88. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegminer.py +0 -0
  89. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegnet.py +0 -0
  90. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegnex.py +0 -0
  91. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegsimpleconv.py +0 -0
  92. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/eegtcnet.py +0 -0
  93. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/fbcnet.py +0 -0
  94. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/fblightconvnet.py +0 -0
  95. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/fbmsnet.py +0 -0
  96. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/hybrid.py +0 -0
  97. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/ifnet.py +0 -0
  98. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/medformer.py +0 -0
  99. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/msvtnet.py +0 -0
  100. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/sccnet.py +0 -0
  101. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/sinc_shallow.py +0 -0
  102. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
  103. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
  104. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/sparcnet.py +0 -0
  105. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/summary.csv +0 -0
  106. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/syncnet.py +0 -0
  107. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/tcn.py +0 -0
  108. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/tidnet.py +0 -0
  109. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/tsinception.py +0 -0
  110. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/models/usleep.py +0 -0
  111. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/attention.py +0 -0
  112. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/blocks.py +0 -0
  113. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/convolution.py +0 -0
  114. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/filter.py +0 -0
  115. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/layers.py +0 -0
  116. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/linear.py +0 -0
  117. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/parametrization.py +0 -0
  118. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/stats.py +0 -0
  119. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/util.py +0 -0
  120. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/modules/wrapper.py +0 -0
  121. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/preprocessing/__init__.py +0 -0
  122. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
  123. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/preprocessing/preprocess.py +0 -0
  124. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/preprocessing/util.py +0 -0
  125. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/preprocessing/windowers.py +0 -0
  126. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/regressor.py +0 -0
  127. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/samplers/__init__.py +0 -0
  128. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/samplers/base.py +0 -0
  129. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/samplers/ssl.py +0 -0
  130. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/training/__init__.py +0 -0
  131. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/training/callbacks.py +0 -0
  132. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/training/losses.py +0 -0
  133. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/training/scoring.py +0 -0
  134. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/visualization/__init__.py +0 -0
  135. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/visualization/confusion_matrices.py +0 -0
  136. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode/visualization/gradients.py +0 -0
  137. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode.egg-info/SOURCES.txt +0 -0
  138. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode.egg-info/dependency_links.txt +0 -0
  139. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/braindecode.egg-info/top_level.txt +0 -0
  140. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/Makefile +0 -0
  141. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/_templates/autosummary/class.rst +0 -0
  142. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
  143. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/_templates/autosummary/function.rst +0 -0
  144. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
  145. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/cite.rst +0 -0
  146. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/help.rst +0 -0
  147. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/index.rst +0 -0
  148. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/install/install.rst +0 -0
  149. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/install/install_pip.rst +0 -0
  150. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/install/install_source.rst +0 -0
  151. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/attention.rst +0 -0
  152. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/channel.rst +0 -0
  153. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/convolution.rst +0 -0
  154. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/filterbank.rst +0 -0
  155. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/gnn.rst +0 -0
  156. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/interpretable.rst +0 -0
  157. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/lbm.rst +0 -0
  158. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/categorization/recurrent.rst +0 -0
  159. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/models.rst +0 -0
  160. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/models_table.rst +0 -0
  161. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/models/models_visualization.rst +0 -0
  162. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/docs/sg_execution_times.rst +0 -0
  163. {braindecode-1.3.2.dev179983996 → braindecode-1.3.2.dev181313884}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: braindecode
3
- Version: 1.3.2.dev179983996
3
+ Version: 1.3.2.dev181313884
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>
@@ -22,7 +22,7 @@ Requires-Python: >=3.11
22
22
  Description-Content-Type: text/x-rst
23
23
  License-File: LICENSE.txt
24
24
  License-File: NOTICE.txt
25
- Requires-Dist: torch>=2.2
25
+ Requires-Dist: torch>=2.0
26
26
  Requires-Dist: torchaudio>=2.0
27
27
  Requires-Dist: mne>=1.11.0
28
28
  Requires-Dist: pandas<3.0.0
@@ -35,7 +35,7 @@ Requires-Dist: wfdb
35
35
  Requires-Dist: linear_attention_transformer
36
36
  Requires-Dist: docstring_inheritance
37
37
  Requires-Dist: rotary_embedding_torch
38
- Requires-Dist: pandas<3.0.0
38
+ Requires-Dist: pydantic>=2.0
39
39
  Provides-Extra: moabb
40
40
  Requires-Dist: moabb>=1.4.3; extra == "moabb"
41
41
  Provides-Extra: eegprep
@@ -845,11 +845,16 @@ class HubDatasetMixin:
845
845
 
846
846
  # Convert to MNE objects and create dataset
847
847
  info = Info.from_json_dict(info_dict)
848
+ targets = metadata["target"].values
849
+ if np.issubdtype(targets.dtype, np.integer):
850
+ event_ids = targets
851
+ else:
852
+ event_ids = np.ones(len(metadata), dtype=int)
848
853
  events = np.column_stack(
849
854
  [
850
- metadata["i_start_in_trial"].values,
855
+ metadata["i_start_in_trial"].values.astype(int),
851
856
  np.zeros(len(metadata), dtype=int),
852
- metadata["target"].values,
857
+ event_ids,
853
858
  ]
854
859
  )
855
860
  epochs = mne.EpochsArray(data, info, events=events, metadata=metadata)
@@ -55,6 +55,7 @@ from .tsinception import TSception
55
55
  from .usleep import USleep
56
56
  from .util import (
57
57
  _init_models_dict,
58
+ build_model_config,
58
59
  extract_channel_locations_from_chs_info,
59
60
  models_mandatory_parameters,
60
61
  )
@@ -118,6 +119,7 @@ __all__ = [
118
119
  "TIDNet",
119
120
  "TSception",
120
121
  "USleep",
122
+ "build_model_config",
121
123
  "_init_models_dict",
122
124
  "models_mandatory_parameters",
123
125
  ]
@@ -17,6 +17,13 @@ from docstring_inheritance import NumpyDocstringInheritanceInitMeta
17
17
  from mne.utils import _soft_import
18
18
  from torchinfo import ModelStatistics, summary
19
19
 
20
+ from braindecode.models.util import (
21
+ _EEG_PARAMS,
22
+ _IMPORT_ADAPTER,
23
+ build_model_config,
24
+ resolve_type_kwargs,
25
+ track_model_init_kwargs,
26
+ )
20
27
  from braindecode.version import __version__
21
28
 
22
29
  huggingface_hub = _soft_import(
@@ -52,7 +59,25 @@ def deprecated_args(obj, *old_new_args):
52
59
  return out_args
53
60
 
54
61
 
55
- class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta):
62
+ class _BraindecodeDocstringMeta(NumpyDocstringInheritanceInitMeta):
63
+ """Defer ``__init__`` wrapping until after docstring inheritance.
64
+
65
+ ``NumpyDocstringInheritanceInitMeta`` uses ``inspect.unwrap()``
66
+ internally, which bypasses ``@wraps`` wrappers. By wrapping
67
+ ``__init__`` *after* docstring processing, the metaclass sees the
68
+ unwrapped function and correctly inherits ``cls.__doc__``.
69
+ """
70
+
71
+ def __init__(cls, class_name, class_bases, class_dict):
72
+ super().__init__(class_name, class_bases, class_dict)
73
+ # Only wrap subclass __init__s, not EEGModuleMixin itself.
74
+ # Wrapping the mixin would cause super().__init__() calls to
75
+ # overwrite _braindecode_init_kwargs captured by the subclass.
76
+ if any(isinstance(b, _BraindecodeDocstringMeta) for b in class_bases):
77
+ track_model_init_kwargs(cls)
78
+
79
+
80
+ class EEGModuleMixin(_BaseHubMixin, metaclass=_BraindecodeDocstringMeta):
56
81
  """
57
82
  Mixin class for all EEG models in braindecode.
58
83
 
@@ -86,59 +111,66 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
86
111
  -----
87
112
  If some input signal-related parameters are not specified,
88
113
  there will be an attempt to infer them from the other parameters.
114
+ """
89
115
 
116
+ #: Template for model-specific Hub integration notes appended by
117
+ #: ``__init_subclass__``. ``{name}`` is replaced with the concrete
118
+ #: model class name.
119
+ _HUB_NOTES_TEMPLATE = """
90
120
  .. rubric:: Hugging Face Hub integration
91
121
 
92
122
  When the optional ``huggingface_hub`` package is installed, all models
93
123
  automatically gain the ability to be pushed to and loaded from the
94
124
  Hugging Face Hub. Install with::
95
125
 
96
- pip install braindecode[hug]
126
+ pip install braindecode[hub]
97
127
 
98
128
  **Pushing a model to the Hub:**
99
129
 
100
- .. code-block:: python
130
+ .. code-block::
101
131
 
102
- from braindecode.models import EEGNetv4
132
+ from braindecode.models import {name}
103
133
 
104
134
  # Train your model
105
- model = EEGNetv4(n_chans=22, n_outputs=4, n_times=1000)
135
+ model = {name}(n_chans=22, n_outputs=4, n_times=1000)
106
136
  # ... training code ...
107
137
 
108
138
  # Push to the Hub
109
139
  model.push_to_hub(
110
- repo_id="username/my-eegnet-model", commit_message="Initial model upload"
140
+ repo_id="username/my-{name_lower}-model",
141
+ commit_message="Initial model upload",
111
142
  )
112
143
 
113
144
  **Loading a model from the Hub:**
114
145
 
115
- .. code-block:: python
146
+ .. code-block::
116
147
 
117
- from braindecode.models import EEGNetv4
148
+ from braindecode.models import {name}
118
149
 
119
150
  # Load pretrained model
120
- model = EEGNetv4.from_pretrained("username/my-eegnet-model")
151
+ model = {name}.from_pretrained("username/my-{name_lower}-model")
121
152
 
122
153
  The integration automatically handles EEG-specific parameters (n_chans,
123
154
  n_times, sfreq, chs_info, etc.) by saving them in a config file alongside
124
155
  the model weights. This ensures that loaded models are correctly configured
125
156
  for their original data specifications.
126
157
 
127
- .. important::
128
- Currently, only EEG-specific parameters (n_outputs, n_chans, n_times,
129
- input_window_seconds, sfreq, chs_info) are saved to the Hub. Model-specific
130
- parameters (e.g., dropout rates, activation functions, number of filters)
131
- are not preserved and will use their default values when loading from the Hub.
132
-
133
- To use non-default model parameters, specify them explicitly when calling
134
- :func:`from_pretrained()`::
135
-
136
- model = EEGNet.from_pretrained("user/model", dropout=0.3, activation='relu')
137
-
138
- Full parameter serialization will be addressed in a future update.
158
+ All model parameters (both EEG-specific and model-specific such as
159
+ dropout rates, activation functions, number of filters) are automatically
160
+ saved to the Hub and restored when loading.
139
161
  """
140
162
 
141
163
  def __init_subclass__(cls, **kwargs):
164
+ # Append model-specific Hub integration notes to the docstring.
165
+ # This runs before the metaclass __init__, so the Hub notes will
166
+ # be included in the docstring that the metaclass processes.
167
+ if cls.__doc__ is not None:
168
+ hub_notes = cls._HUB_NOTES_TEMPLATE.format(
169
+ name=cls.__name__,
170
+ name_lower=cls.__name__.lower(),
171
+ )
172
+ cls.__doc__ = cls.__doc__.rstrip() + "\n" + hub_notes
173
+
142
174
  if not HAS_HF_HUB:
143
175
  super().__init_subclass__(**kwargs)
144
176
  return
@@ -157,6 +189,19 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
157
189
  repo_url = kwargs.pop("repo_url", "https://braindecode.org")
158
190
  library_name = kwargs.pop("library_name", "braindecode")
159
191
  license = kwargs.pop("license", "bsd-3-clause")
192
+
193
+ # Register a coder so that type[nn.Module] parameters
194
+ # (e.g. activation=nn.ELU) are serialized as importable
195
+ # strings in config.json and decoded back on load.
196
+ coders = kwargs.pop("coders", None) or {}
197
+ coders.setdefault(
198
+ type,
199
+ (
200
+ lambda t: f"{t.__module__}.{t.__qualname__}",
201
+ lambda data: _IMPORT_ADAPTER.validate_python(data),
202
+ ),
203
+ )
204
+
160
205
  # TODO: model_card_template can be added in the future for custom model cards
161
206
  super().__init_subclass__(
162
207
  tags=tags,
@@ -164,6 +209,7 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
164
209
  repo_url=repo_url,
165
210
  library_name=library_name,
166
211
  license=license,
212
+ coders=coders,
167
213
  **kwargs,
168
214
  )
169
215
 
@@ -205,6 +251,17 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
205
251
  self._n_times = n_times # type: ignore[assignment]
206
252
  self._sfreq = sfreq # type: ignore[assignment]
207
253
 
254
+ # Back-fill instance attributes from _hub_mixin_config for any
255
+ # params the subclass didn't store on self. Skip EEG params
256
+ # (stored as self._*) and descriptors (properties).
257
+ for key, val in getattr(self, "_hub_mixin_config", {}).items():
258
+ if key in _EEG_PARAMS:
259
+ continue
260
+ if hasattr(getattr(type(self), key, None), "__get__"):
261
+ continue
262
+ if not hasattr(self, key):
263
+ setattr(self, key, val)
264
+
208
265
  super().__init__()
209
266
 
210
267
  @property
@@ -315,6 +372,104 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
315
372
  raise ValueError(msg) from exc
316
373
  raise exc
317
374
 
375
+ def get_config(self) -> dict:
376
+ """Return a JSON-serializable dict of all ``__init__`` parameters.
377
+
378
+ The returned dictionary can be saved to a JSON file and later
379
+ used with :meth:`from_config` to reconstruct the model (without
380
+ weights). It is also used internally by :meth:`push_to_hub` to
381
+ persist the full model configuration.
382
+
383
+ Returns
384
+ -------
385
+ dict
386
+ All ``__init__`` parameters, JSON-serializable.
387
+ ``type[nn.Module]`` parameters (e.g. ``activation``) are
388
+ encoded as importable dotted-path strings.
389
+
390
+ Examples
391
+ --------
392
+ >>> import json
393
+ >>> from braindecode.models import EEGNet
394
+ >>> model = EEGNet(n_chans=22, n_times=1000, n_outputs=4, F1=16)
395
+ >>> config = model.get_config()
396
+ >>> config["F1"]
397
+ 16
398
+ >>> # Save to disk
399
+ >>> with open("config.json", "w") as f:
400
+ ... json.dump(config, f)
401
+
402
+ .. versionadded:: 1.4
403
+ """
404
+ return build_model_config(self)
405
+
406
+ @classmethod
407
+ def from_config(cls, config: dict) -> "EEGModuleMixin":
408
+ """Create a model instance from a configuration dict.
409
+
410
+ This is the inverse of :meth:`get_config`. Weights are **not**
411
+ loaded -- use :meth:`from_pretrained` for that.
412
+
413
+ Parameters
414
+ ----------
415
+ config : dict
416
+ Configuration dict as returned by :meth:`get_config`.
417
+
418
+ Returns
419
+ -------
420
+ EEGModuleMixin
421
+ A new model instance.
422
+
423
+ Examples
424
+ --------
425
+ >>> import json
426
+ >>> from braindecode.models import EEGNet
427
+ >>> model = EEGNet(n_chans=22, n_times=1000, n_outputs=4, F1=16)
428
+ >>> config = model.get_config()
429
+ >>> # Reconstruct (without weights)
430
+ >>> model2 = EEGNet.from_config(config)
431
+ >>> model2.F1
432
+ 16
433
+ >>> # Or from a JSON file
434
+ >>> with open("config.json") as f:
435
+ ... config = json.load(f)
436
+ >>> model3 = EEGNet.from_config(config)
437
+
438
+ .. versionadded:: 1.4
439
+ """
440
+ config = dict(config) # shallow copy
441
+ config.pop("braindecode_version", None)
442
+ resolve_type_kwargs(cls, config)
443
+ return cls(**config)
444
+
445
+ def reset_head(self, n_outputs):
446
+ """Replace the classification head for a new number of outputs.
447
+
448
+ This is called automatically by :meth:`from_pretrained` when the
449
+ user passes an ``n_outputs`` that differs from the saved config.
450
+ Override in subclasses that need a model-specific head structure.
451
+
452
+ Parameters
453
+ ----------
454
+ n_outputs : int
455
+ New number of output classes.
456
+
457
+ Examples
458
+ --------
459
+ >>> from braindecode.models import BENDR
460
+ >>> model = BENDR(n_chans=22, n_times=1000, n_outputs=4)
461
+ >>> model.reset_head(10)
462
+ >>> model.n_outputs
463
+ 10
464
+
465
+ .. versionadded:: 1.4
466
+ """
467
+ raise NotImplementedError(
468
+ f"{type(self).__name__} does not implement reset_head(). "
469
+ "Override this method to support changing n_outputs after "
470
+ "loading pretrained weights."
471
+ )
472
+
318
473
  mapping: Optional[Dict[str, str]] = None
319
474
 
320
475
  def load_state_dict(self, state_dict, *args, **kwargs):
@@ -419,54 +574,22 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
419
574
 
420
575
  @staticmethod
421
576
  def _serialize_chs_info(chs_info):
422
- """
423
- Serialize MNE channel info to JSON-compatible format.
424
-
425
- Parameters
426
- ----------
427
- chs_info : list of dict or None
428
- Channel information from MNE Info object.
429
-
430
- Returns
431
- -------
432
- list of dict or None
433
- Serialized channel information that can be saved to JSON.
434
- """
577
+ """Serialize MNE channel info (``info["chs"]``) to JSON-compatible dicts."""
435
578
  if chs_info is None:
436
579
  return None
437
-
580
+ _INT_FIELDS = ("kind", "coil_type", "unit")
581
+ _FLOAT_FIELDS = ("cal", "range")
438
582
  serialized = []
439
583
  for ch in chs_info:
440
- # Extract serializable fields from MNE channel info
441
- ch_dict = {
442
- "ch_name": ch.get("ch_name", ""),
443
- }
444
-
445
- # Handle kind field - can be either string or integer
446
- kind_val = ch.get("kind")
447
- if kind_val is not None:
448
- ch_dict["kind"] = (
449
- kind_val if isinstance(kind_val, str) else int(kind_val)
450
- )
451
-
452
- # Add numeric fields with safe conversion
453
- coil_type = ch.get("coil_type")
454
- if coil_type is not None:
455
- ch_dict["coil_type"] = int(coil_type)
456
-
457
- unit = ch.get("unit")
458
- if unit is not None:
459
- ch_dict["unit"] = int(unit)
460
-
461
- cal = ch.get("cal")
462
- if cal is not None:
463
- ch_dict["cal"] = float(cal)
464
-
465
- range_val = ch.get("range")
466
- if range_val is not None:
467
- ch_dict["range"] = float(range_val)
468
-
469
- # Serialize location array if present
584
+ ch_dict = {"ch_name": ch.get("ch_name", "")}
585
+ for key in _INT_FIELDS:
586
+ val = ch.get(key)
587
+ if val is not None:
588
+ ch_dict[key] = val if isinstance(val, str) else int(val)
589
+ for key in _FLOAT_FIELDS:
590
+ val = ch.get(key)
591
+ if val is not None:
592
+ ch_dict[key] = float(val)
470
593
  if "loc" in ch and ch["loc"] is not None:
471
594
  ch_dict["loc"] = (
472
595
  ch["loc"].tolist()
@@ -474,35 +597,19 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
474
597
  else list(ch["loc"])
475
598
  )
476
599
  serialized.append(ch_dict)
477
-
478
600
  return serialized
479
601
 
480
602
  @staticmethod
481
603
  def _deserialize_chs_info(chs_info_dict):
482
- """
483
- Deserialize channel info from JSON-compatible format to MNE-like structure.
484
-
485
- Parameters
486
- ----------
487
- chs_info_dict : list of dict or None
488
- Serialized channel information.
489
-
490
- Returns
491
- -------
492
- list of dict or None
493
- Deserialized channel information compatible with MNE.
494
- """
604
+ """Deserialize JSON channel dicts back to MNE-compatible format."""
495
605
  if chs_info_dict is None:
496
606
  return None
497
-
498
607
  deserialized = []
499
608
  for ch_dict in chs_info_dict:
500
609
  ch = ch_dict.copy()
501
- # Convert location back to numpy array if present
502
610
  if "loc" in ch and ch["loc"] is not None:
503
611
  ch["loc"] = np.array(ch["loc"])
504
612
  deserialized.append(ch)
505
-
506
613
  return deserialized
507
614
 
508
615
  def _save_pretrained(self, save_directory):
@@ -522,16 +629,8 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
522
629
 
523
630
  save_directory = Path(save_directory)
524
631
 
525
- # Collect EEG-specific configuration
526
- config = {
527
- "n_outputs": self._n_outputs,
528
- "n_chans": self._n_chans,
529
- "n_times": self._n_times,
530
- "input_window_seconds": self._input_window_seconds,
531
- "sfreq": self._sfreq,
532
- "chs_info": self._serialize_chs_info(self._chs_info),
533
- "braindecode_version": __version__,
534
- }
632
+ config = build_model_config(self)
633
+ config["braindecode_version"] = __version__
535
634
 
536
635
  # Save to config.json
537
636
  config_path = save_directory / "config.json"
@@ -570,7 +669,42 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
570
669
  **model_kwargs,
571
670
  ):
572
671
  model_kwargs.pop("braindecode_version", None)
573
- return super()._from_pretrained( # type: ignore
672
+ resolve_type_kwargs(cls, model_kwargs)
673
+
674
+ # Read saved n_outputs from config.json to detect when the
675
+ # user wants a different number of outputs. Works for both
676
+ # local directories and Hub repo IDs.
677
+ saved_n_outputs = None
678
+ try:
679
+ if Path(model_id).is_dir():
680
+ config_file = Path(model_id) / "config.json"
681
+ else:
682
+ config_file = huggingface_hub.hf_hub_download(
683
+ repo_id=model_id,
684
+ filename="config.json",
685
+ revision=revision,
686
+ cache_dir=cache_dir,
687
+ force_download=force_download,
688
+ token=token,
689
+ local_files_only=local_files_only,
690
+ )
691
+ with open(config_file, "r") as f:
692
+ saved_n_outputs = json.load(f).get("n_outputs")
693
+ except (OSError, json.JSONDecodeError, KeyError):
694
+ pass # config unavailable; skip reset_head logic
695
+
696
+ requested_n_outputs = model_kwargs.get("n_outputs")
697
+
698
+ # If the user requests different n_outputs, load with the
699
+ # saved value first (so weights match), then swap the head.
700
+ if (
701
+ saved_n_outputs is not None
702
+ and requested_n_outputs is not None
703
+ and requested_n_outputs != saved_n_outputs
704
+ ):
705
+ model_kwargs["n_outputs"] = saved_n_outputs
706
+
707
+ model = super()._from_pretrained( # type: ignore
574
708
  model_id=model_id,
575
709
  revision=revision,
576
710
  cache_dir=cache_dir,
@@ -581,3 +715,20 @@ class EEGModuleMixin(_BaseHubMixin, metaclass=NumpyDocstringInheritanceInitMeta)
581
715
  strict=strict,
582
716
  **model_kwargs,
583
717
  )
718
+
719
+ if (
720
+ saved_n_outputs is not None
721
+ and requested_n_outputs is not None
722
+ and requested_n_outputs != saved_n_outputs
723
+ ):
724
+ try:
725
+ model.reset_head(requested_n_outputs)
726
+ except NotImplementedError:
727
+ raise ValueError(
728
+ f"{type(model).__name__} does not support changing "
729
+ f"n_outputs after loading. Saved model has "
730
+ f"n_outputs={saved_n_outputs}, but "
731
+ f"n_outputs={requested_n_outputs} was requested."
732
+ ) from None
733
+
734
+ return model
@@ -291,15 +291,23 @@ class BENDR(EEGModuleMixin, nn.Module):
291
291
  else:
292
292
  in_features = encoder_h
293
293
 
294
+ self._head_in_features = in_features
294
295
  self.final_layer = None
295
296
  if self.include_final_layer:
296
- # in_features: encoder_h*4 (encoder_only) or encoder_h (full model)
297
- linear = nn.Linear(in_features=in_features, out_features=self.n_outputs)
298
- self.final_layer = nn.utils.parametrizations.weight_norm(
299
- linear, name="weight", dim=1
300
- )
297
+ self._build_head(self.n_outputs)
301
298
 
302
- def forward(self, x):
299
+ def _build_head(self, n_outputs):
300
+ linear = nn.Linear(in_features=self._head_in_features, out_features=n_outputs)
301
+ self.final_layer = nn.utils.parametrizations.weight_norm(
302
+ linear, name="weight", dim=1
303
+ )
304
+
305
+ def reset_head(self, n_outputs):
306
+ self._n_outputs = n_outputs
307
+ self.include_final_layer = True
308
+ self._build_head(n_outputs)
309
+
310
+ def forward(self, x, return_features=False):
303
311
  if self.channel_projection is not None:
304
312
  x = self.channel_projection(x)
305
313
  encoded = self.encoder(x)
@@ -328,6 +336,9 @@ class BENDR(EEGModuleMixin, nn.Module):
328
336
  feature = context[:, :, 0]
329
337
  # feature: [batch_size, encoder_h]
330
338
 
339
+ if return_features:
340
+ return {"features": feature, "cls_token": None}
341
+
331
342
  if self.final_layer is not None:
332
343
  feature = self.final_layer(feature)
333
344
  # feature: [batch_size, n_outputs]
@@ -183,13 +183,22 @@ class BIOT(EEGModuleMixin, nn.Module):
183
183
  attn_layer_dropout=att_layer_drop_prob,
184
184
  )
185
185
 
186
+ self._head_activation = activation
186
187
  self.final_layer = _ClassificationHead(
187
188
  emb_size=self.embed_dim,
188
189
  n_outputs=self.n_outputs,
189
190
  activation=activation,
190
191
  )
191
192
 
192
- def forward(self, x):
193
+ def reset_head(self, n_outputs):
194
+ self._n_outputs = n_outputs
195
+ self.final_layer = _ClassificationHead(
196
+ emb_size=self.embed_dim,
197
+ n_outputs=n_outputs,
198
+ activation=self._head_activation,
199
+ )
200
+
201
+ def forward(self, x, return_features=False):
193
202
  """
194
203
  Pass the input through the BIOT encoder, and then through the
195
204
  classification head.
@@ -198,15 +207,24 @@ class BIOT(EEGModuleMixin, nn.Module):
198
207
  ----------
199
208
  x: Tensor
200
209
  (batch_size, n_channels, n_times)
210
+ return_features : bool
211
+ If True, return a dict with ``"features"`` and ``"cls_token"``
212
+ instead of the classification output.
201
213
 
202
214
  Returns
203
215
  -------
204
- out: Tensor
205
- (batch_size, n_outputs)
206
- (out, emb): tuple Tensor
207
- (batch_size, n_outputs), (batch_size, emb_size)
216
+ torch.Tensor or tuple or dict
217
+ Default: ``torch.Tensor`` of shape ``(batch_size, n_outputs)``.
218
+ If ``return_features=True``: ``dict`` with ``"features"``
219
+ ``(batch_size, emb_size)`` and ``"cls_token"`` (``None``).
220
+ If legacy ``return_feature=True`` (init param):
221
+ ``(out, emb)`` tuple (ignored when ``return_features=True``).
208
222
  """
209
223
  emb = self.encoder(x)
224
+
225
+ if return_features:
226
+ return {"features": emb, "cls_token": None}
227
+
210
228
  x = self.final_layer(emb)
211
229
 
212
230
  if self.return_feature:
@@ -205,13 +205,33 @@ class CBraMod(EEGModuleMixin, nn.Module):
205
205
  self.encoder = _TransformerEncoder(encoder_layer, num_layers=n_layer)
206
206
  self.proj_out = nn.Sequential(nn.Linear(d_model, emb_dim))
207
207
 
208
+ self._emb_dim = emb_dim
209
+ self._patch_size = patch_size
208
210
  self._weights_init()
209
211
 
210
- self.final_layer = (
211
- nn.Identity()
212
- if return_encoder_output
213
- else nn.Sequential(nn.Flatten(), nn.LazyLinear(self.n_outputs))
214
- )
212
+ if return_encoder_output:
213
+ self.final_layer = nn.Identity()
214
+ elif self._n_times is not None and self._n_chans is not None:
215
+ n_patch = self._n_times // patch_size
216
+ flat_dim = self._n_chans * n_patch * emb_dim
217
+ self.final_layer = nn.Sequential(
218
+ nn.Flatten(), nn.Linear(flat_dim, self.n_outputs)
219
+ )
220
+ else:
221
+ self.final_layer = nn.Sequential(
222
+ nn.Flatten(), nn.LazyLinear(self.n_outputs)
223
+ )
224
+
225
+ def reset_head(self, n_outputs):
226
+ self._n_outputs = n_outputs
227
+ if self._n_times is not None and self._n_chans is not None:
228
+ n_patch = self._n_times // self._patch_size
229
+ flat_dim = self._n_chans * n_patch * self._emb_dim
230
+ self.final_layer = nn.Sequential(
231
+ nn.Flatten(), nn.Linear(flat_dim, n_outputs)
232
+ )
233
+ else:
234
+ self.final_layer = nn.Sequential(nn.Flatten(), nn.LazyLinear(n_outputs))
215
235
 
216
236
  def _weights_init(self):
217
237
  for m in self.modules():
@@ -223,11 +243,13 @@ class CBraMod(EEGModuleMixin, nn.Module):
223
243
  nn.init.constant_(m.weight, 1)
224
244
  nn.init.constant_(m.bias, 0)
225
245
 
226
- def forward(self, x, mask=None):
246
+ def forward(self, x, mask=None, return_features=False):
227
247
  x = self.rearrange(x)
228
248
  patch_emb = self.patch_embedding(x, mask)
229
249
  feats = self.encoder(patch_emb)
230
250
  out = self.proj_out(feats)
251
+ if return_features:
252
+ return {"features": out, "cls_token": None}
231
253
  return self.final_layer(out)
232
254
 
233
255