SleePyPhases 0.8.1__tar.gz → 0.8.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 (63) hide show
  1. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/PKG-INFO +1 -1
  2. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/Plugin.py +2 -1
  3. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/Eval.py +5 -31
  4. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/EvalReport.py +16 -10
  5. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/Extract.py +6 -2
  6. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/ExtractFeatures.py +100 -12
  7. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/Setup.py +25 -2
  8. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/Training.py +33 -5
  9. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases.egg-info/PKG-INFO +1 -1
  10. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/setup.py +1 -1
  11. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_Extract.py +1 -0
  12. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/LICENSE +0 -0
  13. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/README.md +0 -0
  14. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/DataManipulation.py +0 -0
  15. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/EventScorer.py +0 -0
  16. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/FeatureExtraction.py +0 -0
  17. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/MultiScorer.py +0 -0
  18. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/Plot.py +0 -0
  19. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/PreManipulation.py +0 -0
  20. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/RecordFeatureExtraction.py +0 -0
  21. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/Reporter.py +0 -0
  22. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/SignalPreprocessing.py +0 -0
  23. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/SleePyPhases.py +0 -0
  24. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/SleepMetaData.py +0 -0
  25. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/__init__.py +0 -0
  26. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/config.yaml +0 -0
  27. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/BuildDataset.py +0 -0
  28. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/DataAnalysis.py +0 -0
  29. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/DataReport.py +0 -0
  30. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/EvalPlotExamples.py +0 -0
  31. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/ExportMetadataToMeticalDB.py +0 -0
  32. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/ExtractEvents.py +0 -0
  33. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/ExtractRecordFeatures.py +0 -0
  34. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/GatherMetadata.py +0 -0
  35. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/LogCleanup.py +0 -0
  36. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/LogOverview.py +0 -0
  37. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/Predict.py +0 -0
  38. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/TestRun.py +0 -0
  39. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/ThresholdOptimisation.py +0 -0
  40. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/Validation.py +0 -0
  41. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/VisualizeConfig.py +0 -0
  42. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases/phases/__init__.py +0 -0
  43. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases.egg-info/SOURCES.txt +0 -0
  44. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases.egg-info/dependency_links.txt +0 -0
  45. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases.egg-info/requires.txt +0 -0
  46. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/SleePyPhases.egg-info/top_level.txt +0 -0
  47. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/pyproject.toml +0 -0
  48. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/setup.cfg +0 -0
  49. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/__init__.py +0 -0
  50. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/conftest.py +0 -0
  51. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/__init__.py +0 -0
  52. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_BuildDataset.py +0 -0
  53. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_BuildDatasetFolded.py +0 -0
  54. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_BuildDatasetMetadataSync.py +0 -0
  55. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_BuildDatasetSegments.py +0 -0
  56. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_DataManipulation.py +0 -0
  57. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_Eval.py +0 -0
  58. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_EventScorer.py +0 -0
  59. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_PreManipulation.py +0 -0
  60. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_RecordFeatureExtraction.py +0 -0
  61. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_RecordProcessor.py +0 -0
  62. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_Setup.py +0 -0
  63. {sleepyphases-0.8.1 → sleepyphases-0.8.2}/tests/unit/test_SignalPreprocessing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SleePyPhases
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: A framwork for creating deep learning pipelines for sleep data
5
5
  Home-page: https://gitlab.com/sleep-is-all-you-need/sleepyphases
6
6
  Author: Franz Ehrlich
@@ -33,7 +33,7 @@ class Plugin(PluginAdapter):
33
33
  "Setup": (Setup, ["dataversionmanager"]),
34
34
  "Extract": (Extract, ["removedRecordIds", "data-processed", "data-features", "record-preprocessed"]),
35
35
  "BuildDataset": (BuildDataset, ["dataset-training", "dataset-validation", "dataset-test", "dataset-bySplit", "record-data"]),
