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,73 @@
|
|
|
1
|
+
# Authors: Maciej Sliwowski <maciek.sliwowski@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# License: BSD-3
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import torch
|
|
7
|
+
from skorch.utils import to_tensor
|
|
8
|
+
from torch import optim
|
|
9
|
+
from torch.nn.functional import nll_loss
|
|
10
|
+
|
|
11
|
+
from braindecode import EEGClassifier
|
|
12
|
+
from braindecode.training import CroppedLoss
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MockDataset(torch.utils.data.Dataset):
|
|
16
|
+
def __len__(self):
|
|
17
|
+
return 4
|
|
18
|
+
|
|
19
|
+
def __getitem__(self, item):
|
|
20
|
+
return torch.rand(4, 1), torch.ones(4)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MockModule(torch.nn.Module):
|
|
24
|
+
def __init__(self, preds):
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.preds = to_tensor(preds, device='cpu')
|
|
27
|
+
self.linear = torch.nn.Linear(5, 5)
|
|
28
|
+
|
|
29
|
+
def forward(self, x):
|
|
30
|
+
return self.preds
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_trialwise_predict_and_predict_proba():
|
|
34
|
+
preds = np.array(
|
|
35
|
+
[
|
|
36
|
+
[0.125, 0.875],
|
|
37
|
+
[1., 0.],
|
|
38
|
+
[0.8, 0.2],
|
|
39
|
+
[0.9, 0.1],
|
|
40
|
+
]
|
|
41
|
+
)
|
|
42
|
+
clf = EEGClassifier(
|
|
43
|
+
MockModule(preds),
|
|
44
|
+
optimizer=optim.Adam,
|
|
45
|
+
batch_size=32
|
|
46
|
+
)
|
|
47
|
+
clf.initialize()
|
|
48
|
+
np.testing.assert_array_equal(preds.argmax(1), clf.predict(MockDataset()))
|
|
49
|
+
np.testing.assert_array_equal(preds, clf.predict_proba(MockDataset()))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_cropped_predict_and_predict_proba():
|
|
53
|
+
preds = np.array(
|
|
54
|
+
[
|
|
55
|
+
[[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]],
|
|
56
|
+
[[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]],
|
|
57
|
+
[[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]],
|
|
58
|
+
[[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]],
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
clf = EEGClassifier(
|
|
62
|
+
MockModule(preds),
|
|
63
|
+
cropped=True,
|
|
64
|
+
criterion=CroppedLoss,
|
|
65
|
+
criterion__loss_function=nll_loss,
|
|
66
|
+
optimizer=optim.Adam,
|
|
67
|
+
batch_size=32
|
|
68
|
+
)
|
|
69
|
+
clf.initialize()
|
|
70
|
+
# for cropped decoding classifier returns one label for each trial (averaged over all crops)
|
|
71
|
+
np.testing.assert_array_equal(preds.mean(-1).argmax(1), clf.predict(MockDataset()))
|
|
72
|
+
# for cropped decoding classifier returns values for each trial (average over all crops)
|
|
73
|
+
np.testing.assert_array_equal(preds.mean(-1), clf.predict_proba(MockDataset()))
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Authors: Maciej Sliwowski <maciek.sliwowski@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# License: BSD-3
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import torch
|
|
7
|
+
from skorch.utils import to_tensor
|
|
8
|
+
from torch import optim
|
|
9
|
+
from torch.nn.functional import nll_loss
|
|
10
|
+
|
|
11
|
+
from braindecode import EEGRegressor
|
|
12
|
+
from braindecode.training import CroppedLoss
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MockDataset(torch.utils.data.Dataset):
|
|
16
|
+
def __len__(self):
|
|
17
|
+
return 4
|
|
18
|
+
|
|
19
|
+
def __getitem__(self, item):
|
|
20
|
+
return torch.rand(4, 1), torch.ones(4)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MockModule(torch.nn.Module):
|
|
24
|
+
def __init__(self, preds):
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.preds = to_tensor(preds, device='cpu')
|
|
27
|
+
self.linear = torch.nn.Linear(5, 5)
|
|
28
|
+
|
|
29
|
+
def forward(self, x):
|
|
30
|
+
return self.preds
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_trialwise_predict_and_predict_proba():
|
|
34
|
+
preds = np.array(
|
|
35
|
+
[
|
|
36
|
+
[0.125, 0.875],
|
|
37
|
+
[1., 0.],
|
|
38
|
+
[0.8, 0.2],
|
|
39
|
+
[0.9, 0.1],
|
|
40
|
+
]
|
|
41
|
+
)
|
|
42
|
+
clf = EEGRegressor(
|
|
43
|
+
MockModule(preds),
|
|
44
|
+
optimizer=optim.Adam,
|
|
45
|
+
batch_size=32
|
|
46
|
+
)
|
|
47
|
+
clf.initialize()
|
|
48
|
+
np.testing.assert_array_equal(preds, clf.predict(MockDataset()))
|
|
49
|
+
np.testing.assert_array_equal(preds, clf.predict_proba(MockDataset()))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_cropped_predict_and_predict_proba():
|
|
53
|
+
preds = np.array(
|
|
54
|
+
[
|
|
55
|
+
[[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]],
|
|
56
|
+
[[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]],
|
|
57
|
+
[[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]],
|
|
58
|
+
[[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]],
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
clf = EEGRegressor(
|
|
62
|
+
MockModule(preds),
|
|
63
|
+
cropped=True,
|
|
64
|
+
criterion=CroppedLoss,
|
|
65
|
+
criterion__loss_function=nll_loss,
|
|
66
|
+
optimizer=optim.Adam,
|
|
67
|
+
batch_size=32
|
|
68
|
+
)
|
|
69
|
+
clf.initialize()
|
|
70
|
+
# for cropped decoding regressor returns value for each trial (average over all crops)
|
|
71
|
+
np.testing.assert_array_equal(preds.mean(-1), clf.predict(MockDataset()))
|
|
72
|
+
np.testing.assert_array_equal(preds.mean(-1), clf.predict_proba(MockDataset()))
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Authors: Maciej Sliwowski
|
|
2
|
+
# Lukas Gemein
|
|
3
|
+
# Robin Tibor Schirrmeister
|
|
4
|
+
#
|
|
5
|
+
# License: BSD-3
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import sklearn.datasets
|
|
10
|
+
import torch
|
|
11
|
+
from sklearn.metrics import f1_score, accuracy_score
|
|
12
|
+
from skorch import History
|
|
13
|
+
from skorch.callbacks import Callback
|
|
14
|
+
from skorch.utils import to_numpy, to_tensor
|
|
15
|
+
from torch import optim
|
|
16
|
+
from torch.utils.data import Dataset, DataLoader
|
|
17
|
+
from braindecode.classifier import EEGClassifier
|
|
18
|
+
from braindecode.datautil.xy import create_from_X_y
|
|
19
|
+
from braindecode.training.scoring import CroppedTrialEpochScoring
|
|
20
|
+
from braindecode.training.scoring import PostEpochTrainScoring
|
|
21
|
+
from braindecode.models import ShallowFBCSPNet
|
|
22
|
+
from braindecode.util import set_random_seeds
|
|
23
|
+
from braindecode.training.scoring import trial_preds_from_window_preds
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MockSkorchNet:
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self.device = "cpu"
|
|
29
|
+
self.forward_iter = None
|
|
30
|
+
self.history = History()
|
|
31
|
+
self.history.new_epoch()
|
|
32
|
+
self._default_callbacks = []
|
|
33
|
+
|
|
34
|
+
def fit(self, X, y=None):
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
def predict(self, X):
|
|
38
|
+
return np.concatenate(
|
|
39
|
+
[to_numpy(x.argmax(1)) for x in self.forward_iter(X)], 0
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def get_iterator(self, X_test, training):
|
|
43
|
+
return DataLoader(X_test, batch_size=2)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_cropped_trial_epoch_scoring():
|
|
47
|
+
|
|
48
|
+
dataset_train = None
|
|
49
|
+
# Definition of test cases
|
|
50
|
+
predictions_cases = [
|
|
51
|
+
# Expected predictions classification results: [1, 0, 0, 0]
|
|
52
|
+
np.array(
|
|
53
|
+
[
|
|
54
|
+
[[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]], # trial 0 preds
|
|
55
|
+
[[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], # trial 1 preds
|
|
56
|
+
[[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]], # trial 2 preds
|
|
57
|
+
[[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]], # trial 3 preds
|
|
58
|
+
]
|
|
59
|
+
),
|
|
60
|
+
# Expected predictions classification results: [1, 1, 1, 0]
|
|
61
|
+
np.array(
|
|
62
|
+
[
|
|
63
|
+
[[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]],
|
|
64
|
+
[[0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]],
|
|
65
|
+
[[0.0, 0.0, 0.0, 0.2], [1.0, 1.0, 1.0, 0.8]],
|
|
66
|
+
[[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]],
|
|
67
|
+
]
|
|
68
|
+
),
|
|
69
|
+
]
|
|
70
|
+
y_true_cases = [
|
|
71
|
+
[torch.tensor([0, 0]), torch.tensor([1, 1])],
|
|
72
|
+
[torch.tensor([1, 1]), torch.tensor([1, 1])],
|
|
73
|
+
]
|
|
74
|
+
expected_accuracies_cases = [0.25, 0.75]
|
|
75
|
+
|
|
76
|
+
window_inds = [(
|
|
77
|
+
torch.tensor([0, 0]), # i_window_in_trials
|
|
78
|
+
[None], # won't be used
|
|
79
|
+
torch.tensor([4, 4]), # i_window_stops
|
|
80
|
+
),(
|
|
81
|
+
torch.tensor([0, 0]), # i_window_in_trials
|
|
82
|
+
[None], # won't be used
|
|
83
|
+
torch.tensor([4, 4]), # i_window_stops
|
|
84
|
+
),]
|
|
85
|
+
|
|
86
|
+
for predictions, y_true, accuracy in zip(
|
|
87
|
+
predictions_cases, y_true_cases, expected_accuracies_cases
|
|
88
|
+
):
|
|
89
|
+
dataset_valid = create_from_X_y(
|
|
90
|
+
np.zeros((4, 1, 10)), np.concatenate(y_true),
|
|
91
|
+
window_size_samples=10, window_stride_samples=4, drop_last_window=False)
|
|
92
|
+
|
|
93
|
+
mock_skorch_net = MockSkorchNet()
|
|
94
|
+
cropped_trial_epoch_scoring = CroppedTrialEpochScoring(
|
|
95
|
+
"accuracy", on_train=False)
|
|
96
|
+
mock_skorch_net.callbacks = [(
|
|
97
|
+
"", cropped_trial_epoch_scoring)]
|
|
98
|
+
cropped_trial_epoch_scoring.initialize()
|
|
99
|
+
cropped_trial_epoch_scoring.y_preds_ = [
|
|
100
|
+
to_tensor(predictions[:2], device="cpu"),
|
|
101
|
+
to_tensor(predictions[2:], device="cpu"),
|
|
102
|
+
]
|
|
103
|
+
cropped_trial_epoch_scoring.y_trues_ = y_true
|
|
104
|
+
cropped_trial_epoch_scoring.window_inds_ = window_inds
|
|
105
|
+
|
|
106
|
+
cropped_trial_epoch_scoring.on_epoch_end(
|
|
107
|
+
mock_skorch_net, dataset_train, dataset_valid
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
np.testing.assert_almost_equal(
|
|
111
|
+
mock_skorch_net.history[0]["accuracy"], accuracy
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_cropped_trial_epoch_scoring_none_x_test():
|
|
116
|
+
dataset_train = None
|
|
117
|
+
dataset_valid = None
|
|
118
|
+
predictions = np.array(
|
|
119
|
+
[
|
|
120
|
+
[[0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]],
|
|
121
|
+
[[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]],
|
|
122
|
+
[[1.0, 1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]],
|
|
123
|
+
[[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]],
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
y_true = [torch.tensor([0, 0]), torch.tensor([1, 1])]
|
|
127
|
+
window_inds = [(
|
|
128
|
+
torch.tensor([0, 0]), # i_window_in_trials
|
|
129
|
+
[None], # won't be used
|
|
130
|
+
torch.tensor([4, 4]), # i_window_stops
|
|
131
|
+
),
|
|
132
|
+
(
|
|
133
|
+
torch.tensor([0, 0]), # i_window_in_trials
|
|
134
|
+
[None], # won't be used
|
|
135
|
+
torch.tensor([4, 4]), # i_window_stops
|
|
136
|
+
)]
|
|
137
|
+
cropped_trial_epoch_scoring = CroppedTrialEpochScoring("accuracy")
|
|
138
|
+
cropped_trial_epoch_scoring.initialize()
|
|
139
|
+
cropped_trial_epoch_scoring.y_preds_ = [
|
|
140
|
+
to_tensor(predictions[:2], device="cpu"),
|
|
141
|
+
to_tensor(predictions[2:], device="cpu"),
|
|
142
|
+
]
|
|
143
|
+
cropped_trial_epoch_scoring.y_trues_ = y_true
|
|
144
|
+
cropped_trial_epoch_scoring.window_inds_ = window_inds
|
|
145
|
+
|
|
146
|
+
mock_skorch_net = MockSkorchNet()
|
|
147
|
+
mock_skorch_net.callbacks = [(
|
|
148
|
+
"", cropped_trial_epoch_scoring)]
|
|
149
|
+
output = cropped_trial_epoch_scoring.on_epoch_end(
|
|
150
|
+
mock_skorch_net, dataset_train, dataset_valid
|
|
151
|
+
)
|
|
152
|
+
assert output is None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_post_epoch_train_scoring():
|
|
156
|
+
cuda = False
|
|
157
|
+
set_random_seeds(seed=20170629, cuda=cuda)
|
|
158
|
+
|
|
159
|
+
n_classes = 2
|
|
160
|
+
|
|
161
|
+
class EEGDataSet(Dataset):
|
|
162
|
+
def __init__(self, X, y):
|
|
163
|
+
self.X = X
|
|
164
|
+
if self.X.ndim == 3:
|
|
165
|
+
self.X = self.X[:, :, :, None]
|
|
166
|
+
self.y = y
|
|
167
|
+
|
|
168
|
+
def __len__(self):
|
|
169
|
+
return len(self.X)
|
|
170
|
+
|
|
171
|
+
def __getitem__(self, idx):
|
|
172
|
+
return self.X[idx], self.y[idx]
|
|
173
|
+
|
|
174
|
+
X, y = sklearn.datasets.make_classification(
|
|
175
|
+
40, (3 * 100), n_informative=3 * 50, n_classes=2
|
|
176
|
+
)
|
|
177
|
+
X = X.reshape(40, 3, 100).astype(np.float32)
|
|
178
|
+
|
|
179
|
+
in_chans = X.shape[1]
|
|
180
|
+
|
|
181
|
+
train_set = EEGDataSet(X, y)
|
|
182
|
+
|
|
183
|
+
class TestCallback(Callback):
|
|
184
|
+
def on_epoch_end(self, net, *args, **kwargs):
|
|
185
|
+
preds = net.predict(train_set.X)
|
|
186
|
+
y_true = train_set.y
|
|
187
|
+
np.testing.assert_allclose(
|
|
188
|
+
clf.history[-1]["train_f1"],
|
|
189
|
+
f1_score(y_true, preds),
|
|
190
|
+
rtol=1e-4,
|
|
191
|
+
atol=1e-4,
|
|
192
|
+
)
|
|
193
|
+
np.testing.assert_allclose(
|
|
194
|
+
clf.history[-1]["train_acc"],
|
|
195
|
+
accuracy_score(y_true, preds),
|
|
196
|
+
rtol=1e-4,
|
|
197
|
+
atol=1e-4,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
set_random_seeds(20200114, cuda)
|
|
201
|
+
|
|
202
|
+
# final_conv_length = auto ensures
|
|
203
|
+
# we only get a single output in the time dimension
|
|
204
|
+
model = ShallowFBCSPNet(
|
|
205
|
+
in_chans=in_chans,
|
|
206
|
+
n_classes=n_classes,
|
|
207
|
+
input_window_samples=train_set.X.shape[2],
|
|
208
|
+
pool_time_stride=1,
|
|
209
|
+
pool_time_length=2,
|
|
210
|
+
final_conv_length="auto",
|
|
211
|
+
)
|
|
212
|
+
if cuda:
|
|
213
|
+
model.cuda()
|
|
214
|
+
|
|
215
|
+
clf = EEGClassifier(
|
|
216
|
+
model,
|
|
217
|
+
criterion=torch.nn.NLLLoss,
|
|
218
|
+
optimizer=optim.AdamW,
|
|
219
|
+
train_split=None,
|
|
220
|
+
optimizer__lr=0.0625 * 0.01,
|
|
221
|
+
optimizer__weight_decay=0,
|
|
222
|
+
batch_size=64,
|
|
223
|
+
callbacks=[
|
|
224
|
+
(
|
|
225
|
+
"train_accuracy",
|
|
226
|
+
PostEpochTrainScoring(
|
|
227
|
+
"accuracy", lower_is_better=False, name="train_acc"
|
|
228
|
+
),
|
|
229
|
+
),
|
|
230
|
+
(
|
|
231
|
+
"train_f1_score",
|
|
232
|
+
PostEpochTrainScoring(
|
|
233
|
+
"f1", lower_is_better=False, name="train_f1"
|
|
234
|
+
),
|
|
235
|
+
),
|
|
236
|
+
("test_callback", TestCallback()),
|
|
237
|
+
],
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
clf.fit(train_set, y=None, epochs=4)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _check_preds_windows_trials(preds, window_inds, expected_trial_preds):
|
|
244
|
+
# transform to 2 lists from tuples
|
|
245
|
+
i_window_in_trials = []
|
|
246
|
+
i_stop_in_trials = []
|
|
247
|
+
for i_window_in_trial, _, i_stop_in_trial in window_inds:
|
|
248
|
+
i_window_in_trials.append(i_window_in_trial)
|
|
249
|
+
i_stop_in_trials.append(i_stop_in_trial)
|
|
250
|
+
trial_preds = trial_preds_from_window_preds(
|
|
251
|
+
preds, i_window_in_trials, i_stop_in_trials)
|
|
252
|
+
np.testing.assert_equal(len(trial_preds), len(expected_trial_preds),)
|
|
253
|
+
for expected_pred, actual_pred in zip(expected_trial_preds, trial_preds):
|
|
254
|
+
np.testing.assert_array_equal(actual_pred, expected_pred, )
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def test_two_windows_same_trial_with_overlap():
|
|
258
|
+
preds = [[[4, 5, 6, 7]], [[6, 7, 8, 9]], ]
|
|
259
|
+
window_inds = ((0, 0, 8), (1, 2, 10))
|
|
260
|
+
expected_trial_preds = [[[4, 5, 6, 7, 8, 9]]]
|
|
261
|
+
_check_preds_windows_trials(preds, window_inds, expected_trial_preds)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_three_windows_two_trials_with_overlap():
|
|
265
|
+
preds = [[[4, 5, 6, 7]], [[6, 7, 8, 9]], [[0, 1, 2, 3]]]
|
|
266
|
+
window_inds = ((0, 0, 8), (1, 2, 10), (0, 0, 6,))
|
|
267
|
+
expected_trial_preds = [[[4, 5, 6, 7, 8, 9]], [[0, 1, 2, 3]]]
|
|
268
|
+
_check_preds_windows_trials(preds, window_inds, expected_trial_preds)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def test_one_window_one_trial():
|
|
272
|
+
preds = [[[4, 5, 6, 7]]]
|
|
273
|
+
window_inds = ((0, 0, 8),)
|
|
274
|
+
expected_trial_preds = [[[4, 5, 6, 7]]]
|
|
275
|
+
_check_preds_windows_trials(preds, window_inds, expected_trial_preds)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_three_windows_two_trials_no_overlap():
|
|
279
|
+
preds = [[[4, 5, 6, 7]], [[6, 7, 8, 9]], [[0, 1, 2, 3]]]
|
|
280
|
+
window_inds = ((0, 0, 8), (1, 4, 12), (0, 0, 6,))
|
|
281
|
+
expected_trial_preds = [[[4, 5, 6, 7, 6, 7, 8, 9]], [[0, 1, 2, 3]]]
|
|
282
|
+
_check_preds_windows_trials(preds, window_inds, expected_trial_preds)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Authors: Hubert Banville <hubert.jbanville@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# License: BSD-3
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import mne
|
|
8
|
+
import numpy as np
|
|
9
|
+
import h5py
|
|
10
|
+
|
|
11
|
+
from braindecode.util import create_mne_dummy_raw
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_create_mne_dummy_raw(tmp_path):
|
|
15
|
+
n_channels, n_times, sfreq = 2, 10000, 100
|
|
16
|
+
raw, fnames = create_mne_dummy_raw(
|
|
17
|
+
n_channels, n_times, sfreq, savedir=tmp_path,
|
|
18
|
+
save_format=['fif', 'hdf5'])
|
|
19
|
+
|
|
20
|
+
assert isinstance(raw, mne.io.RawArray)
|
|
21
|
+
assert len(raw.ch_names) == n_channels
|
|
22
|
+
assert raw.n_times == n_times
|
|
23
|
+
assert raw.info['sfreq'] == sfreq
|
|
24
|
+
assert isinstance(fnames, dict)
|
|
25
|
+
assert os.path.isfile(fnames['fif'])
|
|
26
|
+
assert os.path.isfile(fnames['hdf5'])
|
|
27
|
+
|
|
28
|
+
raw = mne.io.read_raw_fif(fnames['fif'], preload=False, verbose=None)
|
|
29
|
+
with h5py.File(fnames['hdf5'], 'r') as hf:
|
|
30
|
+
_ = np.array(hf['fake_raw'])
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Authors: Robin Schirrmeister <robintibor@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# License: BSD (3-clause)
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import torch
|
|
7
|
+
from torch import nn
|
|
8
|
+
from braindecode.util import set_random_seeds
|
|
9
|
+
from braindecode.visualization.gradients import compute_amplitude_gradients_for_X
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_compute_amplitude_gradients_for_X():
|
|
14
|
+
# If the weights are initalized with a sine function
|
|
15
|
+
# gradient of amplitude should be only in one frequency bin
|
|
16
|
+
set_random_seeds(948, False)
|
|
17
|
+
model = nn.Conv1d(1,1, 16)
|
|
18
|
+
# torch.linspace(,,n)[:n-1] is same as np.linspace(,,n,endpoint=False)
|
|
19
|
+
model.weight.data[:,:,:] = torch.sin(torch.linspace(0,2*np.pi,17)[:16])
|
|
20
|
+
model.bias.data[:] = 0
|
|
21
|
+
grads = compute_amplitude_gradients_for_X(model, torch.randn(1,1,16))
|
|
22
|
+
grads = grads.squeeze()
|
|
23
|
+
assert np.abs(grads[1]) / np.sum(np.abs(grads)) > 0.99
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_compute_amplitude_gradients_for_X_two_filters():
|
|
27
|
+
# If the weights are initalized with a sine function
|
|
28
|
+
# gradient of amplitude should be only in one frequency bin
|
|
29
|
+
set_random_seeds(948, False)
|
|
30
|
+
model = nn.Conv1d(1,2, 16)
|
|
31
|
+
# torch.linspace(,,n)[:n-1] is same as np.linspace(,,n,endpoint=False)
|
|
32
|
+
model.weight.data[0,:,:] = torch.sin(torch.linspace(0,2*np.pi,17)[:16])
|
|
33
|
+
model.weight.data[1,:,:] = torch.sin(torch.linspace(0,4*np.pi,17)[:16])
|
|
34
|
+
model.bias.data[:] = 0
|
|
35
|
+
grads = compute_amplitude_gradients_for_X(model, torch.randn(1,1,16))
|
|
36
|
+
grads = grads.squeeze()
|
|
37
|
+
assert np.abs(grads[0][1]) / np.sum(np.abs(grads[0])) > 0.99
|
|
38
|
+
assert np.abs(grads[1][2]) / np.sum(np.abs(grads[1])) > 0.99
|