braindecode 0.4.0__tar.gz → 0.4.3__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-0.4.0 → Braindecode-0.4.3}/Braindecode.egg-info/PKG-INFO +1 -1
- {Braindecode-0.4.0 → Braindecode-0.4.3}/Braindecode.egg-info/SOURCES.txt +1 -1
- {Braindecode-0.4.0 → Braindecode-0.4.3}/PKG-INFO +1 -1
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/base.py +15 -7
- Braindecode-0.4.3/braindecode/models/deep4new.py +228 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/eegnet.py +1 -1
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/hybrid.py +15 -1
- Braindecode-0.4.3/braindecode/version.py +1 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/Cropped_Decoding.ipynb +3 -1
- Braindecode-0.4.0/braindecode/version.py +0 -1
- Braindecode-0.4.0/docs/notebooks/visualization/Perturbation_Old.ipynb +0 -780
- {Braindecode-0.4.0 → Braindecode-0.4.3}/Braindecode.egg-info/dependency_links.txt +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/Braindecode.egg-info/not-zip-safe +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/Braindecode.egg-info/requires.txt +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/Braindecode.egg-info/top_level.txt +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/LICENSE.txt +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/MANIFEST.in +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/README.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datasets/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datasets/bbci.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datasets/bcic_iv_2a.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datasets/multiple.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datasets/sensor_positions.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/iterators.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/signal_target.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/signalproc.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/splitters.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/trial_segment.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/datautil/util.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/experiments/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/experiments/experiment.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/experiments/loggers.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/experiments/monitors.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/experiments/stopcriteria.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/mne_ext/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/mne_ext/signalproc.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/deep4.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/shallow_fbcsp.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/models/util.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/constraints.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/functions.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/init.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/losses.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/modules.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/optimizers.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/schedulers.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/torch_ext/util.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/util.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/visualization/__init__.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/visualization/input_windows.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/visualization/perturbation.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/visualization/plot.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/braindecode/visualization/sinfit.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/Makefile +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/conf.py +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/index.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/BBCI_Data.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/BBCI_Data_Start_Stop.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/BBCI_Data_Trialwise.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/Cropped_Manual_Training_Loop.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/Cropped_Trialwise_Explanation_Figures.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/Trialwise_Decoding.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/Trialwise_Manual_Training_Loop.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/notebooks/visualization/Perturbation.ipynb +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.datasets.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.datautil.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.experiments.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.mne_ext.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.models.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.torch_ext.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/braindecode.visualization.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/docs/source/modules.rst +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/setup.cfg +0 -0
- {Braindecode-0.4.0 → Braindecode-0.4.3}/setup.py +0 -0
|
@@ -33,6 +33,7 @@ braindecode/mne_ext/signalproc.py
|
|
|
33
33
|
braindecode/models/__init__.py
|
|
34
34
|
braindecode/models/base.py
|
|
35
35
|
braindecode/models/deep4.py
|
|
36
|
+
braindecode/models/deep4new.py
|
|
36
37
|
braindecode/models/eegnet.py
|
|
37
38
|
braindecode/models/hybrid.py
|
|
38
39
|
braindecode/models/shallow_fbcsp.py
|
|
@@ -63,7 +64,6 @@ docs/notebooks/Cropped_Trialwise_Explanation_Figures.ipynb
|
|
|
63
64
|
docs/notebooks/Trialwise_Decoding.ipynb
|
|
64
65
|
docs/notebooks/Trialwise_Manual_Training_Loop.ipynb
|
|
65
66
|
docs/notebooks/visualization/Perturbation.ipynb
|
|
66
|
-
docs/notebooks/visualization/Perturbation_Old.ipynb
|
|
67
67
|
docs/source/braindecode.datasets.rst
|
|
68
68
|
docs/source/braindecode.datautil.rst
|
|
69
69
|
docs/source/braindecode.experiments.rst
|
|
@@ -57,7 +57,7 @@ class BaseModel(object):
|
|
|
57
57
|
self.cuda = False
|
|
58
58
|
self.compiled = False
|
|
59
59
|
|
|
60
|
-
def compile(self, loss, optimizer,
|
|
60
|
+
def compile(self, loss, optimizer, extra_monitors=None, cropped=False, iterator_seed=0):
|
|
61
61
|
"""
|
|
62
62
|
Setup training for this model.
|
|
63
63
|
|
|
@@ -66,7 +66,7 @@ class BaseModel(object):
|
|
|
66
66
|
loss: function (predictions, targets) -> torch scalar
|
|
67
67
|
optimizer: `torch.optim.Optimizer` or string
|
|
68
68
|
Either supply an optimizer or the name of the class (e.g. 'adam')
|
|
69
|
-
|
|
69
|
+
extra_monitors: List of Braindecode monitors, optional
|
|
70
70
|
In case you want to monitor additional values except for loss, misclass and runtime.
|
|
71
71
|
cropped: bool
|
|
72
72
|
Whether to perform cropped decoding, see cropped decoding tutorial.
|
|
@@ -84,7 +84,7 @@ class BaseModel(object):
|
|
|
84
84
|
optimizer_class = find_optimizer(optimizer)
|
|
85
85
|
optimizer = optimizer_class(self.network.parameters())
|
|
86
86
|
self.optimizer = optimizer
|
|
87
|
-
self.extra_monitors =
|
|
87
|
+
self.extra_monitors = extra_monitors
|
|
88
88
|
# Already setting it here, so multiple calls to fit
|
|
89
89
|
# will lead to different batches being drawn
|
|
90
90
|
self.seed_rng = RandomState(iterator_seed)
|
|
@@ -93,7 +93,8 @@ class BaseModel(object):
|
|
|
93
93
|
|
|
94
94
|
def fit(self, train_X, train_y, epochs, batch_size, input_time_length=None,
|
|
95
95
|
validation_data=None, model_constraint=None,
|
|
96
|
-
remember_best_column=None, scheduler=None
|
|
96
|
+
remember_best_column=None, scheduler=None,
|
|
97
|
+
log_0_epoch=True):
|
|
97
98
|
"""
|
|
98
99
|
Fit the model using the given training data.
|
|
99
100
|
|
|
@@ -122,6 +123,8 @@ class BaseModel(object):
|
|
|
122
123
|
determines the best epoch.
|
|
123
124
|
scheduler: 'cosine' or None, optional
|
|
124
125
|
Whether to use cosine annealing (:class:`.CosineAnnealing`).
|
|
126
|
+
log_0_epoch: bool
|
|
127
|
+
Whether to compute the metrics once before training as well.
|
|
125
128
|
|
|
126
129
|
Returns
|
|
127
130
|
-------
|
|
@@ -137,6 +140,7 @@ class BaseModel(object):
|
|
|
137
140
|
"which is the number of timesteps that will be pushed through"
|
|
138
141
|
"the network in a single pass.")
|
|
139
142
|
if self.cropped:
|
|
143
|
+
self.network.eval()
|
|
140
144
|
test_input = np_to_var(train_X[0:1], dtype=np.float32)
|
|
141
145
|
while len(test_input.size()) < 4:
|
|
142
146
|
test_input = test_input.unsqueeze(-1)
|
|
@@ -152,7 +156,10 @@ class BaseModel(object):
|
|
|
152
156
|
self.iterator = BalancedBatchSizeIterator(
|
|
153
157
|
batch_size=batch_size,
|
|
154
158
|
seed=self.seed_rng.randint(0, 4294967295))
|
|
155
|
-
|
|
159
|
+
if log_0_epoch:
|
|
160
|
+
stop_criterion = MaxEpochs(epochs)
|
|
161
|
+
else:
|
|
162
|
+
stop_criterion = MaxEpochs(epochs - 1)
|
|
156
163
|
train_set = SignalAndTarget(train_X, train_y)
|
|
157
164
|
optimizer = self.optimizer
|
|
158
165
|
if scheduler is not None:
|
|
@@ -192,7 +199,8 @@ class BaseModel(object):
|
|
|
192
199
|
monitors=self.monitors,
|
|
193
200
|
stop_criterion=stop_criterion,
|
|
194
201
|
remember_best_column=remember_best_column,
|
|
195
|
-
run_after_early_stop=False, cuda=self.cuda,
|
|
202
|
+
run_after_early_stop=False, cuda=self.cuda,
|
|
203
|
+
log_0_epoch=log_0_epoch,
|
|
196
204
|
do_early_stop=(remember_best_column is not None))
|
|
197
205
|
exp.run()
|
|
198
206
|
self.epochs_df = exp.epochs_df
|
|
@@ -237,7 +245,7 @@ class BaseModel(object):
|
|
|
237
245
|
stop_criterion=stop_criterion,
|
|
238
246
|
remember_best_column=None,
|
|
239
247
|
run_after_early_stop=False, cuda=self.cuda,
|
|
240
|
-
log_0_epoch=
|
|
248
|
+
log_0_epoch=True,
|
|
241
249
|
do_early_stop=False)
|
|
242
250
|
|
|
243
251
|
exp.monitor_epoch({'train': train_set})
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from torch import nn
|
|
3
|
+
from torch.nn import init
|
|
4
|
+
from torch.nn.functional import elu
|
|
5
|
+
|
|
6
|
+
from braindecode.models.base import BaseModel
|
|
7
|
+
from braindecode.torch_ext.modules import Expression, AvgPool2dWithConv
|
|
8
|
+
from braindecode.torch_ext.functions import identity
|
|
9
|
+
from braindecode.torch_ext.util import np_to_var
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Deep4NetNew(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Deep ConvNet model from [1]_.
|
|
15
|
+
|
|
16
|
+
References
|
|
17
|
+
----------
|
|
18
|
+
|
|
19
|
+
.. [1] Schirrmeister, R. T., Springenberg, J. T., Fiederer, L. D. J.,
|
|
20
|
+
Glasstetter, M., Eggensperger, K., Tangermann, M., ... & Ball, T. (2017).
|
|
21
|
+
Deep learning with convolutional neural networks for EEG decoding and
|
|
22
|
+
visualization.
|
|
23
|
+
arXiv preprint arXiv:1703.05051.
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, in_chans,
|
|
26
|
+
n_classes,
|
|
27
|
+
input_time_length,
|
|
28
|
+
final_conv_length,
|
|
29
|
+
n_filters_time=25,
|
|
30
|
+
n_filters_spat=25,
|
|
31
|
+
filter_time_length=10,
|
|
32
|
+
pool_time_length=3,
|
|
33
|
+
pool_time_stride=3,
|
|
34
|
+
n_filters_2=50,
|
|
35
|
+
filter_length_2=10,
|
|
36
|
+
n_filters_3=100,
|
|
37
|
+
filter_length_3=10,
|
|
38
|
+
n_filters_4=200,
|
|
39
|
+
filter_length_4=10,
|
|
40
|
+
first_nonlin=elu,
|
|
41
|
+
first_pool_mode='max',
|
|
42
|
+
first_pool_nonlin=identity,
|
|
43
|
+
later_nonlin=elu,
|
|
44
|
+
later_pool_mode='max',
|
|
45
|
+
later_pool_nonlin=identity,
|
|
46
|
+
drop_prob=0.5,
|
|
47
|
+
double_time_convs=False,
|
|
48
|
+
split_first_layer=True,
|
|
49
|
+
batch_norm=True,
|
|
50
|
+
batch_norm_alpha=0.1,
|
|
51
|
+
stride_before_pool=False,
|
|
52
|
+
separable=False):
|
|
53
|
+
if final_conv_length == 'auto':
|
|
54
|
+
assert input_time_length is not None
|
|
55
|
+
|
|
56
|
+
self.__dict__.update(locals())
|
|
57
|
+
del self.self
|
|
58
|
+
|
|
59
|
+
def create_network(self):
|
|
60
|
+
def add_conv_2d(separable, model, name,
|
|
61
|
+
in_filters, out_filters, kernel_size, **kwargs):
|
|
62
|
+
if separable:
|
|
63
|
+
model.add_module(
|
|
64
|
+
name + '_depth',
|
|
65
|
+
nn.Conv2d(
|
|
66
|
+
in_filters, in_filters, kernel_size, groups=in_filters,
|
|
67
|
+
**kwargs))
|
|
68
|
+
model.add_module(
|
|
69
|
+
name + '_point',
|
|
70
|
+
nn.Conv2d(
|
|
71
|
+
in_filters, out_filters, (1, 1), **kwargs))
|
|
72
|
+
else:
|
|
73
|
+
model.add_module(
|
|
74
|
+
name,
|
|
75
|
+
nn.Conv2d(in_filters, out_filters,
|
|
76
|
+
kernel_size, **kwargs))
|
|
77
|
+
|
|
78
|
+
if self.stride_before_pool:
|
|
79
|
+
conv_stride = self.pool_time_stride
|
|
80
|
+
pool_stride = 1
|
|
81
|
+
else:
|
|
82
|
+
conv_stride = 1
|
|
83
|
+
pool_stride = self.pool_time_stride
|
|
84
|
+
pool_class_dict = dict(max=nn.MaxPool2d, mean=AvgPool2dWithConv)
|
|
85
|
+
first_pool_class = pool_class_dict[self.first_pool_mode]
|
|
86
|
+
later_pool_class = pool_class_dict[self.later_pool_mode]
|
|
87
|
+
model = nn.Sequential()
|
|
88
|
+
if self.split_first_layer:
|
|
89
|
+
model.add_module('dimshuffle', Expression(_transpose_time_to_spat))
|
|
90
|
+
model.add_module('conv_time', nn.Conv2d(1, self.n_filters_time,
|
|
91
|
+
(
|
|
92
|
+
self.filter_time_length, 1),
|
|
93
|
+
stride=1, ))
|
|
94
|
+
add_conv_2d(
|
|
95
|
+
self.separable, model, 'conv_spat',
|
|
96
|
+
self.n_filters_time, self.n_filters_spat,
|
|
97
|
+
(1, self.in_chans),
|
|
98
|
+
stride=(conv_stride, 1),
|
|
99
|
+
bias=not self.batch_norm)
|
|
100
|
+
n_filters_conv = self.n_filters_spat
|
|
101
|
+
else:
|
|
102
|
+
add_conv_2d(
|
|
103
|
+
self.separable, model, 'conv_time',
|
|
104
|
+
self.in_chans, self.n_filters_time,
|
|
105
|
+
(self.filter_time_length, 1),
|
|
106
|
+
stride=(conv_stride, 1),
|
|
107
|
+
bias=not self.batch_norm)
|
|
108
|
+
n_filters_conv = self.n_filters_time
|
|
109
|
+
if self.batch_norm:
|
|
110
|
+
model.add_module('bnorm',
|
|
111
|
+
nn.BatchNorm2d(n_filters_conv,
|
|
112
|
+
momentum=self.batch_norm_alpha,
|
|
113
|
+
affine=True,
|
|
114
|
+
eps=1e-5),)
|
|
115
|
+
model.add_module('conv_nonlin', Expression(self.first_nonlin))
|
|
116
|
+
model.add_module('pool',
|
|
117
|
+
first_pool_class(
|
|
118
|
+
kernel_size=(self.pool_time_length, 1),
|
|
119
|
+
stride=(pool_stride, 1)))
|
|
120
|
+
model.add_module('pool_nonlin', Expression(self.first_pool_nonlin))
|
|
121
|
+
|
|
122
|
+
def add_conv_pool_block(model, n_filters_before,
|
|
123
|
+
n_filters, filter_length, block_nr):
|
|
124
|
+
suffix = '_{:d}'.format(block_nr)
|
|
125
|
+
model.add_module('drop' + suffix,
|
|
126
|
+
nn.Dropout(p=self.drop_prob))
|
|
127
|
+
add_conv_2d(
|
|
128
|
+
self.separable, model, 'conv' + suffix.format(block_nr),
|
|
129
|
+
n_filters_before, n_filters,
|
|
130
|
+
(filter_length, 1),
|
|
131
|
+
stride=(conv_stride, 1),
|
|
132
|
+
bias=not self.batch_norm)
|
|
133
|
+
if self.batch_norm:
|
|
134
|
+
model.add_module('bnorm' + suffix,
|
|
135
|
+
nn.BatchNorm2d(n_filters,
|
|
136
|
+
momentum=self.batch_norm_alpha,
|
|
137
|
+
affine=True,
|
|
138
|
+
eps=1e-5))
|
|
139
|
+
model.add_module('nonlin' + suffix,
|
|
140
|
+
Expression(self.later_nonlin))
|
|
141
|
+
|
|
142
|
+
model.add_module('pool' + suffix,
|
|
143
|
+
later_pool_class(
|
|
144
|
+
kernel_size=(self.pool_time_length, 1),
|
|
145
|
+
stride=(pool_stride, 1)))
|
|
146
|
+
model.add_module('pool_nonlin' + suffix,
|
|
147
|
+
Expression(self.later_pool_nonlin))
|
|
148
|
+
|
|
149
|
+
add_conv_pool_block(model, n_filters_conv, self.n_filters_2,
|
|
150
|
+
self.filter_length_2, 2)
|
|
151
|
+
add_conv_pool_block(model, self.n_filters_2, self.n_filters_3,
|
|
152
|
+
self.filter_length_3, 3)
|
|
153
|
+
add_conv_pool_block(model, self.n_filters_3, self.n_filters_4,
|
|
154
|
+
self.filter_length_4, 4)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
model.eval()
|
|
158
|
+
if self.final_conv_length == 'auto':
|
|
159
|
+
out = model(np_to_var(np.ones(
|
|
160
|
+
(1, self.in_chans, self.input_time_length,1),
|
|
161
|
+
dtype=np.float32)))
|
|
162
|
+
n_out_time = out.cpu().data.numpy().shape[2]
|
|
163
|
+
self.final_conv_length = n_out_time
|
|
164
|
+
model.add_module('conv_classifier',
|
|
165
|
+
nn.Conv2d(self.n_filters_4, self.n_classes,
|
|
166
|
+
(self.final_conv_length, 1), bias=True))
|
|
167
|
+
model.add_module('softmax', nn.LogSoftmax(dim=1))
|
|
168
|
+
model.add_module('squeeze', Expression(_squeeze_final_output))
|
|
169
|
+
|
|
170
|
+
# Initialization, xavier is same as in our paper...
|
|
171
|
+
# was default from lasagne
|
|
172
|
+
init.xavier_uniform_(model.conv_time.weight, gain=1)
|
|
173
|
+
# maybe no bias in case of no split layer and batch norm
|
|
174
|
+
if self.split_first_layer or (not self.batch_norm):
|
|
175
|
+
init.constant_(model.conv_time.bias, 0)
|
|
176
|
+
if self.split_first_layer:
|
|
177
|
+
if self.separable:
|
|
178
|
+
init.xavier_uniform_(model.conv_spat_depth.weight, gain=1)
|
|
179
|
+
init.xavier_uniform_(model.conv_spat_point.weight, gain=1)
|
|
180
|
+
else:
|
|
181
|
+
init.xavier_uniform_(model.conv_spat.weight, gain=1)
|
|
182
|
+
if not self.batch_norm:
|
|
183
|
+
if self.separable:
|
|
184
|
+
init.constant_(model.conv_spat_depth.bias, 0)
|
|
185
|
+
init.constant_(model.conv_spat_point.bias, 0)
|
|
186
|
+
else:
|
|
187
|
+
init.constant_(model.conv_spat.bias, 0)
|
|
188
|
+
if self.batch_norm:
|
|
189
|
+
init.constant_(model.bnorm.weight, 1)
|
|
190
|
+
init.constant_(model.bnorm.bias, 0)
|
|
191
|
+
param_dict = dict(list(model.named_parameters()))
|
|
192
|
+
for block_nr in range(2,5):
|
|
193
|
+
suffixes = {True: ['_depth', '_point'], False:['']}[self.separable]
|
|
194
|
+
for suffix in suffixes:
|
|
195
|
+
conv_weight = param_dict['conv_{:d}{:s}.weight'.format(
|
|
196
|
+
block_nr, suffix)]
|
|
197
|
+
init.xavier_uniform_(conv_weight, gain=1)
|
|
198
|
+
if not self.batch_norm:
|
|
199
|
+
for suffix in suffixes:
|
|
200
|
+
conv_bias = param_dict['conv_{:d}{:s}.bias'.format(
|
|
201
|
+
block_nr, suffix)]
|
|
202
|
+
init.constant_(conv_bias, 0)
|
|
203
|
+
else:
|
|
204
|
+
bnorm_weight = param_dict['bnorm_{:d}.weight'.format(block_nr)]
|
|
205
|
+
bnorm_bias = param_dict['bnorm_{:d}.bias'.format(block_nr)]
|
|
206
|
+
init.constant_(bnorm_weight, 1)
|
|
207
|
+
init.constant_(bnorm_bias, 0)
|
|
208
|
+
|
|
209
|
+
init.xavier_uniform_(model.conv_classifier.weight, gain=1)
|
|
210
|
+
init.constant_(model.conv_classifier.bias, 0)
|
|
211
|
+
|
|
212
|
+
# Start in eval mode
|
|
213
|
+
model.eval()
|
|
214
|
+
return model
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# remove empty dim at end and potentially remove empty time dim
|
|
218
|
+
# do not just use squeeze as we never want to remove first dim
|
|
219
|
+
def _squeeze_final_output(x):
|
|
220
|
+
assert x.size()[3] == 1
|
|
221
|
+
x = x[:,:,:,0]
|
|
222
|
+
if x.size()[2] == 1:
|
|
223
|
+
x = x[:,:,0]
|
|
224
|
+
return x
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _transpose_time_to_spat(x):
|
|
228
|
+
return x.permute(0, 3, 2, 1)
|
|
@@ -9,6 +9,20 @@ from braindecode.models.util import to_dense_prediction_model
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class HybridNet(nn.Module, BaseModel):
|
|
12
|
+
"""
|
|
13
|
+
Hybrid ConvNet model from [3]_.
|
|
14
|
+
|
|
15
|
+
Very hardcoded at the moment.
|
|
16
|
+
|
|
17
|
+
References
|
|
18
|
+
----------
|
|
19
|
+
|
|
20
|
+
.. [3] Schirrmeister, R. T., Springenberg, J. T., Fiederer, L. D. J.,
|
|
21
|
+
Glasstetter, M., Eggensperger, K., Tangermann, M., ... & Ball, T. (2017).
|
|
22
|
+
Deep learning with convolutional neural networks for EEG decoding and
|
|
23
|
+
visualization.
|
|
24
|
+
arXiv preprint arXiv:1703.05051.
|
|
25
|
+
"""
|
|
12
26
|
def __init__(self, n_chans, n_classes, input_time_length):
|
|
13
27
|
super(HybridNet, self).__init__()
|
|
14
28
|
deep_model = Deep4Net(n_chans, n_classes, n_filters_time=20,
|
|
@@ -74,4 +88,4 @@ class HybridNet(nn.Module, BaseModel):
|
|
|
74
88
|
linear_out = self.final_conv(merged_out)
|
|
75
89
|
softmaxed = nn.LogSoftmax(dim=1)(linear_out)
|
|
76
90
|
squeezed = softmaxed.squeeze(3)
|
|
77
|
-
return squeezed
|
|
91
|
+
return squeezed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.3"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.0"
|