braindecode 1.6.1.dev1034__tar.gz → 1.6.1.dev1037__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 (172) hide show
  1. {braindecode-1.6.1.dev1034/braindecode.egg-info → braindecode-1.6.1.dev1037}/PKG-INFO +2 -2
  2. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/moabb.py +0 -10
  3. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/__init__.py +3 -2
  4. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/attn_sleep.py +0 -10
  5. braindecode-1.6.1.dev1037/braindecode/models/eegdino.py +533 -0
  6. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegnet.py +1 -10
  7. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/summary.csv +1 -0
  8. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/tsinception.py +1 -11
  9. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/util.py +1 -2
  10. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/__init__.py +2 -1
  11. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/blocks.py +82 -0
  12. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/preprocessing/mne_preprocess.py +28 -1
  13. braindecode-1.6.1.dev1037/braindecode/version.py +1 -0
  14. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037/braindecode.egg-info}/PKG-INFO +2 -2
  15. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode.egg-info/SOURCES.txt +1 -0
  16. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode.egg-info/requires.txt +1 -1
  17. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/api.rst +2 -0
  18. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/index.rst +5 -4
  19. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/whats_new.rst +30 -2
  20. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/pyproject.toml +7 -1
  21. braindecode-1.6.1.dev1034/braindecode/version.py +0 -1
  22. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/LICENSE.txt +0 -0
  23. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/MANIFEST.in +0 -0
  24. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/NOTICE.txt +0 -0
  25. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/README.rst +0 -0
  26. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/__init__.py +0 -0
  27. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/augmentation/__init__.py +0 -0
  28. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/augmentation/base.py +0 -0
  29. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/augmentation/functional.py +0 -0
  30. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/augmentation/transforms.py +0 -0
  31. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/classifier.py +0 -0
  32. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/__init__.py +0 -0
  33. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/base.py +0 -0
  34. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bbci.py +0 -0
  35. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bcicomp.py +0 -0
  36. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/__init__.py +0 -0
  37. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/datasets.py +0 -0
  38. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/format.py +0 -0
  39. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/hub.py +0 -0
  40. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/hub_format.py +0 -0
  41. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/hub_io.py +0 -0
  42. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/hub_validation.py +0 -0
  43. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/bids/iterable.py +0 -0
  44. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/chb_mit.py +0 -0
  45. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/mne.py +0 -0
  46. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/nmt.py +0 -0
  47. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/registry.py +0 -0
  48. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/siena.py +0 -0
  49. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/sleep_physio_challe_18.py +0 -0
  50. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/sleep_physionet.py +0 -0
  51. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/tuh.py +0 -0
  52. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/utils.py +0 -0
  53. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datasets/xy.py +0 -0
  54. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datautil/__init__.py +0 -0
  55. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datautil/channel_utils.py +0 -0
  56. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datautil/hub_formats.py +0 -0
  57. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datautil/serialization.py +0 -0
  58. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/datautil/util.py +0 -0
  59. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/eegneuralnet.py +0 -0
  60. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/functional/__init__.py +0 -0
  61. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/functional/functions.py +0 -0
  62. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/functional/initialization.py +0 -0
  63. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/atcnet.py +0 -0
  64. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/attentionbasenet.py +0 -0
  65. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/base.py +0 -0
  66. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/bendr.py +0 -0
  67. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/biot.py +0 -0
  68. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/brainmodule.py +0 -0
  69. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/cbramod.py +0 -0
  70. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/codebrain.py +0 -0
  71. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/config.py +0 -0
  72. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/contrawr.py +0 -0
  73. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/ctnet.py +0 -0
  74. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/deep4.py +0 -0
  75. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/deepsleepnet.py +0 -0
  76. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/dgcnn.py +0 -0
  77. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegconformer.py +0 -0
  78. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eeginception_erp.py +0 -0
  79. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eeginception_mi.py +0 -0
  80. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegitnet.py +0 -0
  81. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegminer.py +0 -0
  82. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegnex.py +0 -0
  83. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegpt.py +0 -0
  84. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegsimpleconv.py +0 -0
  85. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegsym.py +0 -0
  86. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/eegtcnet.py +0 -0
  87. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/emg2qwerty.py +0 -0
  88. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/fbcnet.py +0 -0
  89. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/fblightconvnet.py +0 -0
  90. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/fbmsnet.py +0 -0
  91. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/hybrid.py +0 -0
  92. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/ifnet.py +0 -0
  93. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/interpolated.py +0 -0
  94. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/labram.py +0 -0
  95. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/luna.py +0 -0
  96. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/medformer.py +0 -0
  97. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/meta_neuromotor.py +0 -0
  98. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/msvtnet.py +0 -0
  99. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/patchedtransformer.py +0 -0
  100. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/reve.py +0 -0
  101. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/sccnet.py +0 -0
  102. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/shallow_fbcsp.py +0 -0
  103. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/signal_jepa.py +0 -0
  104. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/sinc_shallow.py +0 -0
  105. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/sleep_stager_blanco_2020.py +0 -0
  106. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/sleep_stager_chambon_2018.py +0 -0
  107. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/sparcnet.py +0 -0
  108. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/sstdpn.py +0 -0
  109. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/syncnet.py +0 -0
  110. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/tcn.py +0 -0
  111. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/tidnet.py +0 -0
  112. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/models/usleep.py +0 -0
  113. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/activation.py +0 -0
  114. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/attention.py +0 -0
  115. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/convolution.py +0 -0
  116. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/filter.py +0 -0
  117. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/interpolation.py +0 -0
  118. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/layers.py +0 -0
  119. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/linear.py +0 -0
  120. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/parametrization.py +0 -0
  121. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/stats.py +0 -0
  122. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/util.py +0 -0
  123. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/modules/wrapper.py +0 -0
  124. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/preprocessing/__init__.py +0 -0
  125. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/preprocessing/eegprep_preprocess.py +0 -0
  126. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/preprocessing/preprocess.py +0 -0
  127. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/preprocessing/util.py +0 -0
  128. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/preprocessing/windowers.py +0 -0
  129. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/regressor.py +0 -0
  130. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/samplers/__init__.py +0 -0
  131. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/samplers/base.py +0 -0
  132. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/samplers/ssl.py +0 -0
  133. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/training/__init__.py +0 -0
  134. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/training/callbacks.py +0 -0
  135. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/training/losses.py +0 -0
  136. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/training/scoring.py +0 -0
  137. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/util.py +0 -0
  138. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/__init__.py +0 -0
  139. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/attribution.py +0 -0
  140. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/confusion_matrices.py +0 -0
  141. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/frequency.py +0 -0
  142. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/metrics.py +0 -0
  143. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/sanity.py +0 -0
  144. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode/visualization/topology.py +0 -0
  145. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode.egg-info/dependency_links.txt +0 -0
  146. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/braindecode.egg-info/top_level.txt +0 -0
  147. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/Makefile +0 -0
  148. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/_templates/autosummary/class.rst +0 -0
  149. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/_templates/autosummary/class_in_subdir.rst +0 -0
  150. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/_templates/autosummary/function.rst +0 -0
  151. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/_templates/autosummary/function_in_subdir.rst +0 -0
  152. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/cite.rst +0 -0
  153. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/conf.py +0 -0
  154. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/help.rst +0 -0
  155. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/install/install.rst +0 -0
  156. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/install/install_pip.rst +0 -0
  157. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/install/install_source.rst +0 -0
  158. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/attention.rst +0 -0
  159. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/channel.rst +0 -0
  160. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/convolution.rst +0 -0
  161. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/filterbank.rst +0 -0
  162. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/gnn.rst +0 -0
  163. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/interpretable.rst +0 -0
  164. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/lbm.rst +0 -0
  165. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/recurrent.rst +0 -0
  166. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/categorization/spd.rst +0 -0
  167. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/models.rst +0 -0
  168. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/models_categorization.rst +0 -0
  169. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/models_table.rst +0 -0
  170. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/models/models_visualization.rst +0 -0
  171. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/docs/sg_execution_times.rst +0 -0
  172. {braindecode-1.6.1.dev1034 → braindecode-1.6.1.dev1037}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: braindecode