36
- "Training": (Training, ["modelState", "modelStateConfig"]),
36
+ "Training": (Training, ["modelState", "modelStateConfig", "trainedModel"]),
37
37
  "Validation": (Validation, []),
38
38
  "ThresholdOptimisation": (ThresholdOptimisation, ["threshold", "validationResult"]),
39
39
  "Eval": (Eval, ["evalResults", "eventResults"]),
@@ -56,6 +56,7 @@ class Plugin(PluginAdapter):
56
56
  "dataversionmanager": ["data-processed", "datafold", "dataversion"],
57
57
  "removedRecordIds": ["data-processed"],
58
58
  "modelState": ["dataset-bySplit", "modelName", "model", "inputShape", "segmentManipulation", "trainingParameter", "fold"],
59
+ "trainedModel": ["dataset-bySplit", "modelName", "model", "inputShape", "segmentManipulation", "trainingParameter", "fold", "pretraining"],
59
60
  "modelStateConfig": ["modelState"],
60
61
  "threshold": ["modelState", "eventEval", "thresholdMetric", "fixedThreshold"],
61
62
  "validationResult": ["modelState", "segmentManipulationEval", "thresholdMetric", "optimizeOn"],
@@ -16,30 +16,6 @@ class Eval(Phase):
16
16
  allowTraining: bool = False
17
17
  exampleCount: int = 3
18
18
 
19
- def getModel(self) -> Model:
20
- import torch
21
-
22
- pretrainedPath = self.getConfig("pretraining.pretrainedModel")
23
-
24
- if not Path(pretrainedPath).exists():
25
- with self.project:
26
- pretrainingConfig = self.getConfig("pretraining")
27
- self.project.config["dataversion"] = {}
28
- self.project.config.update(pretrainingConfig)
29
- self.project.trigger("configChanged", None)
30
- preModelState = self.project.getData("modelState", Model)
31
-
32
- else:
33
- deviceName = "cuda" if torch.cuda.is_available() else "cpu"
34
- preModelState = torch.load(pretrainedPath, map_location=torch.device(deviceName))
35
-
36
- # set pretrain
37
- model = ModelManager.getModel(True)
38
- model.build()
39
- model.loadState(preModelState)
40
-
41
- return model
42
-
43
19
  def getTestMetadata(self):
44
20
  if not bool(self.getConfig("evalOn", {})):
45
21
  return self.getData("metadata", list)
@@ -95,10 +71,9 @@ class Eval(Phase):
95
71
  def segmentEvaluation(self, name):
96
72
  testData = self.getTestData()
97
73
  self.setConfig("datasetSplits", list(self.getConfig("dataversion.split", {}).keys()))
98
- model = self.getModel()
74
+ model = self.getData("trainedModel")
99
75
 
100
- # setup data and scorer
101
-
76
+ # setup data and scorer
102
77
  classNums = self.getConfig("numClasses", [2])
103
78
  labelNames = self.getConfig("classification.labelNames")
104
79
  ignoreIndex = self.getConfig("classification.ignoreIndex", -1)
@@ -111,12 +86,11 @@ class Eval(Phase):
111
86
  threshold = threshold or self.getData("threshold", list)
112
87
  scorer.setThresholds(threshold)
113
88
 
114
- manipulationAfterPredict = self.getConfig("manipulationAfterPredict", False)
89
+ manipulationAfterPredict = self.getConfig("manipulationAfterPredict", [])
115
90
  da = DataManipulation.getInstance(manipulationAfterPredict, "test", self.project.config, threshold=threshold)
116
91
 
117
92
  for data in tqdm(testData):
118
93
  x, truth = data
119
-
120
94
  prediction = model.predict(x, returnNumpy=False)
121
95
  prediction, truth = da((prediction, truth))
122
96
 
@@ -162,7 +136,7 @@ class Eval(Phase):
162
136
  with self.project:
163
137
  self.project.config.update(self.getConfig("eventEval", {}))
