braindecode 0.5.1__py3-none-any.whl
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.5.1.dist-info/LICENSE.txt +31 -0
- Braindecode-0.5.1.dist-info/METADATA +133 -0
- Braindecode-0.5.1.dist-info/RECORD +69 -0
- Braindecode-0.5.1.dist-info/WHEEL +5 -0
- Braindecode-0.5.1.dist-info/top_level.txt +2 -0
- braindecode/__init__.py +4 -0
- braindecode/classifier.py +258 -0
- braindecode/datasets/__init__.py +7 -0
- braindecode/datasets/base.py +293 -0
- braindecode/datasets/bbci.py +679 -0
- braindecode/datasets/moabb.py +110 -0
- braindecode/datasets/sleep_physionet.py +103 -0
- braindecode/datasets/tuh.py +230 -0
- braindecode/datautil/__init__.py +11 -0
- braindecode/datautil/mne.py +136 -0
- braindecode/datautil/preprocess.py +364 -0
- braindecode/datautil/serialization.py +111 -0
- braindecode/datautil/util.py +36 -0
- braindecode/datautil/windowers.py +435 -0
- braindecode/datautil/xy.py +77 -0
- braindecode/models/__init__.py +12 -0
- braindecode/models/deep4.py +252 -0
- braindecode/models/eegnet.py +339 -0
- braindecode/models/eegresnet.py +232 -0
- braindecode/models/functions.py +43 -0
- braindecode/models/hybrid.py +103 -0
- braindecode/models/modules.py +150 -0
- braindecode/models/shallow_fbcsp.py +158 -0
- braindecode/models/sleep_stager_chambon_2018.py +113 -0
- braindecode/models/tcn.py +143 -0
- braindecode/models/util.py +66 -0
- braindecode/regressor.py +240 -0
- braindecode/samplers/__init__.py +5 -0
- braindecode/samplers/base.py +78 -0
- braindecode/samplers/ssl.py +118 -0
- braindecode/training/__init__.py +8 -0
- braindecode/training/callbacks.py +21 -0
- braindecode/training/losses.py +17 -0
- braindecode/training/scoring.py +296 -0
- braindecode/util.py +362 -0
- braindecode/version.py +1 -0
- braindecode/visualization/__init__.py +5 -0
- braindecode/visualization/gradients.py +53 -0
- test/__init__.py +0 -0
- test/acceptance_tests/__init__.py +0 -0
- test/acceptance_tests/test_cropped_decoding.py +171 -0
- test/acceptance_tests/test_eeg_classifier.py +255 -0
- test/acceptance_tests/test_model_architectures.py +54 -0
- test/acceptance_tests/test_trialwise_decoding.py +168 -0
- test/unit_tests/__init__.py +0 -0
- test/unit_tests/datasets/__init__.py +0 -0
- test/unit_tests/datasets/test_dataset.py +227 -0
- test/unit_tests/datasets/test_mne.py +145 -0
- test/unit_tests/datasets/test_sleep_physionet.py +23 -0
- test/unit_tests/datasets/test_tuh.py +97 -0
- test/unit_tests/datasets/test_xy.py +31 -0
- test/unit_tests/datautil/__init__.py +0 -0
- test/unit_tests/datautil/test_preprocess.py +270 -0
- test/unit_tests/datautil/test_serialization.py +127 -0
- test/unit_tests/datautil/test_windowers.py +519 -0
- test/unit_tests/models/__init__.py +0 -0
- test/unit_tests/models/test_models.py +136 -0
- test/unit_tests/models/test_util.py +22 -0
- test/unit_tests/test_eeg_classifier.py +73 -0
- test/unit_tests/test_eeg_regressor.py +72 -0
- test/unit_tests/test_scoring.py +282 -0
- test/unit_tests/test_util.py +30 -0
- test/unit_tests/visualization/__init__.py +0 -0
- test/unit_tests/visualization/test_gradients.py +38 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Authors: Maciej Sliwowski
|
|
2
|
+
# Robin Tibor Schirrmeister
|
|
3
|
+
# Lukas Gemein
|
|
4
|
+
#
|
|
5
|
+
# License: BSD-3
|
|
6
|
+
|
|
7
|
+
import mne
|
|
8
|
+
import numpy as np
|
|
9
|
+
from mne.io import concatenate_raws
|
|
10
|
+
from skorch.helper import predefined_split
|
|
11
|
+
from torch import optim
|
|
12
|
+
from torch.nn.functional import nll_loss
|
|
13
|
+
|
|
14
|
+
from braindecode.classifier import EEGClassifier
|
|
15
|
+
from braindecode.datautil.xy import create_from_X_y
|
|
16
|
+
from braindecode.training.losses import CroppedLoss
|
|
17
|
+
from braindecode.models import ShallowFBCSPNet
|
|
18
|
+
from braindecode.models.util import to_dense_prediction_model
|
|
19
|
+
from braindecode.training.scoring import CroppedTrialEpochScoring
|
|
20
|
+
from braindecode.util import set_random_seeds, np_to_var
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def assert_deep_allclose(expected, actual, *args, **kwargs):
|
|
24
|
+
"""
|
|
25
|
+
Assert that two complex structures have almost equal contents.
|
|
26
|
+
|
|
27
|
+
Compares lists, dicts and tuples recursively. Checks numeric values
|
|
28
|
+
using test_case's :py:meth:`unittest.TestCase.assertAlmostEqual` and
|
|
29
|
+
checks all other values with :py:meth:`unittest.TestCase.assertEqual`.
|
|
30
|
+
Accepts additional positional and keyword arguments and pass those
|
|
31
|
+
intact to assertAlmostEqual() (that's how you specify comparison
|
|
32
|
+
precision).
|
|
33
|
+
"""
|
|
34
|
+
is_root = not "__trace" in kwargs
|
|
35
|
+
trace = kwargs.pop("__trace", "ROOT")
|
|
36
|
+
try:
|
|
37
|
+
if isinstance(expected, (int, float, complex)):
|
|
38
|
+
np.testing.assert_allclose(expected, actual, *args, **kwargs)
|
|
39
|
+
elif isinstance(expected, (list, tuple, np.ndarray)):
|
|
40
|
+
assert len(expected) == len(actual)
|
|
41
|
+
for index in range(len(expected)):
|
|
42
|
+
v1, v2 = expected[index], actual[index]
|
|
43
|
+
assert_deep_allclose(
|
|
44
|
+
v1, v2, __trace=repr(index), *args, **kwargs
|
|
45
|
+
)
|
|
46
|
+
elif isinstance(expected, dict):
|
|
47
|
+
assert set(expected) == set(actual)
|
|
48
|
+
for key in expected:
|
|
49
|
+
assert_deep_allclose(
|
|
50
|
+
expected[key],
|
|
51
|
+
actual[key],
|
|
52
|
+
__trace=repr(key),
|
|
53
|
+
*args,
|
|
54
|
+
**kwargs
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
assert expected == actual
|
|
58
|
+
except AssertionError as exc:
|
|
59
|
+
exc.__dict__.setdefault("traces", []).append(trace)
|
|
60
|
+
msg = (
|
|
61
|
+
exc.message
|
|
62
|
+
if hasattr(exc, "message")
|
|
63
|
+
else exc.args[0]
|
|
64
|
+
if exc.args
|
|
65
|
+
else ""
|
|
66
|
+
)
|
|
67
|
+
if is_root:
|
|
68
|
+
trace = " -> ".join(reversed(exc.traces))
|
|
69
|
+
exc = AssertionError("%s\nTRACE: %s" % (msg, trace))
|
|
70
|
+
raise exc
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_eeg_classifier():
|
|
74
|
+
# 5,6,7,10,13,14 are codes for executed and imagined hands/feet
|
|
75
|
+
subject_id = 1
|
|
76
|
+
event_codes = [5, 6, 9, 10, 13, 14]
|
|
77
|
+
|
|
78
|
+
# This will download the files if you don't have them yet,
|
|
79
|
+
# and then return the paths to the files.
|
|
80
|
+
physionet_paths = mne.datasets.eegbci.load_data(
|
|
81
|
+
subject_id, event_codes, update_path=False
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Load each of the files
|
|
85
|
+
parts = [
|
|
86
|
+
mne.io.read_raw_edf(
|
|
87
|
+
path, preload=True, stim_channel="auto", verbose="WARNING"
|
|
88
|
+
)
|
|
89
|
+
for path in physionet_paths
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# Concatenate them
|
|
93
|
+
raw = concatenate_raws(parts)
|
|
94
|
+
|
|
95
|
+
# Find the events in this dataset
|
|
96
|
+
events, _ = mne.events_from_annotations(raw)
|
|
97
|
+
|
|
98
|
+
# Use only EEG channels
|
|
99
|
+
eeg_channel_inds = mne.pick_types(
|
|
100
|
+
raw.info, meg=False, eeg=True, stim=False, eog=False, exclude="bads"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Extract trials, only using EEG channels
|
|
104
|
+
epoched = mne.Epochs(
|
|
105
|
+
raw,
|
|
106
|
+
events,
|
|
107
|
+
dict(hands=2, feet=3),
|
|
108
|
+
tmin=1,
|
|
109
|
+
tmax=4.1,
|
|
110
|
+
proj=False,
|
|
111
|
+
picks=eeg_channel_inds,
|
|
112
|
+
baseline=None,
|
|
113
|
+
preload=True,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Convert data from volt to millivolt
|
|
117
|
+
# Pytorch expects float32 for input and int64 for labels.
|
|
118
|
+
X = (epoched.get_data() * 1e6).astype(np.float32)
|
|
119
|
+
y = (epoched.events[:, 2] - 2).astype(np.int64) # 2,3 -> 0,1
|
|
120
|
+
|
|
121
|
+
# Set if you want to use GPU
|
|
122
|
+
# You can also use torch.cuda.is_available() to determine if cuda is available on your machine.
|
|
123
|
+
cuda = False
|
|
124
|
+
set_random_seeds(seed=20170629, cuda=cuda)
|
|
125
|
+
|
|
126
|
+
# This will determine how many crops are processed in parallel
|
|
127
|
+
input_window_samples = 450
|
|
128
|
+
n_classes = 2
|
|
129
|
+
in_chans = X.shape[1]
|
|
130
|
+
# final_conv_length determines the size of the receptive field of the ConvNet
|
|
131
|
+
model = ShallowFBCSPNet(
|
|
132
|
+
in_chans=in_chans,
|
|
133
|
+
n_classes=n_classes,
|
|
134
|
+
input_window_samples=input_window_samples,
|
|
135
|
+
final_conv_length=12,
|
|
136
|
+
)
|
|
137
|
+
to_dense_prediction_model(model)
|
|
138
|
+
|
|
139
|
+
if cuda:
|
|
140
|
+
model.cuda()
|
|
141
|
+
|
|
142
|
+
# determine output size
|
|
143
|
+
test_input = np_to_var(
|
|
144
|
+
np.ones((2, in_chans, input_window_samples, 1), dtype=np.float32)
|
|
145
|
+
)
|
|
146
|
+
if cuda:
|
|
147
|
+
test_input = test_input.cuda()
|
|
148
|
+
out = model(test_input)
|
|
149
|
+
n_preds_per_input = out.cpu().data.numpy().shape[2]
|
|
150
|
+
|
|
151
|
+
train_set = create_from_X_y(X[:48], y[:48],
|
|
152
|
+
drop_last_window=False,
|
|
153
|
+
window_size_samples=input_window_samples,
|
|
154
|
+
window_stride_samples=n_preds_per_input)
|
|
155
|
+
|
|
156
|
+
valid_set = create_from_X_y(X[48:60], y[48:60],
|
|
157
|
+
drop_last_window=False,
|
|
158
|
+
window_size_samples=input_window_samples,
|
|
159
|
+
window_stride_samples=n_preds_per_input)
|
|
160
|
+
|
|
161
|
+
cropped_cb_train = CroppedTrialEpochScoring(
|
|
162
|
+
"accuracy",
|
|
163
|
+
name="train_trial_accuracy",
|
|
164
|
+
lower_is_better=False,
|
|
165
|
+
on_train=True,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
cropped_cb_valid = CroppedTrialEpochScoring(
|
|
169
|
+
"accuracy",
|
|
170
|
+
on_train=False,
|
|
171
|
+
name="valid_trial_accuracy",
|
|
172
|
+
lower_is_better=False,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
clf = EEGClassifier(
|
|
176
|
+
model,
|
|
177
|
+
cropped=True,
|
|
178
|
+
criterion=CroppedLoss,
|
|
179
|
+
criterion__loss_function=nll_loss,
|
|
180
|
+
optimizer=optim.Adam,
|
|
181
|
+
train_split=predefined_split(valid_set),
|
|
182
|
+
batch_size=32,
|
|
183
|
+
callbacks=[
|
|
184
|
+
("train_trial_accuracy", cropped_cb_train),
|
|
185
|
+
("valid_trial_accuracy", cropped_cb_valid),
|
|
186
|
+
],
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
clf.fit(train_set, y=None, epochs=4)
|
|
190
|
+
|
|
191
|
+
expected = [{'batches': [{'train_batch_size': 32, 'train_loss': 1.6639312505722046},
|
|
192
|
+
{'train_batch_size': 32, 'train_loss': 2.6161606311798096},
|
|
193
|
+
{'train_batch_size': 32, 'train_loss': 1.627132773399353},
|
|
194
|
+
{'valid_batch_size': 24, 'valid_loss': 0.9677614569664001}],
|
|
195
|
+
'epoch': 1,
|
|
196
|
+
'train_batch_count': 3,
|
|
197
|
+
'train_loss': 1.9690748850504558,
|
|
198
|
+
'train_loss_best': True,
|
|
199
|
+
'train_trial_accuracy': 0.4791666666666667,
|
|
200
|
+
'train_trial_accuracy_best': True,
|
|
201
|
+
'valid_batch_count': 1,
|
|
202
|
+
'valid_loss': 0.9677614569664001,
|
|
203
|
+
'valid_loss_best': True,
|
|
204
|
+
'valid_trial_accuracy': 0.5,
|
|
205
|
+
'valid_trial_accuracy_best': True},
|
|
206
|
+
{'batches': [{'train_batch_size': 32, 'train_loss': 1.3829222917556763},
|
|
207
|
+
{'train_batch_size': 32, 'train_loss': 1.3123714923858643},
|
|
208
|
+
{'train_batch_size': 32, 'train_loss': 1.0109959840774536},
|
|
209
|
+
{'valid_batch_size': 24, 'valid_loss': 1.9435862302780151}],
|
|
210
|
+
'epoch': 2,
|
|
211
|
+
'train_batch_count': 3,
|
|
212
|
+
'train_loss': 1.2354299227396648,
|
|
213
|
+
'train_loss_best': True,
|
|
214
|
+
'train_trial_accuracy': 0.5,
|
|
215
|
+
'train_trial_accuracy_best': True,
|
|
216
|
+
'valid_batch_count': 1,
|
|
217
|
+
'valid_loss': 1.9435862302780151,
|
|
218
|
+
'valid_loss_best': False,
|
|
219
|
+
'valid_trial_accuracy': 0.5,
|
|
220
|
+
'valid_trial_accuracy_best': False},
|
|
221
|
+
{'batches': [{'train_batch_size': 32, 'train_loss': 1.172208547592163},
|
|
222
|
+
{'train_batch_size': 32, 'train_loss': 0.8899562954902649},
|
|
223
|
+
{'train_batch_size': 32, 'train_loss': 1.0232216119766235},
|
|
224
|
+
{'valid_batch_size': 24, 'valid_loss': 0.9585554599761963}],
|
|
225
|
+
'epoch': 3,
|
|
226
|
+
'train_batch_count': 3,
|
|
227
|
+
'train_loss': 1.0284621516863506,
|
|
228
|
+
'train_loss_best': True,
|
|
229
|
+
'train_trial_accuracy': 0.5,
|
|
230
|
+
'train_trial_accuracy_best': False,
|
|
231
|
+
'valid_batch_count': 1,
|
|
232
|
+
'valid_loss': 0.9585554599761963,
|
|
233
|
+
'valid_loss_best': True,
|
|
234
|
+
'valid_trial_accuracy': 0.5,
|
|
235
|
+
'valid_trial_accuracy_best': False},
|
|
236
|
+
{'batches': [{'train_batch_size': 32, 'train_loss': 0.9693693518638611},
|
|
237
|
+
{'train_batch_size': 32, 'train_loss': 0.900641918182373},
|
|
238
|
+
{'train_batch_size': 32, 'train_loss': 0.8839665651321411},
|
|
239
|
+
{'valid_batch_size': 24, 'valid_loss': 0.873468816280365}],
|
|
240
|
+
'epoch': 4,
|
|
241
|
+
'train_batch_count': 3,
|
|
242
|
+
'train_loss': 0.9179926117261251,
|
|
243
|
+
'train_loss_best': True,
|
|
244
|
+
'train_trial_accuracy': 0.625,
|
|
245
|
+
'train_trial_accuracy_best': True,
|
|
246
|
+
'valid_batch_count': 1,
|
|
247
|
+
'valid_loss': 0.873468816280365,
|
|
248
|
+
'valid_loss_best': True,
|
|
249
|
+
'valid_trial_accuracy': 0.4166666666666667,
|
|
250
|
+
'valid_trial_accuracy_best': False}]
|
|
251
|
+
|
|
252
|
+
history_without_dur = [
|
|
253
|
+
{k: v for k, v in h.items() if k != "dur"} for h in clf.history
|
|
254
|
+
]
|
|
255
|
+
assert_deep_allclose(expected, history_without_dur, atol=1e-3, rtol=1e-3)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Authors: Lukas Gemein <l.gemein@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
#
|
|
4
|
+
# License: BSD-3
|
|
5
|
+
|
|
6
|
+
import torch
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from braindecode.models.tcn import TCN
|
|
10
|
+
from braindecode.util import set_random_seeds
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_tcn():
|
|
14
|
+
set_random_seeds(0, False)
|
|
15
|
+
tcn = TCN(
|
|
16
|
+
n_in_chans=21,
|
|
17
|
+
n_outputs=2,
|
|
18
|
+
n_filters=55,
|
|
19
|
+
n_blocks=5,
|
|
20
|
+
kernel_size=16,
|
|
21
|
+
drop_prob=0.05270154233150525,
|
|
22
|
+
add_log_softmax=True
|
|
23
|
+
)
|
|
24
|
+
# braindecode models are always in eval mode after initialization
|
|
25
|
+
# original model implementation was not
|
|
26
|
+
tcn.train()
|
|
27
|
+
x = torch.rand(1, 21, 1000, 1)
|
|
28
|
+
out = tcn(x)
|
|
29
|
+
# this is the output of the original model implementation using the same
|
|
30
|
+
# initialization arguments as above
|
|
31
|
+
expected = np.array(
|
|
32
|
+
[[[-0.5504, -0.5304, -0.6023, -0.5231, -0.5387, -0.5522, -0.5323,
|
|
33
|
+
-0.5540, -0.5297, -0.5333, -0.5743, -0.5330, -0.5117, -0.5051,
|
|
34
|
+
-0.5523, -0.5507, -0.5724, -0.5380, -0.5697, -0.4871, -0.5400,
|
|
35
|
+
-0.4986, -0.5502, -0.5524, -0.5263, -0.5440, -0.5464, -0.5005,
|
|
36
|
+
-0.5404, -0.5098, -0.5197, -0.5578, -0.5419, -0.5601, -0.5031,
|
|
37
|
+
-0.5616, -0.5205, -0.5378, -0.5472, -0.4897, -0.5216, -0.5560,
|
|
38
|
+
-0.5480, -0.5488, -0.5258, -0.5637, -0.5318, -0.5134, -0.5460,
|
|
39
|
+
-0.5294, -0.5513, -0.5310, -0.5307, -0.5326, -0.5270, -0.5156,
|
|
40
|
+
-0.5569, -0.5416, -0.5279, -0.5553, -0.5589, -0.5166, -0.5108,
|
|
41
|
+
-0.5076, -0.5279, -0.5208, -0.5367, -0.5557, -0.5690, -0.5494],
|
|
42
|
+
[-0.8597, -0.8877, -0.7931, -0.8982, -0.8758, -0.8573, -0.8849,
|
|
43
|
+
-0.8549, -0.8887, -0.8834, -0.8280, -0.8839, -0.9150, -0.9250,
|
|
44
|
+
-0.8572, -0.8593, -0.8305, -0.8769, -0.8340, -0.9530, -0.8741,
|
|
45
|
+
-0.9350, -0.8600, -0.8570, -0.8935, -0.8685, -0.8652, -0.9319,
|
|
46
|
+
-0.8735, -0.9179, -0.9031, -0.8497, -0.8714, -0.8466, -0.9280,
|
|
47
|
+
-0.8447, -0.9019, -0.8771, -0.8640, -0.9489, -0.9003, -0.8521,
|
|
48
|
+
-0.8630, -0.8619, -0.8942, -0.8419, -0.8856, -0.9124, -0.8658,
|
|
49
|
+
-0.8890, -0.8585, -0.8867, -0.8872, -0.8845, -0.8924, -0.9092,
|
|
50
|
+
-0.8509, -0.8718, -0.8912, -0.8531, -0.8482, -0.9077, -0.9163,
|
|
51
|
+
-0.9212, -0.8912, -0.9016, -0.8787, -0.8525, -0.8349,
|
|
52
|
+
-0.8611]]])
|
|
53
|
+
np.testing.assert_allclose(
|
|
54
|
+
out.detach().numpy(), expected, rtol=1e-3, atol=1e-3)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Authors: Maciej Sliwowski
|
|
2
|
+
# Robin Tibor Schirrmeister
|
|
3
|
+
#
|
|
4
|
+
# License: BSD-3
|
|
5
|
+
|
|
6
|
+
import mne
|
|
7
|
+
import numpy as np
|
|
8
|
+
import torch
|
|
9
|
+
from mne.io import concatenate_raws
|
|
10
|
+
from skorch.helper import predefined_split
|
|
11
|
+
from torch.utils.data import Dataset, Subset
|
|
12
|
+
|
|
13
|
+
from braindecode.classifier import EEGClassifier
|
|
14
|
+
from braindecode.models import ShallowFBCSPNet
|
|
15
|
+
from braindecode.util import set_random_seeds
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EpochsDataset(Dataset):
|
|
19
|
+
def __init__(self, windows):
|
|
20
|
+
self.windows = windows
|
|
21
|
+
self.y = np.array(self.windows.events[:, -1])
|
|
22
|
+
self.y = self.y - self.y.min()
|
|
23
|
+
|
|
24
|
+
def __getitem__(self, index):
|
|
25
|
+
X = self.windows.get_data(item=index)[0].astype('float32')[:, :, None]
|
|
26
|
+
y = self.y[index]
|
|
27
|
+
return X, y
|
|
28
|
+
|
|
29
|
+
def __len__(self):
|
|
30
|
+
return len(self.windows.events)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_trialwise_decoding():
|
|
34
|
+
# 5,6,7,10,13,14 are codes for executed and imagined hands/feet
|
|
35
|
+
subject_id = 1
|
|
36
|
+
event_codes = [5, 6, 9, 10, 13, 14]
|
|
37
|
+
|
|
38
|
+
# This will download the files if you don't have them yet,
|
|
39
|
+
# and then return the paths to the files.
|
|
40
|
+
physionet_paths = mne.datasets.eegbci.load_data(
|
|
41
|
+
subject_id, event_codes, update_path=False
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Load each of the files
|
|
45
|
+
parts = [
|
|
46
|
+
mne.io.read_raw_edf(
|
|
47
|
+
path, preload=True, stim_channel="auto", verbose="WARNING"
|
|
48
|
+
)
|
|
49
|
+
for path in physionet_paths
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# Concatenate them
|
|
53
|
+
raw = concatenate_raws(parts)
|
|
54
|
+
raw.apply_function(lambda x: x * 1000000)
|
|
55
|
+
|
|
56
|
+
# Find the events in this dataset
|
|
57
|
+
events, _ = mne.events_from_annotations(raw)
|
|
58
|
+
# Use only EEG channels
|
|
59
|
+
eeg_channel_inds = mne.pick_types(
|
|
60
|
+
raw.info, meg=False, eeg=True, stim=False, eog=False, exclude="bads"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Extract trials, only using EEG channels
|
|
64
|
+
epoched = mne.Epochs(
|
|
65
|
+
raw,
|
|
66
|
+
events,
|
|
67
|
+
dict(hands=2, feet=3),
|
|
68
|
+
tmin=1,
|
|
69
|
+
tmax=4.1,
|
|
70
|
+
proj=False,
|
|
71
|
+
picks=eeg_channel_inds,
|
|
72
|
+
baseline=None,
|
|
73
|
+
preload=True,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
ds = EpochsDataset(epoched)
|
|
77
|
+
|
|
78
|
+
train_set = Subset(ds, np.arange(60))
|
|
79
|
+
valid_set = Subset(ds, np.arange(60, len(ds)))
|
|
80
|
+
|
|
81
|
+
train_valid_split = predefined_split(valid_set)
|
|
82
|
+
|
|
83
|
+
cuda = False
|
|
84
|
+
if cuda:
|
|
85
|
+
device = 'cuda'
|
|
86
|
+
else:
|
|
87
|
+
device = 'cpu'
|
|
88
|
+
set_random_seeds(seed=20170629, cuda=cuda)
|
|
89
|
+
n_classes = 2
|
|
90
|
+
in_chans = train_set[0][0].shape[0]
|
|
91
|
+
input_window_samples = train_set[0][0].shape[1]
|
|
92
|
+
model = ShallowFBCSPNet(
|
|
93
|
+
in_chans=in_chans,
|
|
94
|
+
n_classes=n_classes,
|
|
95
|
+
input_window_samples=input_window_samples,
|
|
96
|
+
final_conv_length="auto",
|
|
97
|
+
)
|
|
98
|
+
if cuda:
|
|
99
|
+
model.cuda()
|
|
100
|
+
|
|
101
|
+
clf = EEGClassifier(
|
|
102
|
+
model,
|
|
103
|
+
cropped=False,
|
|
104
|
+
criterion=torch.nn.NLLLoss,
|
|
105
|
+
optimizer=torch.optim.Adam,
|
|
106
|
+
train_split=train_valid_split,
|
|
107
|
+
optimizer__lr=0.001,
|
|
108
|
+
batch_size=30,
|
|
109
|
+
callbacks=["accuracy"],
|
|
110
|
+
device=device,
|
|
111
|
+
)
|
|
112
|
+
clf.fit(train_set, y=None, epochs=6)
|
|
113
|
+
|
|
114
|
+
np.testing.assert_allclose(
|
|
115
|
+
clf.history[:, 'train_loss'],
|
|
116
|
+
np.array([
|
|
117
|
+
1.623811,
|
|
118
|
+
1.141197,
|
|
119
|
+
0.730361,
|
|
120
|
+
0.5994,
|
|
121
|
+
0.738884,
|
|
122
|
+
0.419241
|
|
123
|
+
]),
|
|
124
|
+
rtol=1e-4,
|
|
125
|
+
atol=1e-5,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
np.testing.assert_allclose(
|
|
129
|
+
clf.history[:, 'valid_loss'],
|
|
130
|
+
np.array([
|
|
131
|
+
1.958356261253357,
|
|
132
|
+
0.8494758009910583,
|
|
133
|
+
0.9321595430374146,
|
|
134
|
+
0.8126973509788513,
|
|
135
|
+
0.7426613569259644,
|
|
136
|
+
0.7547585368156433
|
|
137
|
+
]),
|
|
138
|
+
rtol=1e-4,
|
|
139
|
+
atol=1e-5,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
np.testing.assert_allclose(
|
|
143
|
+
clf.history[:, 'train_accuracy'],
|
|
144
|
+
np.array([
|
|
145
|
+
0.5,
|
|
146
|
+
0.8166666666666667,
|
|
147
|
+
0.7,
|
|
148
|
+
0.8,
|
|
149
|
+
0.8666666666666667,
|
|
150
|
+
0.9
|
|
151
|
+
]),
|
|
152
|
+
rtol=1e-4,
|
|
153
|
+
atol=1e-5,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
np.testing.assert_allclose(
|
|
157
|
+
clf.history[:, 'valid_accuracy'],
|
|
158
|
+
np.array([
|
|
159
|
+
0.4666666666666667,
|
|
160
|
+
0.6,
|
|
161
|
+
0.5,
|
|
162
|
+
0.5666666666666667,
|
|
163
|
+
0.6,
|
|
164
|
+
0.6,
|
|
165
|
+
]),
|
|
166
|
+
rtol=1e-4,
|
|
167
|
+
atol=1e-5,
|
|
168
|
+
)
|
|
File without changes
|
|
File without changes
|