3
- Version: 1.6.1.dev1034
3
+ Version: 1.6.1.dev1037
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>
@@ -46,7 +46,7 @@ Requires-Dist: zarr>=3.0; extra == "hub"
46
46
  Provides-Extra: viz
47
47
  Requires-Dist: captum>=0.7.0; extra == "viz"
48
48
  Provides-Extra: tests
49
- Requires-Dist: pytest; extra == "tests"
49
+ Requires-Dist: pytest<9.1; extra == "tests"
50
50
  Requires-Dist: pytest-cov; extra == "tests"
51
51
  Requires-Dist: codecov; extra == "tests"
52
52
  Requires-Dist: pytest_cases; extra == "tests"
@@ -15,7 +15,6 @@ from typing import Any
15
15
 
16
16
  import mne
17
17
  import pandas as pd
18
- from mne.utils import deprecated
19
18
 
20
19
  from braindecode.util import _update_moabb_docstring
21
20
 
@@ -208,12 +207,3 @@ class HGD(MOABBDataset):
208
207
 
209
208
  def __init__(self, subject_ids):
210
209
  super().__init__("Schirrmeister2017", subject_ids=subject_ids)
211
-
212
-
213
- @deprecated(
214
- "`BNCI2014001` was renamed to `BNCI2014_001` in v1.13; this alias will be removed in v1.14."
215
- )
216
- class BNCI2014001(BNCI2014_001):
217
- """Deprecated alias for BNCI2014001."""
218
-
219
- pass
@@ -15,11 +15,12 @@ from .deep4 import Deep4Net
15
15
  from .deepsleepnet import DeepSleepNet
16
16
  from .dgcnn import DGCNN
17
17
  from .eegconformer import EEGConformer
18
+ from .eegdino import EEGDINO
18
19
  from .eeginception_erp import EEGInceptionERP
19
20
  from .eeginception_mi import EEGInceptionMI
20
21
  from .eegitnet import EEGITNet
21
22
  from .eegminer import EEGMiner
22
- from .eegnet import EEGNet, EEGNetv4
23
+ from .eegnet import EEGNet
23
24
  from .eegnex import EEGNeX
24
25
  from .eegpt import EEGPT, InterpolatedEEGPT