164
138
 
165
- evalMetrics = self.getConfig("eval.metrics")
139
+ evalMetrics = self.getConfig("eval.metrics", self.getConfig("trainingParameter.validationMetrics"))
166
140
  classNums = self.getConfig("numClasses", [2])
167
141
  labelNames = self.getConfig("classification.labelNames")
168
142
  predictionNames = self.getConfig("classification.predictionSignals")
@@ -186,7 +160,7 @@ class Eval(Phase):
186
160
  # metaData["indexArousal"] = metaData["indexArousal"].astype(float)
187
161
  numClasses = self.getConfig("numClasses")
188
162
 
189
- model = self.getModel()
163
+ model = self.getData("trainedModel")
190
164
 
191
165
  resultRows = []
192
166
  result = {}
@@ -177,11 +177,13 @@ class EvalReport(Phase):
177
177
 
178
178
  def main(self):
179
179
  import pandas as pd
180
+ eventEvalEnabled = bool(self.getConfig("eventEval", True))
180
181
 
181
182
  threshold = self.getConfig("fixedThreshold", False)
182
183
  self.threshold = threshold or self.getData("threshold", float)
183
184
 
184
- modelConfigString = self.project.getDataFromName("eventResults").getTagString()
185
+ resultDataName = "eventResults" if eventEvalEnabled else "evalResults"
186
+ modelConfigString = self.project.getDataFromName(resultDataName).getTagString()
185
187
 
186
188
  evalPath = self.getConfig("eval-path", "eval/")
187
189
  evalPath = f"{evalPath}/{modelConfigString}/"
@@ -206,13 +208,16 @@ class EvalReport(Phase):
206
208
  df.to_csv(f"{evalPath}recordResultsSegment.csv", index=False)
207
209
 
208
210
  self.logSuccess(f"result segments: {segmentResults}")
209
- resultRows, result = self.getData("eventResults", list)
210
- self.logSuccess(f"result events: {result}")
211
- df = pd.DataFrame(resultRows)
211
+ if eventEvalEnabled:
212
+ resultRows, result = self.getData("eventResults", list)
213
+ self.logSuccess(f"result events: {result}")
214
+ df = pd.DataFrame(resultRows)
215
+ else:
216
+ result = {}
212
217
 
213
218
 
214
219
  metrics = self.getConfig("trainingParameter.validationMetrics")
215
- evalMetrics = self.getConfig("eval.metrics")
220
+ evalMetrics = self.getConfig("eval.metrics", metrics)
216
221
  labelNames = self.getConfig("classification.labelNames")
217
222
 
218
223
  results = {}
@@ -225,12 +230,13 @@ class EvalReport(Phase):
225
230
  if metricName in segmentResults:
226
231
  results[f"{metricName}-seg"] = segmentResults[metricName]
227
232
 
228
- relMetaData = self.getConfig("eval.clinicalMetrics")
229
- for m in relMetaData:
230
- df[f"{m}-prediction-diff"] = df[f"{m}-prediction"] - df[f"{m}-truth"]
231
- df[f"{m}-prediction-error"] = df[f"{m}-prediction-diff"].abs()
233
+ if eventEvalEnabled:
234
+ relMetaData = self.getConfig("eval.clinicalMetrics")
235
+ for m in relMetaData:
236
+ df[f"{m}-prediction-diff"] = df[f"{m}-prediction"] - df[f"{m}-truth"]
237
+ df[f"{m}-prediction-error"] = df[f"{m}-prediction-diff"].abs()
232
238
 
233
- df.to_csv(f"{evalPath}recordResultsEvents.csv", index=False)
239
+ df.to_csv(f"{evalPath}recordResultsEvents.csv", index=False)
234
240
 
235
241
  self.logSuccess(f"Results: {results}")
236
242
  results = {k: float(v) for k, v in results.items() if not isinstance(v, Iterable)}
