braindecode 0.3.0__tar.gz → 0.3.2__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 (74) hide show
  1. {Braindecode-0.3.0 → Braindecode-0.3.2}/Braindecode.egg-info/PKG-INFO +2 -11
  2. {Braindecode-0.3.0 → Braindecode-0.3.2}/Braindecode.egg-info/SOURCES.txt +2 -0
  3. {Braindecode-0.3.0 → Braindecode-0.3.2}/PKG-INFO +2 -11
  4. {Braindecode-0.3.0 → Braindecode-0.3.2}/README.rst +1 -10
  5. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datasets/bbci.py +3 -3
  6. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datasets/sensor_positions.py +27 -21
  7. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/iterators.py +7 -3
  8. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/signalproc.py +8 -2
  9. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/splitters.py +27 -0
  10. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/trial_segment.py +0 -3
  11. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/experiments/experiment.py +36 -12
  12. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/experiments/monitors.py +19 -6
  13. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/models/deep4.py +23 -11
  14. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/modules.py +51 -9
  15. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/util.py +17 -1
  16. Braindecode-0.3.2/braindecode/version.py +1 -0
  17. Braindecode-0.3.2/braindecode/visualization/input_windows.py +170 -0
  18. Braindecode-0.3.2/braindecode/visualization/perturbation.py +291 -0
  19. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/visualization/plot.py +0 -2
  20. Braindecode-0.3.2/braindecode/visualization/sinfit.py +89 -0
  21. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/index.rst +1 -10
  22. Braindecode-0.3.2/docs/notebooks/BBCI_Data.ipynb +841 -0
  23. Braindecode-0.3.2/docs/notebooks/BBCI_Data_Start_Stop.ipynb +899 -0
  24. Braindecode-0.3.2/docs/notebooks/BBCI_Data_Trialwise.ipynb +715 -0
  25. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/notebooks/visualization/Perturbation.ipynb +7 -24
  26. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.visualization.rst +16 -0
  27. {Braindecode-0.3.0 → Braindecode-0.3.2}/setup.cfg +0 -1
  28. Braindecode-0.3.0/braindecode/version.py +0 -1
  29. Braindecode-0.3.0/braindecode/visualization/perturbation.py +0 -116
  30. Braindecode-0.3.0/docs/notebooks/BBCI_Data.ipynb +0 -886
  31. Braindecode-0.3.0/docs/notebooks/BBCI_Data_Start_Stop.ipynb +0 -899
  32. Braindecode-0.3.0/docs/notebooks/BBCI_Data_Trialwise.ipynb +0 -761
  33. {Braindecode-0.3.0 → Braindecode-0.3.2}/Braindecode.egg-info/dependency_links.txt +0 -0
  34. {Braindecode-0.3.0 → Braindecode-0.3.2}/Braindecode.egg-info/not-zip-safe +0 -0
  35. {Braindecode-0.3.0 → Braindecode-0.3.2}/Braindecode.egg-info/requires.txt +0 -0
  36. {Braindecode-0.3.0 → Braindecode-0.3.2}/Braindecode.egg-info/top_level.txt +0 -0
  37. {Braindecode-0.3.0 → Braindecode-0.3.2}/LICENSE.txt +0 -0
  38. {Braindecode-0.3.0 → Braindecode-0.3.2}/MANIFEST.in +0 -0
  39. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/__init__.py +0 -0
  40. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datasets/__init__.py +0 -0
  41. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datasets/bcic_iv_2a.py +0 -0
  42. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datasets/multiple.py +0 -0
  43. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/__init__.py +0 -0
  44. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/signal_target.py +0 -0
  45. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/datautil/util.py +0 -0
  46. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/experiments/__init__.py +0 -0
  47. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/experiments/stopcriteria.py +0 -0
  48. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/mne_ext/__init__.py +0 -0
  49. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/mne_ext/signalproc.py +0 -0
  50. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/models/__init__.py +0 -0
  51. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/models/eegnet.py +0 -0
  52. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/models/shallow_fbcsp.py +0 -0
  53. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/models/util.py +0 -0
  54. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/__init__.py +0 -0
  55. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/constraints.py +0 -0
  56. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/functions.py +0 -0
  57. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/init.py +0 -0
  58. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/torch_ext/losses.py +0 -0
  59. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/util.py +0 -0
  60. {Braindecode-0.3.0 → Braindecode-0.3.2}/braindecode/visualization/__init__.py +0 -0
  61. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/Makefile +0 -0
  62. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/conf.py +0 -0
  63. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/notebooks/Cropped_Decoding.ipynb +0 -0
  64. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/notebooks/Experiment_Class.ipynb +0 -0
  65. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/notebooks/TrialWise_Decoding.ipynb +0 -0
  66. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.datasets.rst +0 -0
  67. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.datautil.rst +0 -0
  68. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.experiments.rst +0 -0
  69. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.mne_ext.rst +0 -0
  70. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.models.rst +0 -0
  71. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.rst +0 -0
  72. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/braindecode.torch_ext.rst +0 -0
  73. {Braindecode-0.3.0 → Braindecode-0.3.2}/docs/source/modules.rst +0 -0
  74. {Braindecode-0.3.0 → Braindecode-0.3.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: Braindecode
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: A deep learning toolbox to decode raw time-domain EEG.
5
5
  Home-page: https://github.com/robintibor/braindecode
6
6
  Author: Robin Tibor Schirrmeister
@@ -30,16 +30,7 @@ Description: Note: The old braindecode repository has been moved to
30
30
 
31
31
  pip install numpy
32
32
 
33
- 3. Install newest version of python-mne:
34
-
35
- .. code-block:: bash
36
-
37
- git clone git://github.com/mne-tools/mne-python.git
38
- cd mne-python
39
- python setup.py install
40
-
41
-
42
- 4. Install braindecode via pip:
33
+ 3. Install braindecode via pip:
43
34
 
44
35
  .. code-block:: bash
45
36
 
@@ -42,8 +42,10 @@ braindecode/torch_ext/losses.py
42
42
  braindecode/torch_ext/modules.py
43
43
  braindecode/torch_ext/util.py
44
44
  braindecode/visualization/__init__.py
45
+ braindecode/visualization/input_windows.py
45
46
  braindecode/visualization/perturbation.py
46
47
  braindecode/visualization/plot.py
48
+ braindecode/visualization/sinfit.py
47
49
  docs/Makefile
48
50
  docs/conf.py
49
51
  docs/index.rst
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: Braindecode
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: A deep learning toolbox to decode raw time-domain EEG.
5
5
  Home-page: https://github.com/robintibor/braindecode
6
6
  Author: Robin Tibor Schirrmeister
@@ -30,16 +30,7 @@ Description: Note: The old braindecode repository has been moved to
30
30
 
31
31
  pip install numpy
32
32
 
33
- 3. Install newest version of python-mne:
34
-
35
- .. code-block:: bash
36
-
37
- git clone git://github.com/mne-tools/mne-python.git
38
- cd mne-python
39
- python setup.py install
40
-
41
-
42
- 4. Install braindecode via pip:
33
+ 3. Install braindecode via pip:
43
34
 
44
35
  .. code-block:: bash
45
36
 
@@ -22,16 +22,7 @@ Installation
22
22
 
23
23
  pip install numpy
24
24
 
25
- 3. Install newest version of python-mne:
26
-
27
- .. code-block:: bash
28
-
29
- git clone git://github.com/mne-tools/mne-python.git
30
- cd mne-python
31
- python setup.py install
32
-
33
-
34
- 4. Install braindecode via pip:
25
+ 3. Install braindecode via pip:
35
26
 
36
27
  .. code-block:: bash
37
28
 
@@ -93,7 +93,7 @@ class BBCIDataset(object):
93
93
 
94
94
  def _determine_samplingrate(self):
95
95
  with h5py.File(self.filename, 'r') as h5file:
96
- fs = h5file['dat']['fs'][0, 0]
96
+ fs = h5file['nfo']['fs'][0, 0]
97
97
  assert isinstance(fs, int) or fs.is_integer()
98
98
  fs = int(fs)
99
99
  return fs
@@ -125,7 +125,7 @@ class BBCIDataset(object):
125
125
  Sensor names that match the pattern or all sensor names in the file.
126
126
  """
127
127
  with h5py.File(filename, 'r') as h5file:
128
- clab_set = h5file['dat']['clab'][:].squeeze()
128
+ clab_set = h5file['nfo']['clab'][:].squeeze()
129
129
  all_sensor_names = [''.join(chr(c) for c in h5file[obj_ref]) for
130
130
  obj_ref in clab_set]
131
131
  if pattern is not None:
@@ -137,7 +137,7 @@ class BBCIDataset(object):
137
137
  def _add_markers(self, cnt):
138
138
  with h5py.File(self.filename, 'r') as h5file:
139
139
  event_times_in_ms = h5file['mrk']['time'][:].squeeze()
140
- event_classes = h5file['mrk']['event']['desc'][:].squeeze()
140
+ event_classes = h5file['mrk']['event']['desc'][:].squeeze().astype(np.int64)
141
141
 
142
142
  # Check whether class names known and correct order
143
143
  class_name_set = h5file['nfo']['className'][:].squeeze()
@@ -192,26 +192,32 @@ def get_channelpos_from_angle(channame, chan_pos_list=CHANNEL_10_20_APPROX):
192
192
  if i[0].lower() == channame:
193
193
  # convert the 90/4th angular position into x, y, z
194
194
  p = i[1]
195
- ea, eb = p[0] * (90 / 4), p[1] * (90 / 4)
196
- ea = ea * math.pi / 180
197
- eb = eb * math.pi / 180
198
- x = math.sin(ea) * math.cos(eb)
199
- y = math.sin(eb)
200
- z = math.cos(ea) * math.cos(eb)
201
- # Calculate the stereographic projection.
202
- # Given a unit sphere with radius ``r = 1`` and center at
203
- # the origin. Project the point ``p = (x, y, z)`` from the
204
- # sphere's South pole (0, 0, -1) on a plane on the sphere's
205
- # North pole (0, 0, 1).
206
- #
207
- # The formula is:
208
- #
209
- # P' = P * (2r / (r + z))
210
- #
211
- # We changed the values to move the point of projection
212
- # further below the south pole
213
- mu = 1 / (1.3 + z)
214
- x *= mu
215
- y *= mu
195
+ x, y = _convert_2d_angle_to_2d_coord(*p)
216
196
  return x, y
217
197
  return None
198
+
199
+
200
+ def _convert_2d_angle_to_2d_coord(a,b):
201
+ # convert the 90/4th angular position into x, y, z
202
+ ea, eb = a * (90 / 4), b * (90 / 4)
203
+ ea = ea * math.pi / 180
204
+ eb = eb * math.pi / 180
205
+ x = math.sin(ea) * math.cos(eb)
206
+ y = math.sin(eb)
207
+ z = math.cos(ea) * math.cos(eb)
208
+ # Calculate the stereographic projection.
209
+ # Given a unit sphere with radius ``r = 1`` and center at
210
+ # the origin. Project the point ``p = (x, y, z)`` from the
211
+ # sphere's South pole (0, 0, -1) on a plane on the sphere's
212
+ # North pole (0, 0, 1).
213
+ #
214
+ # The formula is:
215
+ #
216
+ # P' = P * (2r / (r + z))
217
+ #
218
+ # We changed the values to move the point of projection
219
+ # further below the south pole
220
+ mu = 1 / (1.3 + z)
221
+ x *= mu
222
+ y *= mu
223
+ return x, y
@@ -162,19 +162,23 @@ class CropsFromTrialsIterator(object):
162
162
  Number of predictions ConvNet makes per one input. Can be computed
163
163
  by making a forward pass with the given input time length, the
164
164
  output length in 3rd dimension is n_preds_per_input.
165
+ seed: int
166
+ Random seed for initialization of `numpy.RandomState`.
165
167
 
166
168
  See Also
167
169
  --------
168
170
  braindecode.experiments.monitors.compute_preds_per_trial_for_set : Assigns predictions to trials, removes overlaps.
169
171
  """
170
- def __init__(self, batch_size, input_time_length, n_preds_per_input):
172
+ def __init__(self, batch_size, input_time_length, n_preds_per_input,
173
+ seed=(2017, 6, 28)):
171
174
  self.batch_size = batch_size
172
175
  self.input_time_length = input_time_length
173
176
  self.n_preds_per_input = n_preds_per_input
174
- self.rng = RandomState((2017, 6, 28))
177
+ self.seed = seed
178
+ self.rng = RandomState(self.seed)
175
179
 
176
180
  def reset_rng(self):
177
- self.rng = RandomState((2017, 6, 28))
181
+ self.rng = RandomState(self.seed)
178
182
 
179
183
  def get_batches(self, dataset, shuffle):
180
184
  # start always at first predictable sample, so
@@ -146,7 +146,8 @@ def lowpass_cnt(data, high_cut_hz, fs, filt_order=3, axis=0):
146
146
  return data_lowpassed
147
147
 
148
148
 
149
- def bandpass_cnt(data, low_cut_hz, high_cut_hz, fs, filt_order=3, axis=0):
149
+ def bandpass_cnt(data, low_cut_hz, high_cut_hz, fs, filt_order=3, axis=0,
150
+ filtfilt=False):
150
151
  """
151
152
  Bandpass signal applying **causal** butterworth filter of given order.
152
153
 
@@ -158,6 +159,8 @@ def bandpass_cnt(data, low_cut_hz, high_cut_hz, fs, filt_order=3, axis=0):
158
159
  high_cut_hz: float
159
160
  fs: float
160
161
  filt_order: int
162
+ filtfilt: bool
163
+ Whether to use filtfilt instead of lfilter
161
164
 
162
165
  Returns
163
166
  -------
@@ -182,7 +185,10 @@ def bandpass_cnt(data, low_cut_hz, high_cut_hz, fs, filt_order=3, axis=0):
182
185
  high = high_cut_hz / nyq_freq
183
186
  b, a = scipy.signal.butter(filt_order, [low, high], btype='bandpass')
184
187
  assert filter_is_stable(a), "Filter should be stable..."
185
- data_bandpassed = scipy.signal.lfilter(b, a, data, axis=axis)
188
+ if filtfilt:
189
+ data_bandpassed = scipy.signal.filtfilt(b, a, data, axis=axis)
190
+ else:
191
+ data_bandpassed = scipy.signal.lfilter(b, a, data, axis=axis)
186
192
  return data_bandpassed
187
193
 
188
194
 
@@ -77,3 +77,30 @@ def split_into_two_sets(dataset, first_set_fraction=None, n_first_set=None):
77
77
  second_set = apply_to_X_y(lambda a: a[n_first_set:], dataset)
78
78
  return first_set, second_set
79
79
 
80
+
81
+ def select_examples(dataset, indices):
82
+ """
83
+ Select examples from dataset.
84
+
85
+ Parameters
86
+ ----------
87
+ dataset: :class:`.SignalAndTarget`
88
+ indices: list of int, 1d-array of int
89
+ Indices to select
90
+
91
+ Returns
92
+ -------
93
+ reduced_set: :class:`.SignalAndTarget`
94
+ Dataset with only examples selected.
95
+ """
96
+ # probably not necessary
97
+ indices = np.array(indices)
98
+ if hasattr(dataset.X, 'ndim'):
99
+ # numpy array
100
+ new_X = np.array(dataset.X)[indices]
101
+ else:
102
+ # list
103
+ new_X = [dataset.X[i] for i in indices]
104
+ new_y = np.asarray(dataset.y)[indices]
105
+ return SignalAndTarget(new_X, new_y)
106
+
@@ -9,9 +9,6 @@ from braindecode.datautil.util import ms_to_samples
9
9
 
10
10
  log = logging.getLogger(__name__)
11
11
 
12
- marker_def = OrderedDict(
13
- (['Right', 1], ['Left', [2]], ['Rest', 3], ['Feet', 4]))
14
-
15
12
 
16
13
  def create_signal_target_from_raw_mne(
17
14
  raw, name_to_start_codes, epoch_ival_ms,
@@ -134,6 +134,9 @@ class Experiment(object):
134
134
  Whether to use cuda.
135
135
  pin_memory: bool, optional
136
136
  Whether to pin memory of inputs and targets of batch.
137
+ do_early_stop: bool
138
+ Whether to do an early stop at all. If true, reset to best model
139
+ even in case experiment does not run after early stop.
137
140
 
138
141
  Attributes
139
142
  ----------
@@ -145,10 +148,21 @@ class Experiment(object):
145
148
  monitors, stop_criterion, remember_best_column,
146
149
  run_after_early_stop,
147
150
  model_loss_function=None,
148
- batch_modifier=None, cuda=True, pin_memory=False):
151
+ batch_modifier=None, cuda=True, pin_memory=False,
152
+ do_early_stop=True):
153
+ if run_after_early_stop:
154
+ assert do_early_stop == True, ("Can only run after early stop if "
155
+ "doing an early stop")
156
+ if do_early_stop:
157
+ assert valid_set is not None
158
+ assert remember_best_column is not None
149
159
  self.model = model
150
160
  self.datasets = OrderedDict(
151
161
  (('train', train_set), ('valid', valid_set), ('test', test_set)))
162
+ if valid_set is None:
163
+ self.datasets.pop('valid')
164
+ assert run_after_early_stop == False
165
+ assert do_early_stop == False
152
166
  self.iterator = iterator
153
167
  self.loss_function = loss_function
154
168
  self.optimizer = optimizer
@@ -164,6 +178,8 @@ class Experiment(object):
164
178
  self.before_stop_df = None
165
179
  self.rememberer = None
166
180
  self.pin_memory = pin_memory
181
+ self.do_early_stop = do_early_stop
182
+
167
183
 
168
184
  def run(self):
169
185
  """
@@ -171,11 +187,12 @@ class Experiment(object):
171
187
  """
172
188
  self.setup_training()
173
189
  log.info("Run until first stop...")
174
- self.run_until_early_stop()
175
- # always setup for second stop, in order to get best model
176
- # even if not running after early stop...
177
- log.info("Setup for second stop...")
178
- self.setup_after_stop_training()
190
+ self.run_until_first_stop()
191
+ if self.do_early_stop:
192
+ # always setup for second stop, in order to get best model
193
+ # even if not running after early stop...
194
+ log.info("Setup for second stop...")
195
+ self.setup_after_stop_training()
179
196
  if self.run_after_early_stop:
180
197
  log.info("Run until second stop...")
181
198
  self.run_until_second_stop()
@@ -186,19 +203,20 @@ class Experiment(object):
186
203
  initialize monitoring.
187
204
  """
188
205
  # reset remember best extension in case you rerun some experiment
189
- self.rememberer = RememberBest(self.remember_best_column)
206
+ if self.do_early_stop:
207
+ self.rememberer = RememberBest(self.remember_best_column)
190
208
  self.epochs_df = pd.DataFrame()
191
209
  set_random_seeds(seed=2382938, cuda=self.cuda)
192
210
  if self.cuda:
193
211
  assert th.cuda.is_available(), "Cuda not available"
194
212
  self.model.cuda()
195
213
 
196
- def run_until_early_stop(self):
214
+ def run_until_first_stop(self):
197
215
  """
198
216
  Run training and evaluation using only training set for training
199
217
  until stop criterion is fulfilled.
200
218
  """
201
- self.run_until_stop(self.datasets, remember_best=True)
219
+ self.run_until_stop(self.datasets, remember_best=self.do_early_stop)
202
220
 
203
221
  def run_until_second_stop(self):
204
222
  """
@@ -313,14 +331,20 @@ class Experiment(object):
313
331
 
314
332
  """
315
333
  self.model.eval()
316
- input_vars = np_to_var(inputs, pin_memory=self.pin_memory)
317
- target_vars = np_to_var(targets, pin_memory=self.pin_memory)
334
+ input_vars = np_to_var(inputs, pin_memory=self.pin_memory,
335
+ volatile=True)
336
+ target_vars = np_to_var(targets, pin_memory=self.pin_memory,
337
+ volatile=True)
318
338
  if self.cuda:
319
339
  input_vars = input_vars.cuda()
320
340
  target_vars = target_vars.cuda()
321
341
  outputs = self.model(input_vars)
322
342
  loss = self.loss_function(outputs, target_vars)
323
- outputs = outputs.cpu().data.numpy()
343
+ if hasattr(outputs, 'cpu'):
344
+ outputs = outputs.cpu().data.numpy()
345
+ else:
346
+ # assume it is iterable
347
+ outputs = [o.cpu().data.numpy() for o in outputs]
324
348
  loss = loss.cpu().data.numpy()
325
349
  return outputs, loss
326
350
 
@@ -10,10 +10,15 @@ class MisclassMonitor(object):
10
10
  ----------
11
11
  col_suffix: str, optional
12
12
  Name of the column in the monitoring output.
13
+ threshold_for_binary_case: bool, optional
14
+ In case of binary classification with only one output prediction
15
+ per target, define the threshold for separating the classes, i.e.
16
+ 0.5 for sigmoid outputs, or np.log(0.5) for log sigmoid outputs
13
17
  """
14
18
 
15
- def __init__(self, col_suffix='misclass'):
19
+ def __init__(self, col_suffix='misclass', threshold_for_binary_case=None):
16
20
  self.col_suffix = col_suffix
21
+ self.threshold_for_binary_case = threshold_for_binary_case
17
22
 
18
23
  def monitor_epoch(self, ):
19
24
  return
@@ -28,11 +33,19 @@ class MisclassMonitor(object):
28
33
  # or just
29
34
  # examples x classes
30
35
  # make sure not to remove first dimension if it only has size one
31
- only_one_row = preds.shape[0] == 1
32
- pred_labels = np.argmax(preds, axis=1).squeeze()
33
- # add first dimension again if needed
34
- if only_one_row:
35
- pred_labels = pred_labels[None]
36
+ if preds.ndim > 1:
37
+ only_one_row = preds.shape[0] == 1
38
+
39
+ pred_labels = np.argmax(preds, axis=1).squeeze()
40
+ # add first dimension again if needed
41
+ if only_one_row:
42
+ pred_labels = pred_labels[None]
43
+ else:
44
+ assert self.threshold_for_binary_case is not None, (
45
+ "In case of only one output, please supply the "
46
+ "threshold_for_binary_case parameter")
47
+ # binary classification case... assume logits
48
+ pred_labels = np.int32(preds > self.threshold_for_binary_case)
36
49
  # now examples x time or examples
37
50
  all_pred_labels.extend(pred_labels)
38
51
  targets = all_targets[i_batch]
@@ -65,8 +65,7 @@ class Deep4Net(object):
65
65
  later_pool_class = pool_class_dict[self.later_pool_mode]
66
66
  model = nn.Sequential()
67
67
  if self.split_first_layer:
68
- model.add_module('dimshuffle',
69
- Expression(lambda x: x.permute(0, 3, 2, 1)))
68
+ model.add_module('dimshuffle', Expression(_transpose_time_to_spat))
70
69
  model.add_module('conv_time', nn.Conv2d(1, self.n_filters_time,
71
70
  (
72
71
  self.filter_time_length, 1),
@@ -131,6 +130,7 @@ class Deep4Net(object):
131
130
  self.filter_length_4, 4)
132
131
 
133
132
 
133
+ model.eval()
134
134
  if self.final_conv_length == 'auto':
135
135
  out = model(np_to_var(np.ones(
136
136
  (1, self.in_chans, self.input_time_length,1),
@@ -141,15 +141,7 @@ class Deep4Net(object):
141
141
  nn.Conv2d(self.n_filters_4, self.n_classes,
142
142
  (self.final_conv_length, 1), bias=True))
143
143
  model.add_module('softmax', nn.LogSoftmax())
144
- # remove empty dim at end and potentially remove empty time dim
145
- # do not just use squeeze as we never want to remove first dim
146
- def squeeze_output(x):
147
- assert x.size()[3] == 1
148
- x = x[:,:,:,0]
149
- if x.size()[2] == 1:
150
- x = x[:,:,0]
151
- return x
152
- model.add_module('squeeze', Expression(squeeze_output))
144
+ model.add_module('squeeze', Expression(_squeeze_final_output))
153
145
 
154
146
  # Initialization, xavier is same as in our paper...
155
147
  # was default from lasagne
@@ -161,6 +153,9 @@ class Deep4Net(object):
161
153
  init.xavier_uniform(model.conv_spat.weight, gain=1)
162
154
  if not self.batch_norm:
163
155
  init.constant(model.conv_spat.bias, 0)
156
+ if self.batch_norm:
157
+ init.constant(model.bnorm.weight, 1)
158
+ init.constant(model.bnorm.bias, 0)
164
159
  param_dict = dict(list(model.named_parameters()))
165
160
  for block_nr in range(2,5):
166
161
  conv_weight = param_dict['conv_{:d}.weight'.format(block_nr)]
@@ -177,4 +172,21 @@ class Deep4Net(object):
177
172
  init.xavier_uniform(model.conv_classifier.weight, gain=1)
178
173
  init.constant(model.conv_classifier.bias, 0)
179
174
 
175
+ # Start in eval mode
176
+ model.eval()
180
177
  return model
178
+
179
+
180
+ # remove empty dim at end and potentially remove empty time dim
181
+ # do not just use squeeze as we never want to remove first dim
182
+ def _squeeze_final_output(x):
183
+ assert x.size()[3] == 1
184
+ x = x[:,:,:,0]
185
+ if x.size()[2] == 1:
186
+ x = x[:,:,0]
187
+ return x
188
+
189
+
190
+ def _transpose_time_to_spat(x):
191
+ return x.permute(0, 3, 2, 1)
192
+
@@ -8,7 +8,7 @@ from braindecode.torch_ext.util import np_to_var
8
8
  class Expression(torch.nn.Module):
9
9
  """
10
10
  Compute given expression on forward pass.
11
-
11
+
12
12
  Parameters
13
13
  ----------
14
14
  expression_fn: function
@@ -37,7 +37,7 @@ class Expression(torch.nn.Module):
37
37
  class AvgPool2dWithConv(torch.nn.Module):
38
38
  """
39
39
  Compute average pooling using a convolution, to have the dilation parameter.
40
-
40
+
41
41
  Parameters
42
42
  ----------
43
43
  kernel_size: (int,int)
@@ -52,7 +52,10 @@ class AvgPool2dWithConv(torch.nn.Module):
52
52
  self.kernel_size = kernel_size
53
53
  self.stride = stride
54
54
  self.dilation = dilation
55
- self.weights = None
55
+ # don't name them "weights" to
56
+ # make sure these are not accidentally used by some procedure
57
+ # that initializes parameters or something
58
+ self._pool_weights = None
56
59
 
57
60
  def forward(self, x):
58
61
  # Create weights for the convolution on demand:
@@ -60,11 +63,11 @@ class AvgPool2dWithConv(torch.nn.Module):
60
63
  in_channels = x.size()[1]
61
64
  weight_shape = (in_channels, 1,
62
65
  self.kernel_size[0], self.kernel_size[1])
63
- if self.weights is None or (
64
- (tuple(self.weights.size()) != tuple(weight_shape)) or (
65
- self.weights.is_cuda != x.is_cuda
66
+ if self._pool_weights is None or (
67
+ (tuple(self._pool_weights.size()) != tuple(weight_shape)) or (
68
+ self._pool_weights.is_cuda != x.is_cuda
66
69
  ) or (
67
- self.weights.data.type() != x.data.type()
70
+ self._pool_weights.data.type() != x.data.type()
68
71
  )):
69
72
  n_pool = np.prod(self.kernel_size)
70
73
  weights = np_to_var(
@@ -72,9 +75,48 @@ class AvgPool2dWithConv(torch.nn.Module):
72
75
  weights = weights.type_as(x)
73
76
  if x.is_cuda:
74
77
  weights = weights.cuda()
75
- self.weights = weights
78
+ self._pool_weights = weights
76
79
 
77
- pooled = F.conv2d(x, self.weights, bias=None, stride=self.stride,
80
+ pooled = F.conv2d(x, self._pool_weights, bias=None, stride=self.stride,
78
81
  dilation=self.dilation,
79
82
  groups=in_channels,)
80
83
  return pooled
84
+
85
+
86
+ class IntermediateOutputWrapper(torch.nn.Module):
87
+ """Wraps network model such that outputs of intermediate layers can be returned.
88
+ forward() returns list of intermediate activations in a network during forward pass.
89
+
90
+ Parameters
91
+ ----------
92
+ to_select : list
93
+ list of module names for which activation should be returned
94
+ model : model object
95
+ network model
96
+
97
+ Examples
98
+ --------
99
+ >>> model = Deep4Net()
100
+ >>> select_modules = ['conv_spat','conv_2','conv_3','conv_4'] # Specify intermediate outputs
101
+ >>> model_pert = IntermediateOutputWrapper(select_modules,model) # Wrap model
102
+ """
103
+ def __init__(self, to_select, model):
104
+ if not len(list(model.children()))==len(list(model.named_children())):
105
+ raise Exception('All modules in model need to have names!')
106
+
107
+ super(IntermediateOutputWrapper, self).__init__()
108
+
109
+ modules_list = model.named_children()
110
+ for key, module in modules_list:
111
+ self.add_module(key, module)
112
+ self._modules[key].load_state_dict(module.state_dict())
113
+ self._to_select = to_select
114
+
115
+ def forward(self,x):
116
+ # Call modules individually and append activation to output if module is in to_select
117
+ o = []
118
+ for name, module in self._modules.items():
119
+ x = module(x)
120
+ if name in self._to_select:
121
+ o.append(x)
122
+ return o
@@ -12,7 +12,7 @@ def np_to_var(X, requires_grad=False, dtype=None, pin_memory=False, **var_kwargs
12
12
 
13
13
  Parameters
14
14
  ----------
15
- X: ndarray or list
15
+ X: ndarray or list or number
16
16
  Input arrays
17
17
  requires_grad: bool
18
18
  passed on to Variable constructor
@@ -24,6 +24,8 @@ def np_to_var(X, requires_grad=False, dtype=None, pin_memory=False, **var_kwargs
24
24
  -------
25
25
  var: `torch.autograd.Variable`
26
26
  """
27
+ if not hasattr(X, '__len__'):
28
+ X = [X]
27
29
  X = np.asarray(X)
28
30
  if dtype is not None:
29
31
  X = X.astype(dtype)
@@ -58,3 +60,17 @@ def set_random_seeds(seed, cuda):
58
60
  if cuda:
59
61
  th.cuda.manual_seed_all(seed)
60
62
  np.random.seed(seed)
63
+
64
+
65
+ def confirm_gpu_availability():
66
+ """
67
+ Should crash if gpu not available, attempts to create a FloatTensor on GPU.
68
+ Returns
69
+ -------
70
+ success: bool
71
+ Always returns true, should crash if gpu not available
72
+ """
73
+ a = th.FloatTensor(1).cuda()
74
+ # Just make sure a is not somehow removed by any smart compiling,
75
+ # probably not necessary.
76
+ return a is not None
@@ -0,0 +1 @@
1
+ __version__ = "0.3.2"