25
26
  from .eegsimpleconv import EEGSimpleConv
@@ -84,6 +85,7 @@ __all__ = [
84
85
  "DeepSleepNet",
85
86
  "BrainModule",
86
87
  "EEGConformer",
88
+ "EEGDINO",
87
89
  "EEGPT",
88
90
  "InterpolatedEEGPT",
89
91
  "EEGInceptionERP",
@@ -91,7 +93,6 @@ __all__ = [
91
93
  "EEGITNet",
92
94
  "EEGMiner",
93
95
  "EEGNet",
94
- "EEGNetv4",
95
96
  "EEGNeX",
96
97
  "EEGSym",
97
98
  "EEGSimpleConv",
@@ -8,7 +8,6 @@ from copy import deepcopy
8
8
 
9
9
  import torch
10
10
  import torch.nn.functional as F
11
- from mne.utils import deprecated
12
11
  from torch import nn
13
12
 
14
13
  from braindecode.models.base import EEGModuleMixin
@@ -538,12 +537,3 @@ class _PositionwiseFeedForward(nn.Module):
538
537
  def forward(self, x: torch.Tensor) -> torch.Tensor:
539
538
  """Implements FFN equation."""
540
539
  return self.w_2(self.dropout(self.activate(self.w_1(x))))
541
-
542
-
543
- @deprecated(
544
- "`SleepStagerEldele2021` was renamed to `AttnSleep` in v1.12 to follow original author's name; this alias will be removed in v1.14."
545
- )
546
- class SleepStagerEldele2021(AttnSleep):
547
- r"""Deprecated alias for SleepStagerEldele2021."""
548
-
549
- pass
@@ -0,0 +1,533 @@
1
+ # Authors: Bruno Aristimunha <b.aristimunha@gmail.com>
2
+ #
3
+ # Code adapted from https://huggingface.co/eegdino/EEG-DINO
4
+ #
5
+ # License: BSD (3-clause)
6
+ from __future__ import annotations
7
+
8
+ from typing import Sequence
9
+ from warnings import warn
10
+
11
+ import torch
12
+ import torch.nn.functional as F
13
+ from torch import nn
14
+
15
+ from braindecode.models.base import EEGModuleMixin
16
+ from braindecode.modules import DropPath, PatchTokenizer
17
+
18
+
19
+ class EEGDINO(EEGModuleMixin, nn.Module):
20
+ r"""EEG-DINO from Wang et al. (2025) [eegdino]_.
21
+
22
+ :bdg-danger:`Foundation Model` :bdg-info:`Attention/Transformer`
23
+
24
+ .. figure:: https://braindecode.org/dev/_static/model/eegdino.png
25
+ :align: center
26
+ :alt: EEG-DINO Architecture
27
+
28
+ EEG-DINO is a ViT-style EEG foundation model pre-trained with DINO-v2
29
+ hierarchical self-distillation. Only the *encoder* (plus a classification
30
+ head) is integrated here; the self-distillation pre-training is out of scope
31
+ and it was not released by the authors.
32
+
33
+ The forward path is, end to end:
34
+
35
+ ``(batch, n_chans, n_times)`` → patchify → time-frequency embedding →
36
+ decoupled positional embedding → transformer encoder (+ global token) →
37
+ pooling head → ``(batch, n_outputs)``.
38
+
39
+ .. rubric:: Step 1 -- Patchify
40
+
41
+ The signal is split along time into non-overlapping patches of
42
+ ``patch_size`` samples (200 samples = 1 second at 200 Hz), giving one
43
+ *token* per (channel, patch). Inputs whose length is not a multiple of
44
+ ``patch_size`` are zero-padded with a warning.
45
+ This is analogous to the patchification in ViT, BEiT (LaBraM), and CBraMod.
46
+
47
+ .. rubric:: Step 2 -- Time-Frequency Embedding (TFE)
48
+
49
+ Each patch is embedded by summing two branches, exactly as in
50
+ :class:`~braindecode.models.CBraMod` (the EEG-DINO paper reuses CBraMod's
51
+ TFE): a **time-domain** branch of stacked grouped convolutions
52
+ (``proj_in``), and a **frequency-domain** branch projecting the magnitude
53
+ of the patch's real FFT (``spectral_proj``). The embedding dimension
54
+ ``emb_dim`` is therefore *derived* from the convolution configuration, not
55
+ set independently.
56
+
57
+ .. rubric:: Step 3 -- Decoupled Positional Embedding (DPE)
58
+
59
+ Where CBraMod uses a single convolutional positional encoding (ACPE),
60
+ EEG-DINO *decouples* space and time and adds both to every token: a
61
+ learnable **one-hot channel embedding** over a fixed vocabulary of
62
+ ``n_channel_embeddings`` slots (input channel ``i`` maps to slot ``i``, so a
63
+ 19-slot embedding serves any ``n_chans <= 19``) and a **depthwise temporal
64
+ convolution** over the patch axis (``time_encoding``).
65
+
66
+ .. rubric:: Step 4 -- Transformer Encoder & Global Token
67
+
68
+ Tokens are flattened to a single sequence and processed by ``n_layer``
69
+ pre-norm transformer blocks (BEiT-style attention with separate query/value
70
+ biases). A learnable ``global_tokens`` summary token is prepended after the
71
+ ``global_token_layer``-th block and attends jointly with the patch tokens.
72
+
73
+ .. rubric:: Step 5 -- Classification Head (``final_layer``)
74
+
75
+ Following EEG-DINO's finetuning head, the patch tokens (global token
76
+ excluded) pass through a per-token projection, are mean-pooled over channels
77
+ then over patches (with an intermediate projection), and a three-layer MLP
78
+ (``final_layer``) maps them to ``n_outputs``
79
+ (:class:`~braindecode.EEGClassifier` applies the softmax). With
80
+ ``return_encoder_output=True`` the mean-pooled encoder representation is
81
+ returned instead (linear probing).
82
+
83
+ .. warning::
84
+ EEG-DINO was pre-trained on EEG in microvolts scaled by ``1 / 100``. The
85
+ model does **not** rescale its input, so for the released weights to
86
+ behave as intended provide data on the same scale (e.g. microvolts
87
+ divided by 100) or normalize your signals consistently.
88
+
89
+ .. important::
90
+ **Pre-trained Weights Available**
91
+
92
+ Small and Medium encoders converted from the released checkpoints are
93
+ hosted on the Hugging Face Hub, one repository per size. Only the encoder
94
+ is pretrained; the classification head is randomly initialized, so
95
+ fine-tune or linear-probe before use::
96
+
97
+ from braindecode.models import EEGDINO
98
+
99
+ model = EEGDINO.from_pretrained(
100
+ "braindecode/eegdino-small-pretrained", # or -medium-pretrained
101
+ n_outputs=6,
102
+ n_chans=19,
103
+ sfreq=200,
104
+ )
105
+
106
+ The Small/Medium/Large architectures are also available in
107
+ :data:`EEGDINO_CONFIGS`. Requires ``braindecode[hub]``.
108
+
109
+ .. versionadded:: 1.6.1
110
+
111
+ Parameters
112
+ ----------
113
+ patch_size : int, default=200
114
+ Temporal patch size in samples (200 = 1 second at 200 Hz). Fixed at 200
115
+ for the released weights (the FFT branch uses ``patch_size // 2 + 1`` bins).
116
+ n_layer : int, default=12
117
+ Number of transformer encoder layers.
118
+ nhead : int, default=8
119
+ Number of attention heads.
120
+ dim_feedforward : int, default=512
121
+ Hidden size of the transformer feed-forward block.
122
+ channels_kernel_stride_padding_norm : sequence of tuple, optional
123
+ Configuration of the time-domain convolutions in the patch embedding,
124
+ as ``(out_channels, kernel, stride, padding, (groups, group_channels))``
125
+ per layer. The embedding dimension is derived from this (see
126
+ :class:`~braindecode.models.CBraMod`). Default is the EEG-DINO-Small /
127
+ CBraMod configuration.
128
+ n_channel_embeddings : int, default=19
129
+ Size of the one-hot channel-embedding vocabulary. ``n_chans`` must not
130
+ exceed it; the first ``n_chans`` slots are used. Default 19 (the released
131
+ montage), so the pretrained weights load for any ``n_chans <= 19``.
132
+ n_global_tokens : int, default=1
133
+ Number of learnable global summary tokens.
134
+ global_token_layer : int, default=1
135
+ 1-based index of the encoder layer after which the global tokens are
136
+ inserted.
137
+ activation : type[nn.Module], default=nn.GELU
138
+ Activation function used in the transformer feed-forward blocks and the
139
+ classification head.
140
+ patch_activation : type[nn.Module], default=nn.GELU
141
+ Activation function used in the patch-embedding convolutions. Defaults to
142
+ ``nn.GELU`` to match the released pretrained weights.
143
+ drop_prob : float, default=0.1
144
+ Dropout / stochastic-depth probability in the encoder.
145
+ return_features : bool, default=False
146
+ If True, ``forward`` returns ``{"features", "cls_token"}``.
147
+ return_encoder_output : bool, default=False
148
+ If True, ``final_layer`` is :class:`~torch.nn.Identity` and ``forward``
149
+ returns the pooled encoder representation (linear probing).
150
+
151
+ References
152
+ ----------
153
+ .. [eegdino] Wang, X., Liu, X., Liu, X., Si, Q., Xu, Z., Li, Y., & Zhen, X.
154
+ (2025). EEG-DINO: Learning EEG Foundation Models via Hierarchical
155
+ Self-Distillation. In Medical Image Computing and Computer Assisted
156
+ Intervention (MICCAI 2025).
157
+ """
158
+
159
+ def __init__(
160
+ self,
161
+ n_outputs=None,
162
+ n_chans=None,
163
+ chs_info=None,
164
+ n_times=None,
165
+ input_window_seconds=None,
166
+ sfreq=None,
167
+ patch_size: int = 200,
168
+ n_layer: int = 12,
169
+ nhead: int = 8,
170
+ dim_feedforward: int = 512,
171
+ channels_kernel_stride_padding_norm: Sequence[
172
+ tuple[int, int, int, int, tuple[int, int]]
173
+ ] = (
174
+ (25, 49, 25, 24, (5, 25)),
175
+ (25, 3, 1, 1, (5, 25)),
176
+ (25, 3, 1, 1, (5, 25)),
177
+ ),
178
+ n_channel_embeddings: int = 19,
179
+ n_global_tokens: int = 1,
180
+ global_token_layer: int = 1,
181
+ activation: type[nn.Module] = nn.GELU,
182
+ patch_activation: type[nn.Module] = nn.GELU,
183
+ drop_prob: float = 0.1,
184
+ return_features: bool = False,
185
+ return_encoder_output: bool = False,
186
+ ):
187
+ super().__init__(
188
+ n_outputs=n_outputs,
189
+ n_chans=n_chans,
190
+ chs_info=chs_info,
191
+ n_times=n_times,
192
+ input_window_seconds=input_window_seconds,
193
+ sfreq=sfreq,
194
+ )
195
+ del n_outputs, n_chans, chs_info, n_times, input_window_seconds, sfreq
196
+
197
+ if not 1 <= global_token_layer <= n_layer:
198
+ raise ValueError(
199
+ f"global_token_layer must be in [1, n_layer={n_layer}], "
200
+ f"got {global_token_layer}."
201
+ )
202
+ if n_global_tokens < 1:
203
+ raise ValueError(f"n_global_tokens must be >= 1, got {n_global_tokens}.")
204
+ if self.n_chans > n_channel_embeddings:
205
+ raise ValueError(
206
+ f"n_chans ({self.n_chans}) must not exceed n_channel_embeddings "
207
+ f"({n_channel_embeddings}); the released weights use 19."
208
+ )
209
+
210
+ if self._sfreq is not None and self.sfreq != 200:
211
+ warn(
212
+ f"EEG-DINO was trained at 200 Hz but sfreq={self.sfreq}. Inputs are "
213
+ "not resampled internally; results may be unreliable.",
214
+ UserWarning,
215
+ )
216
+
217
+ self.tokenizer = PatchTokenizer(
218
+ patch_size, n_times=self.n_times, learnable=False
219
+ )
220
+ self.n_global_tokens = n_global_tokens
221
+ self.global_token_layer = global_token_layer
222
+ self.activation = activation
223
+ self.return_features = return_features
224
+ self.return_encoder_output = return_encoder_output
225
+
226
+ self.patch_embedding = _PatchEmbedding(
227
+ patch_size=patch_size,
228
+ channels_kernel_stride_padding_norm=channels_kernel_stride_padding_norm,
229
+ n_channel_embeddings=n_channel_embeddings,
230
+ activation=patch_activation,
231
+ drop_prob=drop_prob,
232
+ )
233
+ self.emb_dim = self.patch_embedding.emb_dim
234
+
235
+ self.encoder_layers = nn.ModuleList(
236
+ _TransformerEncoderLayer(
237
+ self.emb_dim, nhead, dim_feedforward, activation, drop_prob
238
+ )
239
+ for _ in range(n_layer)
240
+ )
241
+ self.global_tokens = nn.Parameter(torch.zeros(1, n_global_tokens, self.emb_dim))
242
+
243
+ self.final_layer = self._make_head()
244
+
245
+ self._init_weights()
246
+
247
+ def _init_weights(self):
248
+ nn.init.trunc_normal_(self.global_tokens, std=0.02)
249
+
250
+ def _make_head(self):
251
+ if self.return_encoder_output:
252
+ return nn.Identity() # ``n_outputs`` not needed for linear probing
253
+ return _ClassificationHead(self.emb_dim, self.n_outputs, self.activation)
254
+
255
+ def reset_head(self, n_outputs):
256
+ """Replace ``final_layer`` with a fresh classification head for ``n_outputs``.
257
+
258
+ Asking for a head implies a classification model, so this also clears
259
+ ``return_encoder_output`` (a linear-probe model thereby gains a real head),
260
+ mirroring :meth:`braindecode.models.CBraMod.reset_head`.
261
+ """
262
+ self._n_outputs = n_outputs
263
+ self.return_encoder_output = False
264
+ self.final_layer = self._make_head()
265
+
266
+ def forward(self, x, return_features: bool | None = None):
267
+ """Forward pass.
268
+
269
+ Parameters
270
+ ----------
271
+ x : torch.Tensor
272
+ Input of shape ``(batch, n_chans, n_times)``. The model does not
273
+ rescale ``x`` (see the amplitude-scale warning in the class docstring).
274
+ return_features : bool, optional
275
+ Overrides ``self.return_features`` for this call.
276
+
277
+ Returns
278
+ -------
279
+ torch.Tensor or dict
280
+ Logits ``(batch, n_outputs)``; or, with features,
281
+ ``{"features": (batch, n_chans * n_patches, emb_dim),
282
+ "cls_token": (batch, emb_dim)}``.
283
+ """
284
+ if return_features is None:
285
+ return_features = self.return_features
286
+ x = self.tokenizer(x) # (batch, n_chans, n_patches, patch_size)
287
+
288
+ patch_emb = self.patch_embedding(x) # (batch, n_chans, n_patches, emb_dim)
289
+ n_chans = patch_emb.shape[1]
290
+ tokens = patch_emb.flatten(1, 2) # (batch, chans * patches, emb)
291
+
292
+ global_tokens = self.global_tokens.expand(tokens.shape[0], -1, -1)
293
+ for i, layer in enumerate(self.encoder_layers):
294
+ tokens = layer(tokens)
295
+ if i + 1 == self.global_token_layer:
296
+ tokens = torch.cat([global_tokens, tokens], dim=1)
297
+
298
+ global_out = tokens[:, : self.n_global_tokens]
299
+ patch_out = tokens[:, self.n_global_tokens :]
300
+
301
+ if return_features:
302
+ return {"features": patch_out, "cls_token": global_out.mean(dim=1)}
303
+
304
+ if self.return_encoder_output:
305
+ return patch_out.mean(dim=1) # pooled encoder features (linear probing)
306
+ return self.final_layer(patch_out, n_chans)
307
+
308
+
309
+ #: Architecture presets. Small and Medium have released weights; Large does not.
310
+ EEGDINO_CONFIGS = {
311
+ "small": dict(
312
+ n_layer=12,
313
+ nhead=8,
314
+ dim_feedforward=512,
315
+ channels_kernel_stride_padding_norm=(
316
+ (25, 49, 25, 24, (5, 25)),
317
+ (25, 3, 1, 1, (5, 25)),
318
+ (25, 3, 1, 1, (5, 25)),
319
+ ),
320
+ ),
321
+ "medium": dict(
322
+ n_layer=16,
323
+ nhead=8,
324
+ dim_feedforward=1024,
325
+ channels_kernel_stride_padding_norm=(
326
+ (64, 49, 25, 24, (8, 64)),
327
+ (128, 3, 1, 1, (8, 128)),
328
+ (64, 3, 1, 1, (8, 64)),
329
+ ),
330
+ ),
331
+ "large": dict(
332
+ n_layer=24,
333
+ nhead=8,
334
+ dim_feedforward=2048,
335
+ channels_kernel_stride_padding_norm=(
336
+ (128, 49, 25, 24, (16, 128)),
337
+ (256, 3, 1, 1, (16, 256)),
338
+ (128, 3, 1, 1, (16, 128)),
339
+ ),
340
+ ),
341
+ }
342
+
343
+
344
+ class _PatchEmbedding(nn.Module):
345
+ """Time-frequency patch embedding with a decoupled positional embedding.
346
+
347
+ The time-domain (``proj_in``) and frequency-domain (``spectral_proj``)
348
+ branches are CBraMod's TFE (see :class:`~braindecode.models.CBraMod`); the
349
+ one-hot ``channel_embedding`` and depthwise ``time_encoding`` are EEG-DINO's
350
+ decoupled positional embedding.
351
+ """
352
+
353
+ def __init__(
354
+ self,
355
+ patch_size,
356
+ channels_kernel_stride_padding_norm,
357
+ n_channel_embeddings,
358
+ activation=nn.GELU,
359
+ drop_prob=0.1,
360
+ ):
361
+ super().__init__()
362
+ self.patch_size = patch_size
363
+ self.channels_kernel_stride_padding_norm = channels_kernel_stride_padding_norm
364
+ self.n_channel_embeddings = n_channel_embeddings
365
+
366
+ in_channels, conv_layers = 1, []
367
+ for (
368
+ out_channels,
369
+ kernel,
370
+ stride,
371
+ padding,
372
+ norm,
373
+ ) in channels_kernel_stride_padding_norm:
374
+ conv_layers += [
375
+ nn.Conv2d(
376
+ in_channels, out_channels, (1, kernel), (1, stride), (0, padding)
377
+ ),
378
+ nn.GroupNorm(*norm),
379
+ activation(),
380
+ ]
381
+ in_channels = out_channels
382
+ self.proj_in = nn.Sequential(*conv_layers)
383
+
384
+ self.spectral_proj = nn.Sequential(
385
+ nn.Linear(patch_size // 2 + 1, self.emb_dim), nn.Dropout(drop_prob)
386
+ )
387
+ self.channel_embedding = nn.Linear(n_channel_embeddings, self.emb_dim)
388
+ # Sequential wrapper keeps the released-checkpoint key ``time_encoding.0.*``.
389
+ self.time_encoding = nn.Sequential(
390
+ nn.Conv2d(
391
+ self.emb_dim, self.emb_dim, (1, 5), (1, 1), (0, 2), groups=self.emb_dim
392
+ )
393
+ )
394
+
395
+ @property
396
+ def emb_dim(self):
397
+ """Embedding dimension implied by the convolution configuration."""
398
+ reduced = self.patch_size
399
+ for _, kernel, stride, padding, _ in self.channels_kernel_stride_padding_norm:
400
+ reduced = (reduced + 2 * padding - kernel) // stride + 1
401
+ return self.channels_kernel_stride_padding_norm[-1][0] * reduced
402
+
403
+ def forward(self, x):
404
+ # x: (batch, n_chans, n_patches, patch_size)
405
+ n_chans = x.shape[1]
406
+
407
+ # Time-frequency embedding (CBraMod TFE): conv branch + FFT-magnitude branch.
408
+ time_tokens = self.proj_in(x.flatten(1, 2).unsqueeze(1))
409
+ batch, conv_dim, _, conv_len = time_tokens.shape
410
+ time_tokens = (
411
+ time_tokens.reshape(batch, conv_dim, n_chans, -1, conv_len)
412
+ .permute(0, 2, 3, 1, 4)
413
+ .flatten(3, 4)
414
+ )
415
+ spectrum = torch.fft.rfft(x, dim=-1, norm="forward").abs()
416
+ patch_emb = time_tokens + self.spectral_proj(spectrum)
417
+
418
+ # Decoupled positional embedding: one-hot channel + depthwise temporal conv.
419
+ # The one-hot uses the first ``n_chans`` of the ``n_channel_embeddings``
420
+ # slots, so the released 19-slot embedding serves any n_chans <= 19.
421
+ channel_ids = torch.arange(n_chans, device=x.device)
422
+ one_hot = F.one_hot(channel_ids, self.n_channel_embeddings).to(
423
+ self.channel_embedding.weight.dtype
424
+ )
425
+ channel_emb = self.channel_embedding(one_hot)
426
+ patch_emb = patch_emb + channel_emb.unsqueeze(0).unsqueeze(2)
427
+ temporal_emb = self.time_encoding(patch_emb.permute(0, 3, 1, 2))
428
+ return patch_emb + temporal_emb.permute(0, 2, 3, 1)
429
+
430
+
431
+ class _TransformerEncoderLayer(nn.Module):
432
+ """Pre-norm transformer block (no LayerScale, matching the released config)."""
433
+
434
+ def __init__(
435
+ self, emb_dim, nhead, dim_feedforward, activation=nn.GELU, drop_prob=0.1
436
+ ):
437
+ super().__init__()
438
+ self.norm1 = nn.LayerNorm(emb_dim)
439
+ self.attn = _Attention(emb_dim, nhead, attn_drop=drop_prob, proj_drop=drop_prob)
440
+ self.drop_path = DropPath(drop_prob) if drop_prob > 0.0 else nn.Identity()
441
+ self.norm2 = nn.LayerNorm(emb_dim)
442
+ self.mlp = _MLP(emb_dim, dim_feedforward, activation=activation, drop=drop_prob)
443
+
444
+ def forward(self, x):
445
+ x = x + self.drop_path(self.attn(self.norm1(x)))
446
+ x = x + self.drop_path(self.mlp(self.norm2(x)))
447
+ return x
448
+
449
+
450
+ class _Attention(nn.Module):
451
+ """BEiT-style attention: fused ``qkv`` without bias plus separate q/v biases.
452
+
453
+ ``head_dim`` is decoupled from ``emb_dim`` (``all_head_dim = head_dim *
454
+ nhead``) so a custom ``nhead`` need not divide ``emb_dim``. For the released
455
+ presets ``nhead=8`` divides every ``emb_dim`` (200/512/1024), so this is a
456
+ no-op there and the released ``qkv``/``proj`` shapes are preserved.
457
+ """
458
+
459
+ def __init__(self, emb_dim, nhead=8, attn_drop=0.0, proj_drop=0.0):
460
+ super().__init__()
461
+ if not 1 <= nhead <= emb_dim:
462
+ raise ValueError(
463
+ f"nhead ({nhead}) must be in [1, emb_dim={emb_dim}] so head_dim >= 1."
464
+ )
465
+ self.nhead = nhead
466
+ head_dim = emb_dim // nhead
467
+ all_head_dim = head_dim * nhead
468
+ self.scale = head_dim**-0.5
469
+ self.qkv = nn.Linear(emb_dim, all_head_dim * 3, bias=False)
470
+ self.q_bias = nn.Parameter(torch.zeros(all_head_dim))
471
+ self.v_bias = nn.Parameter(torch.zeros(all_head_dim))
472
+ self.attn_drop = nn.Dropout(attn_drop)
473
+ self.proj = nn.Linear(all_head_dim, emb_dim)
474
+ self.proj_drop = nn.Dropout(proj_drop)
475
+
476
+ def forward(self, x):
477
+ bias = torch.cat((self.q_bias, torch.zeros_like(self.v_bias), self.v_bias))
478
+ qkv = F.linear(x, self.qkv.weight, bias)
479
+ batch, tokens, _ = qkv.shape
480
+ query, key, value = (
481
+ qkv.reshape(batch, tokens, 3, self.nhead, -1)
482
+ .permute(2, 0, 3, 1, 4)
483
+ .unbind(0)
484
+ )
485
+ attn = (query * self.scale @ key.transpose(-2, -1)).softmax(dim=-1)
486
+ attn = self.attn_drop(attn)
487
+ out = (attn @ value).transpose(1, 2).flatten(2)
488
+ return self.proj_drop(self.proj(out))
489
+
490
+
491
+ class _MLP(nn.Module):
492
+ """Feed-forward block; ``fc1``/``fc2`` names match the released checkpoint."""
493
+
494
+ def __init__(self, emb_dim, hidden_dim, activation=nn.GELU, drop=0.0):
495
+ super().__init__()
496
+ self.fc1 = nn.Linear(emb_dim, hidden_dim)
497
+ self.act = activation()
498
+ self.fc2 = nn.Linear(hidden_dim, emb_dim)
499
+ self.drop = nn.Dropout(drop)
500
+
501
+ def forward(self, x):
502
+ return self.drop(self.fc2(self.act(self.fc1(x))))
503
+
504
+
505
+ class _ClassificationHead(nn.Module):
506
+ """EEG-DINO finetuning head (Wang et al., 2025).
507
+
508
+ Applies a per-token projection, mean-pools the patch tokens over channels
509
+ then over patches (with an intermediate projection), and maps the pooled
510
+ ``emb_dim`` vector to ``n_outputs`` with a three-layer MLP.
511
+ """
512
+
513
+ def __init__(
514
+ self, emb_dim, n_outputs, activation=nn.GELU, drop_prob_1=0.5, drop_prob_2=0.3
515
+ ):
516
+ super().__init__()
517
+ self.token_proj = nn.Sequential(nn.Linear(emb_dim, emb_dim), activation())
518
+ self.time_proj = nn.Sequential(nn.Linear(emb_dim, emb_dim), activation())
519
+ self.classifier = nn.Sequential(
520
+ nn.Linear(emb_dim, emb_dim // 2),
521
+ activation(),
522
+ nn.Dropout(drop_prob_1),
523
+ nn.Linear(emb_dim // 2, emb_dim // 4),
524
+ activation(),
525
+ nn.Dropout(drop_prob_2),
526
+ nn.Linear(emb_dim // 4, n_outputs),
527
+ )
528
+
529
+ def forward(self, patch_tokens, n_chans):
530
+ x = self.token_proj(patch_tokens)
531
+ x = x.reshape(x.shape[0], n_chans, -1, x.shape[2]).mean(dim=1)
532
+ x = self.time_proj(x).mean(dim=1)
533
+ return self.classifier(x)
@@ -6,7 +6,7 @@ from __future__ import annotations
6
6
  from typing import Dict, Optional
7
7
 
8
8
  from einops.layers.torch import Rearrange
9
- from mne.utils import deprecated, warn
9
+ from mne.utils import warn
10
10
  from torch import nn
11
11
 
12
12
  from braindecode.functional import glorot_weight_zero_bias
@@ -348,12 +348,3 @@ class EEGNet(EEGModuleMixin, nn.Sequential):
348
348
  self.add_module("final_layer", module)
349
349
 
350
350
  glorot_weight_zero_bias(self)
351
-
352
-
353
- @deprecated(
354
- "`EEGNetv4` was renamed to `EEGNet` in v1.12; this alias will be removed in v1.14."
355
- )
356
- class EEGNetv4(EEGNet):
357
- r"""Deprecated alias for EEGNet."""
358
-
359
- pass
@@ -56,3 +56,4 @@ InterpolatedBIOT,"Sleep Staging, Epilepsy",Classification,200,"chs_info, n_outpu
56
56
  InterpolatedEEGPT,General,Classification,256,"chs_info, n_outputs, n_times",25323137,"InterpolatedEEGPT(chs_info=<user>, n_outputs=4, n_times=1024)","Foundation Model,Attention/Transformer,Channel"
57
57
  InterpolatedLaBraM,General,"Classification, Embedding",200,"chs_info, n_outputs, n_times",5866196,"InterpolatedLaBraM(chs_info=<user>, n_outputs=4, n_times=1000)","Convolution,Foundation Model,Channel"
58
58
  InterpolatedSignalJEPA,"Motor Imagery, ERP, SSVEP",Embedding,128,"chs_info",3456882,"InterpolatedSignalJEPA(chs_info=<user>)","Convolution,Channel,Foundation Model"
59
+ EEGDINO,General,Classification,200,"n_chans, n_outputs, n_times",4539698,"EEGDINO(n_chans=19, n_outputs=4, n_times=1000)","Foundation Model,Attention/Transformer"
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
  import torch
8
8
  import torch.nn as nn
9
9
  from einops.layers.torch import Rearrange
10
- from mne.utils import deprecated, warn
10
+ from mne.utils import warn
11
11
 
12
12
  from braindecode.models.base import EEGModuleMixin
13
13
 
@@ -283,13 +283,3 @@ class TSception(EEGModuleMixin, nn.Module):
283
283
  activation(),
284
284
  nn.AvgPool2d(kernel_size=(1, pool_size), stride=(1, pool_size)),
285
285
  )
286
-
287
-
288
- @deprecated(
289
- "`TSceptionV1` was renamed to `TSception` in v1.12; "
290
- "this alias will be removed in v1.14."
291
- )
292
- class TSceptionV1(TSception):
293
- r"""Deprecated alias for TSception."""
294
-
295
- pass
@@ -189,8 +189,6 @@ def _init_models_dict():
189
189
  issubclass(m[1], models.base.EEGModuleMixin)
190
190
  and m[1] != models.base.EEGModuleMixin
191
191
  ):
192
- if m[1].__name__ == "EEGNetv4":
193
- continue
194
192
  models_dict[m[0]] = m[1]
195
193
 
196
194
 
@@ -434,6 +432,7 @@ models_mandatory_parameters: list[
434
432
  {"n_chans": 19, "n_times": 6000},
435
433
  ),
436
434
  ("DGCNN", ["n_chans", "n_outputs", "n_times", "chs_info"], None),
435
+ ("EEGDINO", ["n_chans", "n_outputs", "n_times"], None),
437
436
  ]
438
437
 
439
438
  ################################################################