@@ -51,6 +51,7 @@ class RecordProcessor:
51
51
  if "sleepStage" not in eventSignal:
52
52
  raise ChannelsNotPresent("sleepStage")
53
53
  eventSignal = eventSignal["sleepStage"]
54
+ labelSignalArray[eventSignal == 0] = -1
54
55
  labelSignalArray[eventSignal == PSGEventManager.INDEX_WAKE] = 0
55
56
  labelSignalArray[eventSignal == PSGEventManager.INDEX_REM] = 1
56
57
  labelSignalArray[eventSignal == PSGEventManager.INDEX_NREM1] = 2
@@ -170,8 +171,8 @@ class RecordProcessor:
170
171
  recordSignal, events = self.recordLoader.loadRecord(recordId)
171
172
 
172
173
  targetSignals = self.preProcessingConfig["targetChannels"] if "targetChannels" in self.preProcessingConfig else []
173
- targetFrequency = self.preProcessingConfig["targetFrequency"] if "targetFrequency" in self.preProcessingConfig else []
174
- labelFrequency = self.preProcessingConfig["labelFrequency"] if "labelFrequency" in self.preProcessingConfig else []
174
+ targetFrequency = self.preProcessingConfig["targetFrequency"] if "targetFrequency" in self.preProcessingConfig else 0
175
+ labelFrequency = self.preProcessingConfig["labelFrequency"] if "labelFrequency" in self.preProcessingConfig else targetFrequency
175
176
  featureChannels = self.preProcessingConfig["featureChannels"] if "featureChannels" in self.preProcessingConfig else []
176
177
  tailorToTargetSignals = self.preProcessingConfig["tailorToTargetSignals"] if "tailorToTargetSignals" in self.preProcessingConfig else False
177
178
  recordSignal.targetFrequency = targetFrequency
@@ -188,11 +189,14 @@ class RecordProcessor:
188
189
  self.featureExtraction.extractChannelsByConfig(recordSignal, featureChannels)
189
190
 
190
191
  signalLength = round(recordSignal.getSignalLength() * labelFrequency / targetFrequency)
192
+ sleepEvents = [e for e in events if e.name in ["R", "N1", "N2", "N3", "W", "undefined"]]
193
+ lastDefaultEventDuration = sleepEvents[-1].duration if sleepEvents and sleepEvents[-1].duration > 0 else 30 * labelFrequency
191
194
  eventSignal = self.eventManager.getEventSignalFromList(
192
195
  events,
193
196
  signalLength,
194
197
  targetFrequency=labelFrequency,
195
198
  forceGapBetweenEvents=False,
199
+ lastDefaultEventDuration=lastDefaultEventDuration,
196
200
  )
197
201
  # signalLength * 30
198
202
  eventSignal = self.preparelabelChannels(eventSignal, signalLength)
@@ -1,6 +1,6 @@
1
1
  from typing import List
2
2
  from pyPhases import Phase
3
- from tqdm import tqdm
3
+ from pyPhases.util import BatchProgress
4
4
  import pandas as pd
5
5
 
6
6
  from pyPhasesRecordloader import AnnotationNotFound, RecordLoader, ChannelsNotPresent, Event, ParseError
@@ -8,10 +8,62 @@ from pyPhasesRecordloader import AnnotationNotFound, RecordLoader, ChannelsNotPr
8
8
  from SleePyPhases.RecordFeatureExtraction import RecordFeatureExtraction
9
9
  from SleePyPhases.SleepMetaData import SleepMetaData
10
10
 
