-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathinf_config.py
257 lines (210 loc) · 11.3 KB
/
inf_config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
import os
from typing import List, Any
import numpy as np
from pathlib import Path
from inf_narco_features import HypnodensityFeatures
class AppConfig(object):
edf_file: Path
def __init__(self):
# Model folder
self.models_used = ['ac_rh_ls_lstm_01', 'ac_rh_ls_lstm_02',
'ac_rh_ls_lstm_03', 'ac_rh_ls_lstm_04',
'ac_rh_ls_lstm_05', 'ac_rh_ls_lstm_06',
'ac_rh_ls_lstm_07', 'ac_rh_ls_lstm_08',
'ac_rh_ls_lstm_09', 'ac_rh_ls_lstm_10',
'ac_rh_ls_lstm_11', 'ac_rh_ls_lstm_12',
'ac_rh_ls_lstm_13', 'ac_rh_ls_lstm_14',
'ac_rh_ls_lstm_15', 'ac_rh_ls_lstm_16']
# Uncomment the following when running validation comparison given in readme file.
# self.models_used = ['ac_rh_ls_lstm_01']
# Hypnodensity classification settings
self.relevance_threshold = 1
# Predictions above this value will be considered narcoleptic
self.narco_threshold = 0.0
self.fs = np.array(100, dtype=float)
self.fs_high = np.array(0.2, dtype=float)
self.fs_low = np.array(49, dtype=float)
# Wrapper hooks for auditing processing time of various parts of the app.
self.audit = {'encoding': False, 'hypnodensity': False, 'diagnosis': False}
self.channels = ['C3', 'C4', 'O1', 'O2', 'EOG-L', 'EOG-R', 'EMG', 'A1', 'A2']
# Size of cross correlation in seconds - so in samples this will be
# sum([200 200 400 400 40 ]) == 1240 + 400 (for EOGLR) == 1640
self.cc_size = {'C3': 2, 'C4': 2, 'O1': 2, 'O2': 2,
'EOG-L': 4, 'EOG-R': 4,
'EMG': 0.4,
'A1': [], 'A2': [],
}
self.channels_used = dict.fromkeys(self.channels)
self.loaded_channels = dict.fromkeys(self.channels)
# Make it easier to run stanford-stages code from other repositories, or scripts in other directories.
this_path = Path(__file__).parent.absolute()
self.psg_noise_file_pathname = str(this_path.joinpath('ml/noiseM.mat'))
self.hypnodensity_model_root_path = str(this_path.joinpath('ml/ac'))
self.hypnodensity_scale_path = str(this_path.joinpath('ml/scaling/'))
# self.psg_noise_file_pathname = 'F:/ml/noiseM.mat'
# self.hypnodensity_model_root_path = 'F:/ml/'
# self.hypnodensity_scale_path = 'F:/ml/scaling/'
# Related to classifying narcolepsy from hypnodensity features
self.narco_classifier_path: str = str(this_path.joinpath('ml/gp/'))
self.edf_file = None
# lights off and on indicate the number of seconds from the start of the psg recording until the
# "lights off" and "lights on" events for a formal psg recording. Negative values indicate time
# elapsed from the end of the study. For example: lights_on=-1 means the lights were turned on 1 s
# prior to the recording ending. Leave values as None to include all data from the start of the
# recording until the end of the reocrding, otherwise only the time between lights_off until
# lights_on will be evaluated for hypnodensity features to be used in narcolepsy detection, and
# for sleep staging scoring. Epochs outside lights off/on range and lights on epoch itself are
# scored as '7' in the hypnogram to indicate unscored epochs. Similiarly, these epochs are
# excluded from the features extraction step that is used to predict narcolepsy.
self._lights_off: int = None
self._lights_on: int = None
# Set to 0 to turn off cross-validation. Otherwise a model must have been trained previously for each fold desired. This is for the gp classifier.
self.narco_prediction_num_folds = 0
self.narco_prediction_scales = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
self.narco_prediction_selected_features = None
# Uncomment to use the following 153 features
# self.narco_prediction_selected_features = [0, 2, 4, 8, 9, 10, 13, 15, 16, 17, 19, 21, 24, 25, 28, 31, 32, 34, 35, 39, 46, 47, 50, 52, 58, 61, 63, 64, 77, 93, 102, 110, 119, 121, 122, 123, 126, 127, 131, 133, 137, 150, 154, 156, 157, 160, 162, 166, 167, 173, 176, 181, 182, 192, 195, 197, 199, 205, 206, 208, 210, 215, 220, 223, 227, 236, 237, 245, 246, 257, 258, 260, 262, 267, 273, 274, 275, 281, 284, 285, 288, 293, 294, 295, 302, 311, 316, 317, 319, 321, 331, 333, 334, 337, 341, 348, 350, 356, 357, 362, 363, 364, 368, 369, 371, 373, 376, 378, 380, 381, 387, 390, 392, 393, 394, 398, 400, 403, 404, 406, 407, 412, 414, 415, 419, 422, 428, 429, 436, 437, 441, 442, 443, 445, 447, 454, 455, 456, 457, 459, 460, 461, 462, 463, 469, 470, 471, 474, 475, 477, 481, 482, 485]
# Feature scaling options include:
# ['range']: (x-median(X))/(percentile(X, 85)-percentile(X, 15)) where x is the feature and X is the population sample of the features
# 'z': (x-mean(X))/std(X) - This is gives 0 mean and unit variance.
# 'unscaled' or None: (x - 0) / 1 - This is unscaled; no change to the features.
self.narco_feature_scaling_method = 'range'
# Set to False to minimize printed output.
self.verbose: bool = True
@staticmethod
def str2int(value):
if isinstance(value, str):
try:
value = int(value)
except ValueError:
value = None
return value
def set_narco_feature_selection(self, feature_selections):
if feature_selections is None:
self.narco_prediction_selected_features = None
elif isinstance(feature_selections, list):
self.narco_prediction_selected_features = feature_selections
elif feature_selections.lower() == 'all':
self.narco_prediction_selected_features = list(range(HypnodensityFeatures.num_features))
else:
print('Unhandled value for feature_selections, which should be a list or "all"')
exit(1)
@property
def lights_off(self):
return self._lights_off
@lights_off.setter
def lights_off(self, value: int):
self._lights_off = self.str2int(value)
if self._lights_off is None:
self._lights_off = 0
@property
def lights_on(self):
return self._lights_on
@lights_on.setter
def lights_on(self, value: int):
self._lights_on = self.str2int(value)
if self.lights_on == 0:
self.lights_on = None
def get_lights_off_epoch(self, epoch_len: int = 15):
return self.sec2epoch(self.lights_off, epoch_len)
def get_lights_on_epoch(self, epoch_len: int= 15):
return self.sec2epoch(self.lights_on, epoch_len)
@staticmethod
def sec2epoch(sec: int, epoch_len: int = 15):
# Translates an integer value in seconds to the equivalent epoch based on the radix epoch_len,
# which is also given in seconds. This works here for negative values as well, which are assumed to be
# referenced from the end of the study, where a -1 sec would be the equivalent of -1 epoch,
# which includeds the last epoch of the study using python indexing (e.g. study[-1]).
# Returns None if sec is not entered or None
if sec is None:
return None
else:
return sec // epoch_len # np.floor_divide(sec, epoch_len)
# Define Config
class Config(object):
@staticmethod
def get(model_name):
if model_name[0:2] == 'ac':
return ACConfig(model_name)
else:
raise Exception
def __getitem__(self, itemname):
return object.__getattribute__(self, itemname)
def __init__(self, scope, num_features, num_hidden, segsize, lstm, num_classes, batch_size, max_train_len, atonce,
restart=True, model_name='small_lstm', is_train=False, root_model_dir='./'):
# Initially there were several different scopes to consider. Over time, the 'ac' scope became the dominant choice
# The check for scope matching the path stem is an attempt not to penalize users for including the 'ac' subpath
# as part of their root model directory (e.g in their configuration file)
root_model_path = Path(root_model_dir)
if root_model_path.stem == scope:
self.hypnodensity_model_dir = os.path.join(root_model_dir, model_name)
else:
self.hypnodensity_model_dir = os.path.join(root_model_dir, scope, model_name)
self.model_name = model_name
self.scope = scope
self.is_training = is_train
self.num_features = num_features
self.num_classes = num_classes
self.batch_size = batch_size
self.restart = restart
self.lstm = lstm
self.num_hidden = num_hidden
self.keep_prob = 0.5
self.segsize = segsize
self.eval_nseg_atonce = atonce
self.max_train_len = max_train_len
self.save_freq = 2
self.max_steps = 2000
self.init_learning_rate = 0.005
self.learning_rate_decay = 750
def checkpoint_file(self, ckpt=0):
if ckpt == 0:
return os.path.join(self.hypnodensity_model_dir, 'model.ckpt')
else:
return os.path.join(self.hypnodensity_model_dir, 'model.ckpt-%.0f' % ckpt)
# Now we can pass Config to ACConfig for construction; inheritence.
class ACConfig(Config):
def __init__(self, restart=True, model_name='ac_rh_ls_lstm_01', is_training=False, root_model_dir='./'):
print('model: ' + model_name)
if model_name[3:5] == 'lh':
num_hidden = 256
elif model_name[3:5] == 'rh':
np.random.seed(int(model_name[-2:]))
num_hidden = 256 + np.round(np.random.rand(1) * 128)
num_hidden = num_hidden[0].astype(int)
else:
num_hidden = 128
if model_name[6:8] == 'ls':
segsize = 60
atonce = 1000 # 2 batches, runs out of memory though :(
# atonce = 1500 #
# atonce = 600 # 173 seconds
# atonce = 500 # 154 seconds
# atonce = 400 # 154 seconds
# atonce = 200 # 150 seconds
# atonce = 100 # 110, 121
# atonce = 60 # 120 (num batches = 30)
# atonce = 50 # 104
# atonce = 45 # 102, 116 (allow growth = true, num batches = 40
# atonce = 40 # 101, 123, 114 (allow growth=true) numbatches = 44
# atonce = 35 # 103, 132
# atonce = 30 # 102, 116, allow growth=true, num batches = 59
# atonce = 10 # 118, 133 (num batches = 176)
else:
segsize = 20
atonce = 3000
if model_name[9:11] == 'ff':
lstm = False
else:
lstm = True
if is_training:
batch_size = 30
else:
batch_size = 1
scope = 'ac'
num_features = 1640
num_classes = 5
max_train_len = 14400
super(ACConfig, self).__init__(scope, num_features, num_hidden, segsize, lstm, num_classes, batch_size,
max_train_len, atonce, restart, model_name, is_training, root_model_dir)