11
+
12
+
13
+ class RecordFeatureProcessor:
14
+ """Processor class for multithreaded/multiprocess record feature extraction"""
15
+
16
+ def __init__(self, featureConfigs, recordLoader, recordFeatureExtraction):
17
+ self.featureConfigs = featureConfigs
18
+ self.recordLoader = recordLoader
19
+ self.recordFeatureExtraction = recordFeatureExtraction
20
+
21
+ def __call__(self, recordId):
22
+ recordLoader = self.recordLoader
23
+ try:
24
+ recordSignal, eventlist = recordLoader.loadRecord(recordId)
25
+ except AnnotationNotFound as e:
26
+ print(f"\033[31;1;4m\033[0mAnnotation missing in {recordId}: {e.name} ... skipping record")
27
+ return None
28
+ except ChannelsNotPresent as e:
29
+ print(f"\033[31;1;4m\033[0mChannels missing in {recordId}: {e.channels} ... skipping record")
30
+ return None
31
+ except ParseError as e:
32
+ print(f"\033[31;1;4m\033[0mParseError in record {recordId}, record will be skipped: {e}")
33
+ return None
34
+
35
+ features = pd.DataFrame(
36
+ index=pd.MultiIndex(levels=[[], []], codes=[[], []], names=["recordId", "channel"])
37
+ )
38
+
39
+ for featureConfig in self.featureConfigs:
40
+ checkedCol = featureConfig["name"] + "_checked"
41
+ channels = featureConfig["channels"]
42
+ for c in channels:
43
+ idx = (recordId, c)
44
+ try:
45
+ featureStep = featureConfig["type"]
46
+ self.recordFeatureExtraction.updateMetadataByStep(
47
+ featureStep, recordSignal, eventlist, metadata=features, channel=c
48
+ )
49
+ except ChannelsNotPresent as e:
50
+ print(f"\033[31;1;4m\033[0mChannels not present in {recordId}: {e.channels}")
51
+
52
+ features.loc[idx, "recordId"] = recordId
53
+ features.loc[idx, "channel"] = c
54
+ features.loc[idx, checkedCol] = True
55
+
56
+ return features
57
+
58
+
11
59
  class ExtractFeatures(Phase):
12
60
  allowTraining = False
13
61
  features = None
14
62
 
63
+ useMultiThreading = True
64
+ spawnMultiprocessing = False
65
+ threads = None
66
+
15
67
  def getChannelMetadata(self, recordId):
16
68
  recordLoader = RecordLoader.get()
17
69
  recordLoader.getSignalHeaders(recordId)
@@ -212,16 +264,52 @@ class ExtractFeatures(Phase):
212
264
  recordIds = metadata["recordId"]
213
265
  forceCalculation = self.getConfig("featureExtraction.force", False)
214
266
 
215
- reqFeatures = {f["name"] + "_checked": True for f in self.getConfig("featureExtraction.features", [])}
216
-
217
267
  recordId = self.getConfig("recordId", False)
218
- recordIds = [recordId] if recordId else recordIds.iloc
219
-
220
- for recordId in tqdm(recordIds):
221
- if forceCalculation:
222
- # the parameter forced has no meaning, it is just known to be not in the metadata, so the record will always be recalculated
223
- reqFeatures["forced"] = True # should never be found
224
- rows = self.getData("features", pd.DataFrame, recordId=recordId, **reqFeatures)
225
- if len(rows) == 0:
226
- self.logError(f"no features for {recordId}")
268
+ recordIds = [recordId] if recordId else recordIds.tolist()
269
+
270
+ featureConfigs = self.getConfig("featureExtraction.features", [])
271
+ checkedCols = [f["name"] + "_checked" for f in featureConfigs]
272
+
273
+ self.features = self.getData("features", pd.DataFrame)
274
+
275
+ if not forceCalculation and len(self.features) > 0:
276
+ # a record is complete if all _checked columns are True for it
277
+ completedIds = set()
278
+ if all(col in self.features.columns for col in checkedCols):
279
+ byRecord = self.features.groupby(level="recordId")
280
+ for rid, group in byRecord:
281
+ if group[checkedCols].all(axis=None):
282
+ completedIds.add(rid)
283
+
284
+ pendingIds = [r for r in recordIds if r not in completedIds]
285
+ self.logSuccess(f"Skipping {len(recordIds) - len(pendingIds)} already-calculated records, {len(pendingIds)} to process")
286
+ else:
287
+ pendingIds = recordIds
288
+
289
+ if len(pendingIds) == 0:
290
+ self.logSuccess("All records already have features calculated")
291
+ return
292
+
293
+ recordLoader = RecordLoader.get()
294
+ recordFeatureExtraction = RecordFeatureExtraction.getInstance()
295
+ processor = RecordFeatureProcessor(featureConfigs, recordLoader, recordFeatureExtraction)
296
+
297
+ bp = BatchProgress(pendingIds)
298
+ bp.useMultiThreading = self.useMultiThreading
299
+ if self.threads is not None:
300
+ bp.threads = self.threads
301
+ if self.spawnMultiprocessing:
302
+ bp.startMethod = "spawn"
303
+
304
+ def combine(resultList, dataIndex):
305
+ for result in resultList:
306
+ if result is None:
307
+ continue
308
+ if len(result) > 0:
309
+ self.features = pd.concat([self.features, result])
310
+ else:
311
+ self.logError(f"no features for record at index {dataIndex}")
312
+ self.registerData("features", self.features)
313
+
314
+ bp.start(processor, afterBatch=combine)
227
315
 
@@ -28,6 +28,9 @@ class Setup(Phase):
28
28
  # default fold is 0 if none is set
29
29
  self.setConfig("fold", self.getConfig("fold", 0))
30
30
  # self.setConfig("datasetSplits", list(self.getConfig("dataversion.split", {}).keys()))
31
+ namedFolds = self.getConfig("dataversion.namedFolds", False)
32
+ if namedFolds:
33
+ self.setConfig("dataversion.folds", len(namedFolds))
31
34
  self.setConfig("hasFolds", self.getConfig("dataversion.folds", 0) > 0)
32
35
 
33
36
  s = Scorer(5)
@@ -75,13 +78,33 @@ class Setup(Phase):
75
78
 
76
79
  dm = DataversionManager(groupedRecords, seed=seed)
77
80
 
81
+ # named fold presets: dataversion.namedFolds resolved by name (foldName) or integer index (fold)
82
+ namedFolds = self.getConfig("dataversion.namedFolds", False)
83
+ if namedFolds:
84
+ foldNames = list(namedFolds.keys())
85
+ foldName = self.getConfig("foldName", False)
86
+ if not foldName:
87
+ foldIndex = self.getConfig("fold", 0)
88
+ if foldIndex >= len(foldNames):
89
+ raise Exception("fold index %i out of range for namedFolds (len %i)" % (foldIndex, len(foldNames)))
90
+ foldName = foldNames[foldIndex]
91
+ elif foldName not in namedFolds:
92
+ raise Exception("foldName '%s' not found in dataversion.namedFolds. Available: %s" % (foldName, foldNames))
93
+ foldConfig = namedFolds[foldName]
94
+ self.log(f"Using named fold '{foldName}'")
95
+ for splitName in ["training", "validation", "test"]:
96
+ if splitName in foldConfig:
97
+ dm.addSplitBySlices(splitName, foldConfig[splitName])
98
+ dm.validatDatasetVersion(raiseException=self.getConfig("validatDataset", True))
99
+ return dm
100
+
78
101
  # add manual addes splits
79
102
  for splitName in ["training", "validation", "test"]:
80
103
  if splitName in splits:
81
104
  dm.addSplitBySlices(splitName, splits[splitName])
82
105
 
83
106
  # add training folding splits
84
- if "trainval" in splits:
107
+ if "trainval" in splits and splits["trainval"] is not None:
85
108
  foldcount = self.getConfig("dataversion.folds", 0)
86
109
  if foldcount > 0:
87
110
  dm.addSplitsByFold("training", "validation", splits["trainval"], self.getConfig("dataversion.folds", 0), self.getConfig("fold", 0))
@@ -92,7 +115,7 @@ class Setup(Phase):
92
115
  if "training" in splits or "validation" in splits:
93
116
  raise Exception("trainval and training/validation split are mutually exclusive. Please remove one of them from the config. This can be caused by loading multiple configs with different splits.")
94
117
 
95
- if "trainvaltest" in splits:
118
+ if "trainvaltest" in splits and splits["trainvaltest"] is not None:
96
119
  foldCount = self.getConfig("dataversion.folds")
97
120
  currentFold = self.getConfig("fold", 0)
98
121
  testSlice, trainingSlice = dm.getSplitsByPercentage(
@@ -57,9 +57,37 @@ class Training(Phase):
57
57
  return trainedModel
58
58
 
59
59
  def generateData(self, name):
60
- trainedModel = self.train()
61
- self.project.registerData("modelState", trainedModel)
62
- self.project.registerData("modelStateConfig", self.project.getDataFromName("modelState").getDependencyDict())
60
+ if name == "modelState":
61
+ trainedModel = self.train()
62
+ self.project.registerData("modelState", trainedModel)
63
+ elif name == "modelStateConfig":
64
+ self.project.registerData("modelStateConfig", self.project.getDataFromName("modelState").getDependencyDict())
65
+ elif name == "trainedModel":
66
+
67
+ model = ModelManager.getModel(True)
68
+ pretrainingConfig = self.getConfig("pretraining", {})
69
+
70
+ pretrainedPath = pretrainingConfig.get("pretrainedModel", False)
71
+ preModelState = None
72
+
73
+ if pretrainedPath:
74
+ if not Path(pretrainedPath).exists():
75
+ err = f"model path for evaluation does not exist (pretraining.pretrainedModel): {pretrainedPath}"
76
+ self.logError(err)
77
+ raise Exception(err)
78
+ else:
79
+ preModelState = model.load(pretrainedPath)
80
+
81
+ if preModelState is None:
82
+ with self.project:
83
+ self.project.config["dataversion"] = {}
84
+ self.project.config.update(pretrainingConfig)
85
+ self.project.trigger("configChanged", None)
86
+ preModelState = self.project.getData("modelState", Model)
87
+
88
+ model.build()
89
+ model.loadState(preModelState)
90
+ return model
63
91
 
64
92
  def main(self):
65
93
  startfold = self.getConfig("startFold", 0)
@@ -88,9 +116,9 @@ class Training(Phase):
88
116
  self.project.unregister("modelStateConfig")
89
117
  gridOutputs.append(self.gridOutput)
90
118
  # reset model state
91
- ModelManager.getModel().model.load_state_dict(startState)
119
+ ModelManager.getModel().resetParameters()
92
120
 
93
- self.project.gridOutput = {
121
+ self.project.gridOutput= {
94
122
  "epochs": str(np.mean([m["epochs"] for m in gridOutputs])),
95
123
  "best": str(np.mean([m["best"] for m in gridOutputs])),
96
124
  "folds": len(gridOutputs),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SleePyPhases
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: A framwork for creating deep learning pipelines for sleep data
5
5
  Home-page: https://gitlab.com/sleep-is-all-you-need/sleepyphases
6
6
  Author: Franz Ehrlich
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="SleePyPhases",
8
- version="v0.8.1"[1:],
8
+ version="v0.8.2"[1:],
9
9
  author="Franz Ehrlich",
10
10
  author_email="fehrlichd@gmail.com",
11
11
  description="A framwork for creating deep learning pipelines for sleep data",
@@ -117,6 +117,7 @@ class TestExtract(TestCase):
117
117
  signalLength,
118
118
  targetFrequency=1,
119
119
  forceGapBetweenEvents=False,
120
+ lastDefaultEventDuration=10 # (Event("N1", 0, duration=10)])
120
121
  )
121
122
 
122
123
  # Check that the sliced recordSignal has the correct targetFrequency
File without changes
File without changes
File without changes