accusleepy 0.9.3__py3-none-any.whl → 0.10.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.
- accusleepy/__init__.py +1 -0
- accusleepy/__main__.py +2 -0
- accusleepy/bouts.py +2 -0
- accusleepy/brain_state_set.py +2 -0
- accusleepy/classification.py +2 -0
- accusleepy/config.json +2 -1
- accusleepy/constants.py +4 -0
- accusleepy/fileio.py +12 -3
- accusleepy/gui/__init__.py +1 -0
- accusleepy/gui/images/primary_window.png +0 -0
- accusleepy/gui/main.py +5 -7
- accusleepy/gui/manual_scoring.py +105 -42
- accusleepy/gui/mplwidget.py +2 -1
- accusleepy/gui/primary_window.py +85 -85
- accusleepy/gui/primary_window.ui +95 -107
- accusleepy/gui/recording_manager.py +24 -14
- accusleepy/gui/settings_widget.py +12 -0
- accusleepy/gui/text/main_guide.md +15 -12
- accusleepy/gui/text/manual_scoring_guide.md +1 -0
- accusleepy/models.py +2 -0
- accusleepy/multitaper.py +3 -60
- accusleepy/signal_processing.py +2 -0
- accusleepy/temperature_scaling.py +2 -0
- accusleepy/validation.py +2 -0
- {accusleepy-0.9.3.dist-info → accusleepy-0.10.1.dist-info}/METADATA +5 -8
- accusleepy-0.10.1.dist-info/RECORD +47 -0
- {accusleepy-0.9.3.dist-info → accusleepy-0.10.1.dist-info}/WHEEL +1 -1
- accusleepy-0.9.3.dist-info/RECORD +0 -47
accusleepy/gui/primary_window.ui
CHANGED
|
@@ -1239,14 +1239,14 @@ color: rgb(244, 195, 68);</string>
|
|
|
1239
1239
|
<number>10</number>
|
|
1240
1240
|
</property>
|
|
1241
1241
|
<item row="0" column="0">
|
|
1242
|
-
<layout class="QHBoxLayout" name="top_training_layout" stretch="
|
|
1242
|
+
<layout class="QHBoxLayout" name="top_training_layout" stretch="5,1,3,4,1,5">
|
|
1243
1243
|
<property name="spacing">
|
|
1244
1244
|
<number>10</number>
|
|
1245
1245
|
</property>
|
|
1246
1246
|
<item>
|
|
1247
1247
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
|
1248
1248
|
<property name="spacing">
|
|
1249
|
-
<number>
|
|
1249
|
+
<number>3</number>
|
|
1250
1250
|
</property>
|
|
1251
1251
|
<item>
|
|
1252
1252
|
<widget class="QLabel" name="label">
|
|
@@ -1299,7 +1299,7 @@ color: rgb(244, 195, 68);</string>
|
|
|
1299
1299
|
</spacer>
|
|
1300
1300
|
</item>
|
|
1301
1301
|
<item>
|
|
1302
|
-
<widget class="QCheckBox" name="
|
|
1302
|
+
<widget class="QCheckBox" name="calibrate_checkbox">
|
|
1303
1303
|
<property name="sizePolicy">
|
|
1304
1304
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1305
1305
|
<horstretch>0</horstretch>
|
|
@@ -1307,7 +1307,7 @@ color: rgb(244, 195, 68);</string>
|
|
|
1307
1307
|
</sizepolicy>
|
|
1308
1308
|
</property>
|
|
1309
1309
|
<property name="text">
|
|
1310
|
-
<string>
|
|
1310
|
+
<string>Calibrate model</string>
|
|
1311
1311
|
</property>
|
|
1312
1312
|
<property name="checked">
|
|
1313
1313
|
<bool>true</bool>
|
|
@@ -1315,7 +1315,55 @@ color: rgb(244, 195, 68);</string>
|
|
|
1315
1315
|
</widget>
|
|
1316
1316
|
</item>
|
|
1317
1317
|
<item>
|
|
1318
|
-
<
|
|
1318
|
+
<layout class="QHBoxLayout" name="horizontalLayout_84">
|
|
1319
|
+
<property name="spacing">
|
|
1320
|
+
<number>3</number>
|
|
1321
|
+
</property>
|
|
1322
|
+
<property name="leftMargin">
|
|
1323
|
+
<number>10</number>
|
|
1324
|
+
</property>
|
|
1325
|
+
<item>
|
|
1326
|
+
<widget class="QLabel" name="calibrate_label">
|
|
1327
|
+
<property name="sizePolicy">
|
|
1328
|
+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1329
|
+
<horstretch>0</horstretch>
|
|
1330
|
+
<verstretch>0</verstretch>
|
|
1331
|
+
</sizepolicy>
|
|
1332
|
+
</property>
|
|
1333
|
+
<property name="text">
|
|
1334
|
+
<string>Calibration set size:</string>
|
|
1335
|
+
</property>
|
|
1336
|
+
</widget>
|
|
1337
|
+
</item>
|
|
1338
|
+
<item>
|
|
1339
|
+
<widget class="QSpinBox" name="calibration_spinbox">
|
|
1340
|
+
<property name="sizePolicy">
|
|
1341
|
+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1342
|
+
<horstretch>0</horstretch>
|
|
1343
|
+
<verstretch>0</verstretch>
|
|
1344
|
+
</sizepolicy>
|
|
1345
|
+
</property>
|
|
1346
|
+
<property name="suffix">
|
|
1347
|
+
<string>%</string>
|
|
1348
|
+
</property>
|
|
1349
|
+
<property name="prefix">
|
|
1350
|
+
<string/>
|
|
1351
|
+
</property>
|
|
1352
|
+
<property name="minimum">
|
|
1353
|
+
<number>10</number>
|
|
1354
|
+
</property>
|
|
1355
|
+
<property name="maximum">
|
|
1356
|
+
<number>50</number>
|
|
1357
|
+
</property>
|
|
1358
|
+
<property name="value">
|
|
1359
|
+
<number>15</number>
|
|
1360
|
+
</property>
|
|
1361
|
+
</widget>
|
|
1362
|
+
</item>
|
|
1363
|
+
</layout>
|
|
1364
|
+
</item>
|
|
1365
|
+
<item>
|
|
1366
|
+
<spacer name="horizontalSpacer_3">
|
|
1319
1367
|
<property name="orientation">
|
|
1320
1368
|
<enum>Qt::Orientation::Horizontal</enum>
|
|
1321
1369
|
</property>
|
|
@@ -1376,30 +1424,14 @@ color: rgb(244, 195, 68);</string>
|
|
|
1376
1424
|
</item>
|
|
1377
1425
|
</layout>
|
|
1378
1426
|
</item>
|
|
1379
|
-
<item>
|
|
1380
|
-
<spacer name="horizontalSpacer_3">
|
|
1381
|
-
<property name="orientation">
|
|
1382
|
-
<enum>Qt::Orientation::Horizontal</enum>
|
|
1383
|
-
</property>
|
|
1384
|
-
<property name="sizeType">
|
|
1385
|
-
<enum>QSizePolicy::Policy::Preferred</enum>
|
|
1386
|
-
</property>
|
|
1387
|
-
<property name="sizeHint" stdset="0">
|
|
1388
|
-
<size>
|
|
1389
|
-
<width>10</width>
|
|
1390
|
-
<height>5</height>
|
|
1391
|
-
</size>
|
|
1392
|
-
</property>
|
|
1393
|
-
</spacer>
|
|
1394
|
-
</item>
|
|
1395
1427
|
</layout>
|
|
1396
1428
|
</item>
|
|
1397
1429
|
<item row="1" column="0">
|
|
1398
|
-
<layout class="QHBoxLayout" name="bottom_training_layout" stretch="
|
|
1430
|
+
<layout class="QHBoxLayout" name="bottom_training_layout" stretch="5,5">
|
|
1399
1431
|
<item>
|
|
1400
1432
|
<widget class="QPushButton" name="train_model_button">
|
|
1401
1433
|
<property name="sizePolicy">
|
|
1402
|
-
<sizepolicy hsizetype="
|
|
1434
|
+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1403
1435
|
<horstretch>0</horstretch>
|
|
1404
1436
|
<verstretch>0</verstretch>
|
|
1405
1437
|
</sizepolicy>
|
|
@@ -1428,89 +1460,6 @@ color: rgb(244, 195, 68);</string>
|
|
|
1428
1460
|
</property>
|
|
1429
1461
|
</spacer>
|
|
1430
1462
|
</item>
|
|
1431
|
-
<item>
|
|
1432
|
-
<widget class="QCheckBox" name="calibrate_checkbox">
|
|
1433
|
-
<property name="sizePolicy">
|
|
1434
|
-
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1435
|
-
<horstretch>0</horstretch>
|
|
1436
|
-
<verstretch>0</verstretch>
|
|
1437
|
-
</sizepolicy>
|
|
1438
|
-
</property>
|
|
1439
|
-
<property name="text">
|
|
1440
|
-
<string>Calibrate model</string>
|
|
1441
|
-
</property>
|
|
1442
|
-
<property name="checked">
|
|
1443
|
-
<bool>true</bool>
|
|
1444
|
-
</property>
|
|
1445
|
-
</widget>
|
|
1446
|
-
</item>
|
|
1447
|
-
<item>
|
|
1448
|
-
<spacer name="horizontalSpacer_7">
|
|
1449
|
-
<property name="orientation">
|
|
1450
|
-
<enum>Qt::Orientation::Horizontal</enum>
|
|
1451
|
-
</property>
|
|
1452
|
-
<property name="sizeType">
|
|
1453
|
-
<enum>QSizePolicy::Policy::Preferred</enum>
|
|
1454
|
-
</property>
|
|
1455
|
-
<property name="sizeHint" stdset="0">
|
|
1456
|
-
<size>
|
|
1457
|
-
<width>10</width>
|
|
1458
|
-
<height>10</height>
|
|
1459
|
-
</size>
|
|
1460
|
-
</property>
|
|
1461
|
-
</spacer>
|
|
1462
|
-
</item>
|
|
1463
|
-
<item>
|
|
1464
|
-
<widget class="QLabel" name="calibrate_label">
|
|
1465
|
-
<property name="sizePolicy">
|
|
1466
|
-
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1467
|
-
<horstretch>0</horstretch>
|
|
1468
|
-
<verstretch>0</verstretch>
|
|
1469
|
-
</sizepolicy>
|
|
1470
|
-
</property>
|
|
1471
|
-
<property name="text">
|
|
1472
|
-
<string>Calibration set size:</string>
|
|
1473
|
-
</property>
|
|
1474
|
-
</widget>
|
|
1475
|
-
</item>
|
|
1476
|
-
<item>
|
|
1477
|
-
<widget class="QSpinBox" name="calibration_spinbox">
|
|
1478
|
-
<property name="sizePolicy">
|
|
1479
|
-
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
1480
|
-
<horstretch>0</horstretch>
|
|
1481
|
-
<verstretch>0</verstretch>
|
|
1482
|
-
</sizepolicy>
|
|
1483
|
-
</property>
|
|
1484
|
-
<property name="suffix">
|
|
1485
|
-
<string>%</string>
|
|
1486
|
-
</property>
|
|
1487
|
-
<property name="minimum">
|
|
1488
|
-
<number>10</number>
|
|
1489
|
-
</property>
|
|
1490
|
-
<property name="maximum">
|
|
1491
|
-
<number>50</number>
|
|
1492
|
-
</property>
|
|
1493
|
-
<property name="value">
|
|
1494
|
-
<number>15</number>
|
|
1495
|
-
</property>
|
|
1496
|
-
</widget>
|
|
1497
|
-
</item>
|
|
1498
|
-
<item>
|
|
1499
|
-
<spacer name="horizontalSpacer_78">
|
|
1500
|
-
<property name="orientation">
|
|
1501
|
-
<enum>Qt::Orientation::Horizontal</enum>
|
|
1502
|
-
</property>
|
|
1503
|
-
<property name="sizeType">
|
|
1504
|
-
<enum>QSizePolicy::Policy::Preferred</enum>
|
|
1505
|
-
</property>
|
|
1506
|
-
<property name="sizeHint" stdset="0">
|
|
1507
|
-
<size>
|
|
1508
|
-
<width>10</width>
|
|
1509
|
-
<height>10</height>
|
|
1510
|
-
</size>
|
|
1511
|
-
</property>
|
|
1512
|
-
</spacer>
|
|
1513
|
-
</item>
|
|
1514
1463
|
</layout>
|
|
1515
1464
|
</item>
|
|
1516
1465
|
</layout>
|
|
@@ -4612,6 +4561,43 @@ Each brain state has several attributes:
|
|
|
4612
4561
|
</item>
|
|
4613
4562
|
</layout>
|
|
4614
4563
|
</item>
|
|
4564
|
+
<item>
|
|
4565
|
+
<layout class="QHBoxLayout" name="horizontalLayout_79">
|
|
4566
|
+
<item>
|
|
4567
|
+
<layout class="QHBoxLayout" name="horizontalLayout_85">
|
|
4568
|
+
<item>
|
|
4569
|
+
<widget class="QCheckBox" name="delete_image_box">
|
|
4570
|
+
<property name="sizePolicy">
|
|
4571
|
+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
4572
|
+
<horstretch>0</horstretch>
|
|
4573
|
+
<verstretch>0</verstretch>
|
|
4574
|
+
</sizepolicy>
|
|
4575
|
+
</property>
|
|
4576
|
+
<property name="text">
|
|
4577
|
+
<string>Delete images after training</string>
|
|
4578
|
+
</property>
|
|
4579
|
+
<property name="checked">
|
|
4580
|
+
<bool>true</bool>
|
|
4581
|
+
</property>
|
|
4582
|
+
</widget>
|
|
4583
|
+
</item>
|
|
4584
|
+
</layout>
|
|
4585
|
+
</item>
|
|
4586
|
+
<item>
|
|
4587
|
+
<spacer name="horizontalSpacer_93">
|
|
4588
|
+
<property name="orientation">
|
|
4589
|
+
<enum>Qt::Orientation::Horizontal</enum>
|
|
4590
|
+
</property>
|
|
4591
|
+
<property name="sizeHint" stdset="0">
|
|
4592
|
+
<size>
|
|
4593
|
+
<width>40</width>
|
|
4594
|
+
<height>20</height>
|
|
4595
|
+
</size>
|
|
4596
|
+
</property>
|
|
4597
|
+
</spacer>
|
|
4598
|
+
</item>
|
|
4599
|
+
</layout>
|
|
4600
|
+
</item>
|
|
4615
4601
|
<item>
|
|
4616
4602
|
<layout class="QHBoxLayout" name="horizontalLayout_74">
|
|
4617
4603
|
<item>
|
|
@@ -4682,7 +4668,9 @@ Each brain state has several attributes:
|
|
|
4682
4668
|
- Batch size: number of examples in each training iteration
|
|
4683
4669
|
- Learning rate: step size for adjusting model weights
|
|
4684
4670
|
- Momentum: amount of past gradients to use in the current update. Typically between 0.6 and 0.99
|
|
4685
|
-
- Epochs: number of passes over the training data
|
|
4671
|
+
- Epochs: number of passes over the training data
|
|
4672
|
+
|
|
4673
|
+
You can also choose whether to delete images created by the training process once it's finished. This is recommended unless you are debugging the training code.</string>
|
|
4686
4674
|
</property>
|
|
4687
4675
|
<property name="textFormat">
|
|
4688
4676
|
<enum>Qt::TextFormat::MarkdownText</enum>
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
"""Recording list manager"""
|
|
2
2
|
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
3
5
|
from PySide6.QtCore import QObject
|
|
4
6
|
from PySide6.QtWidgets import QListWidget, QListWidgetItem
|
|
5
7
|
|
|
6
8
|
from accusleepy.fileio import Recording, load_recording_list, save_recording_list
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
@dataclass
|
|
12
|
+
class RecordingListItem(Recording):
|
|
13
|
+
"""A Recording with an associated QListWidget item for the GUI"""
|
|
14
|
+
|
|
15
|
+
widget: QListWidgetItem = None
|
|
16
|
+
|
|
17
|
+
|
|
9
18
|
class RecordingListManager(QObject):
|
|
10
19
|
"""Manages the list of recordings and the associated QListWidget"""
|
|
11
20
|
|
|
@@ -14,19 +23,19 @@ class RecordingListManager(QObject):
|
|
|
14
23
|
self._widget = list_widget
|
|
15
24
|
|
|
16
25
|
# Create initial empty recording (there is always at least one)
|
|
17
|
-
first_recording =
|
|
26
|
+
first_recording = RecordingListItem(
|
|
18
27
|
widget=QListWidgetItem("Recording 1", self._widget),
|
|
19
28
|
)
|
|
20
|
-
self._recordings: list[
|
|
29
|
+
self._recordings: list[RecordingListItem] = [first_recording]
|
|
21
30
|
self._widget.addItem(first_recording.widget)
|
|
22
31
|
self._widget.setCurrentRow(0)
|
|
23
32
|
|
|
24
33
|
@property
|
|
25
|
-
def current(self) ->
|
|
34
|
+
def current(self) -> RecordingListItem:
|
|
26
35
|
"""The currently selected recording"""
|
|
27
36
|
return self._recordings[self._widget.currentRow()]
|
|
28
37
|
|
|
29
|
-
def add(self, sampling_rate: int | float) ->
|
|
38
|
+
def add(self, sampling_rate: int | float) -> RecordingListItem:
|
|
30
39
|
"""Add a new recording to the list
|
|
31
40
|
|
|
32
41
|
:param sampling_rate: sampling rate for the new recording
|
|
@@ -35,7 +44,7 @@ class RecordingListManager(QObject):
|
|
|
35
44
|
new_name = max(r.name for r in self._recordings) + 1
|
|
36
45
|
|
|
37
46
|
# Create recording with widget
|
|
38
|
-
recording =
|
|
47
|
+
recording = RecordingListItem(
|
|
39
48
|
name=new_name,
|
|
40
49
|
sampling_rate=sampling_rate,
|
|
41
50
|
widget=QListWidgetItem(f"Recording {new_name}", self._widget),
|
|
@@ -63,7 +72,7 @@ class RecordingListManager(QObject):
|
|
|
63
72
|
else:
|
|
64
73
|
# Reset the single recording to defaults
|
|
65
74
|
recording_name = self._recordings[0].name
|
|
66
|
-
self._recordings[0] =
|
|
75
|
+
self._recordings[0] = RecordingListItem(widget=self._recordings[0].widget)
|
|
67
76
|
self._recordings[0].widget.setText(f"Recording {self._recordings[0].name}")
|
|
68
77
|
return f"cleared Recording {recording_name}"
|
|
69
78
|
|
|
@@ -85,14 +94,15 @@ class RecordingListManager(QObject):
|
|
|
85
94
|
try:
|
|
86
95
|
self._widget.clear()
|
|
87
96
|
|
|
88
|
-
# Load recordings
|
|
89
|
-
self._recordings =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
recording.widget = QListWidgetItem(
|
|
94
|
-
f"Recording {recording.name}", self._widget
|
|
97
|
+
# Load recordings and create widgets
|
|
98
|
+
self._recordings = [
|
|
99
|
+
RecordingListItem(
|
|
100
|
+
**r.__dict__,
|
|
101
|
+
widget=QListWidgetItem(f"Recording {r.name}", self._widget),
|
|
95
102
|
)
|
|
103
|
+
for r in load_recording_list(filename)
|
|
104
|
+
]
|
|
105
|
+
for recording in self._recordings:
|
|
96
106
|
self._widget.addItem(recording.widget)
|
|
97
107
|
finally:
|
|
98
108
|
self._widget.blockSignals(False)
|
|
@@ -106,5 +116,5 @@ class RecordingListManager(QObject):
|
|
|
106
116
|
def __len__(self):
|
|
107
117
|
return len(self._recordings)
|
|
108
118
|
|
|
109
|
-
def __getitem__(self, index: int) ->
|
|
119
|
+
def __getitem__(self, index: int) -> RecordingListItem:
|
|
110
120
|
return self._recordings[index]
|
|
@@ -9,6 +9,7 @@ from PySide6.QtWidgets import QCheckBox, QDoubleSpinBox, QLineEdit
|
|
|
9
9
|
from accusleepy.brain_state_set import BrainState, BrainStateSet
|
|
10
10
|
from accusleepy.constants import (
|
|
11
11
|
DEFAULT_BATCH_SIZE,
|
|
12
|
+
DEFAULT_DELETE_TRAINING_IMAGES_STATE,
|
|
12
13
|
DEFAULT_EMG_BP_LOWER,
|
|
13
14
|
DEFAULT_EMG_BP_UPPER,
|
|
14
15
|
DEFAULT_EMG_FILTER_ORDER,
|
|
@@ -55,6 +56,7 @@ class SettingsWidget(QObject):
|
|
|
55
56
|
self._hyperparameters = config.hyperparameters
|
|
56
57
|
self._default_epochs_to_show = config.epochs_to_show
|
|
57
58
|
self._default_autoscroll_state = config.autoscroll_state
|
|
59
|
+
self._delete_training_images = config.delete_training_images
|
|
58
60
|
|
|
59
61
|
# Store default values for main tab settings (used to populate Settings tab UI)
|
|
60
62
|
self._default_epoch_length = config.default_epoch_length
|
|
@@ -81,6 +83,11 @@ class SettingsWidget(QObject):
|
|
|
81
83
|
"""Model training hyperparameters"""
|
|
82
84
|
return self._hyperparameters
|
|
83
85
|
|
|
86
|
+
@property
|
|
87
|
+
def delete_training_images(self) -> bool:
|
|
88
|
+
"""Whether to delete images after training"""
|
|
89
|
+
return self._delete_training_images
|
|
90
|
+
|
|
84
91
|
@property
|
|
85
92
|
def default_epochs_to_show(self) -> int:
|
|
86
93
|
"""Default number of epochs to show in manual scoring"""
|
|
@@ -186,6 +193,7 @@ class SettingsWidget(QObject):
|
|
|
186
193
|
self._ui.learning_rate_spinbox.setValue(self._hyperparameters.learning_rate)
|
|
187
194
|
self._ui.momentum_spinbox.setValue(self._hyperparameters.momentum)
|
|
188
195
|
self._ui.training_epochs_spinbox.setValue(self._hyperparameters.training_epochs)
|
|
196
|
+
self._ui.delete_image_box.setChecked(self._delete_training_images)
|
|
189
197
|
# Brain states
|
|
190
198
|
states = {b.digit: b for b in self._brain_state_set.brain_states}
|
|
191
199
|
for digit in range(10):
|
|
@@ -233,6 +241,7 @@ class SettingsWidget(QObject):
|
|
|
233
241
|
self._ui.training_epochs_spinbox.valueChanged.connect(
|
|
234
242
|
self._hyperparameters_changed
|
|
235
243
|
)
|
|
244
|
+
self._ui.delete_image_box.stateChanged.connect(self._hyperparameters_changed)
|
|
236
245
|
for digit in range(10):
|
|
237
246
|
state = self._settings_widgets[digit]
|
|
238
247
|
state.enabled_widget.stateChanged.connect(
|
|
@@ -302,6 +311,7 @@ class SettingsWidget(QObject):
|
|
|
302
311
|
momentum=self._ui.momentum_spinbox.value(),
|
|
303
312
|
training_epochs=self._ui.training_epochs_spinbox.value(),
|
|
304
313
|
)
|
|
314
|
+
self._delete_training_images = self._ui.delete_image_box.isChecked()
|
|
305
315
|
self._ui.save_config_status.setText("")
|
|
306
316
|
|
|
307
317
|
def reset_status_message(self, _new_value=None) -> None:
|
|
@@ -392,6 +402,7 @@ class SettingsWidget(QObject):
|
|
|
392
402
|
hyperparameters=self._hyperparameters,
|
|
393
403
|
epochs_to_show=self._ui.epochs_to_show_spinbox.value(),
|
|
394
404
|
autoscroll_state=self._ui.autoscroll_checkbox.isChecked(),
|
|
405
|
+
delete_training_images=self._ui.delete_image_box.isChecked(),
|
|
395
406
|
)
|
|
396
407
|
self._ui.save_config_status.setText("configuration saved")
|
|
397
408
|
|
|
@@ -407,3 +418,4 @@ class SettingsWidget(QObject):
|
|
|
407
418
|
self._ui.learning_rate_spinbox.setValue(DEFAULT_LEARNING_RATE)
|
|
408
419
|
self._ui.momentum_spinbox.setValue(DEFAULT_MOMENTUM)
|
|
409
420
|
self._ui.training_epochs_spinbox.setValue(DEFAULT_TRAINING_EPOCHS)
|
|
421
|
+
self._ui.delete_image_box.setChecked(DEFAULT_DELETE_TRAINING_IMAGES_STATE)
|
|
@@ -125,25 +125,28 @@ To train a new model on your own data:
|
|
|
125
125
|
recording. Calibration files are not required.
|
|
126
126
|
2. Click the "Model training" tab
|
|
127
127
|
3. Choose the number of epochs to consider when scoring each epoch.
|
|
128
|
-
This will be the
|
|
129
|
-
type models, this must be an odd number.
|
|
130
|
-
seconds worth of data is enough.
|
|
131
|
-
4. Choose whether
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
a default-type model.
|
|
138
|
-
6. Choose whether to calibrate the model. This process uses part
|
|
128
|
+
This will be the width, in pixels, of the training images.
|
|
129
|
+
For "default" type models, this must be an odd number. Generally,
|
|
130
|
+
about 30 seconds worth of data is enough.
|
|
131
|
+
4. Choose whether to create a "default" or "real-time"-type model.
|
|
132
|
+
Scoring recordings in the AccuSleePy interface requires a
|
|
133
|
+
default-type model. Only select real-time if you plan to create
|
|
134
|
+
your own scoring function. An example real-time scoring function
|
|
135
|
+
can be found in `classification.py`.
|
|
136
|
+
5. Choose whether to calibrate the model. This process uses part
|
|
139
137
|
of the training data to make the model's confidence scores
|
|
140
138
|
more accurately reflect the probability that the output
|
|
141
139
|
labels are accurate. If using calibration, choose what percent
|
|
142
140
|
of the training data to set aside for calibration.
|
|
143
|
-
|
|
141
|
+
6. Click "Train classification model" and enter a
|
|
144
142
|
filename for the trained model. Training can take some time.
|
|
145
143
|
The terminal will display progress updates.
|
|
146
144
|
|
|
145
|
+
As part of the training process, a temporary folder of images will
|
|
146
|
+
be created in the location where the model will be saved. This folder
|
|
147
|
+
is deleted by default, but if you want to keep it for debugging
|
|
148
|
+
purposes, you can change this behavior in the "Settings" tab.
|
|
149
|
+
|
|
147
150
|
## 4C. Automatic scoring
|
|
148
151
|
|
|
149
152
|
Instructions for automatic scoring using this interface are below.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Keyboard shortcuts:
|
|
4
4
|
- Mouse click on the upper 3 plots: jump to epoch
|
|
5
|
+
- Mouse scroll on the upper 3 plots: zoom in/out along the x-axis
|
|
5
6
|
- Ctrl + S: save labels to file
|
|
6
7
|
- Right / left arrow: move one epoch forward / backward in time
|
|
7
8
|
- Numbers 0-9: set current epoch to this brain state
|
accusleepy/models.py
CHANGED
accusleepy/multitaper.py
CHANGED
|
@@ -14,10 +14,6 @@ import timeit
|
|
|
14
14
|
import warnings
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
|
-
from joblib import Parallel, cpu_count, delayed
|
|
18
|
-
|
|
19
|
-
# from scipy.signal import detrend # unused by AccuSleePy
|
|
20
|
-
# from scipy.signal.windows import dpss # lazily loaded later
|
|
21
17
|
|
|
22
18
|
|
|
23
19
|
# MULTITAPER SPECTROGRAM #
|
|
@@ -206,19 +202,9 @@ def spectrogram(
|
|
|
206
202
|
wt,
|
|
207
203
|
)
|
|
208
204
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
Parallel(n_jobs=n_jobs)(
|
|
213
|
-
delayed(calc_mts_segment)(data_segments[num_window, :], *mts_params)
|
|
214
|
-
for num_window in range(num_windows)
|
|
215
|
-
)
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
else: # if no multiprocessing, compute normally
|
|
219
|
-
mt_spectrogram = np.apply_along_axis(
|
|
220
|
-
calc_mts_segment, 1, data_segments, *mts_params
|
|
221
|
-
)
|
|
205
|
+
mt_spectrogram = np.apply_along_axis(
|
|
206
|
+
calc_mts_segment, 1, data_segments, *mts_params
|
|
207
|
+
)
|
|
222
208
|
|
|
223
209
|
# Compute one-sided PSD spectrum
|
|
224
210
|
mt_spectrogram = mt_spectrogram.T
|
|
@@ -251,37 +237,6 @@ def spectrogram(
|
|
|
251
237
|
if np.all(mt_spectrogram.flatten() == 0):
|
|
252
238
|
print("\n Data was all zeros, no output")
|
|
253
239
|
|
|
254
|
-
# # Plot multitaper spectrogram
|
|
255
|
-
# if plot_on:
|
|
256
|
-
# # convert from power to dB
|
|
257
|
-
# spect_data = nanpow2db(mt_spectrogram)
|
|
258
|
-
#
|
|
259
|
-
# # Set x and y axes
|
|
260
|
-
# dx = stimes[1] - stimes[0]
|
|
261
|
-
# dy = sfreqs[1] - sfreqs[0]
|
|
262
|
-
# extent = [stimes[0] - dx, stimes[-1] + dx, sfreqs[-1] + dy, sfreqs[0] - dy]
|
|
263
|
-
#
|
|
264
|
-
# # Plot spectrogram
|
|
265
|
-
# if ax is None:
|
|
266
|
-
# fig, ax = plt.subplots()
|
|
267
|
-
# else:
|
|
268
|
-
# fig = ax.get_figure()
|
|
269
|
-
# im = ax.imshow(spect_data, extent=extent, aspect="auto")
|
|
270
|
-
# fig.colorbar(im, ax=ax, label="PSD (dB)", shrink=0.8)
|
|
271
|
-
# ax.set_xlabel("Time (HH:MM:SS)")
|
|
272
|
-
# ax.set_ylabel("Frequency (Hz)")
|
|
273
|
-
# im.set_cmap(plt.cm.get_cmap("cet_rainbow4"))
|
|
274
|
-
# ax.invert_yaxis()
|
|
275
|
-
#
|
|
276
|
-
# # Scale colormap
|
|
277
|
-
# if clim_scale:
|
|
278
|
-
# clim = np.percentile(spect_data, [5, 98]) # from 5th percentile to 98th
|
|
279
|
-
# im.set_clim(clim) # actually change colorbar scale
|
|
280
|
-
#
|
|
281
|
-
# fig.show()
|
|
282
|
-
# if return_fig:
|
|
283
|
-
# return mt_spectrogram, stimes, sfreqs, (fig, ax)
|
|
284
|
-
|
|
285
240
|
return mt_spectrogram, stimes, sfreqs
|
|
286
241
|
|
|
287
242
|
|
|
@@ -568,18 +523,6 @@ def nanpow2db(y):
|
|
|
568
523
|
return ydB
|
|
569
524
|
|
|
570
525
|
|
|
571
|
-
# Helper #
|
|
572
|
-
def is_outlier(data):
|
|
573
|
-
smad = 1.4826 * np.median(
|
|
574
|
-
abs(data - np.median(data))
|
|
575
|
-
) # scaled median absolute deviation
|
|
576
|
-
outlier_mask = (
|
|
577
|
-
abs(data - np.median(data)) > 3 * smad
|
|
578
|
-
) # outliers are more than 3 smads away from median
|
|
579
|
-
outlier_mask = outlier_mask | np.isnan(data) | np.isinf(data)
|
|
580
|
-
return outlier_mask
|
|
581
|
-
|
|
582
|
-
|
|
583
526
|
# CALCULATE MULTITAPER SPECTRUM ON SINGLE SEGMENT
|
|
584
527
|
def calc_mts_segment(
|
|
585
528
|
data_segment,
|
accusleepy/signal_processing.py
CHANGED
accusleepy/validation.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: accusleepy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Python implementation of AccuSleep
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Author: Zeke Barger
|
|
@@ -11,14 +11,12 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Requires-Dist: fastparquet (>=2024.11.0,<2025.0.0)
|
|
15
|
-
Requires-Dist: joblib (>=1.4.2,<2.0.0)
|
|
16
14
|
Requires-Dist: matplotlib (>=3.10.1,<4.0.0)
|
|
17
15
|
Requires-Dist: numpy (>=2.2.4,<3.0.0)
|
|
18
16
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
19
17
|
Requires-Dist: pillow (>=11.1.0,<12.0.0)
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist: pyside6 (>=6.
|
|
18
|
+
Requires-Dist: pyarrow (>=23.0.0,<24.0.0)
|
|
19
|
+
Requires-Dist: pyside6 (>=6.10.1,<7.0.0)
|
|
22
20
|
Requires-Dist: scipy (>=1.15.2,<2.0.0)
|
|
23
21
|
Requires-Dist: torch (>=2.8.0,<3.0.0)
|
|
24
22
|
Requires-Dist: torchvision (>=0.23.0,<1.0.0)
|
|
@@ -79,9 +77,8 @@ please consult the [developer guide](accusleepy/gui/text/dev_guide.md).
|
|
|
79
77
|
|
|
80
78
|
## Changelog
|
|
81
79
|
|
|
82
|
-
- 0.
|
|
83
|
-
- 0.
|
|
84
|
-
- 0.7.1-0.7.3: Bugfixes, code cleanup
|
|
80
|
+
- 0.10.0-0.10.1: Improved zoom behavior, updated dependencies
|
|
81
|
+
- 0.7.1-0.9.3: Bugfixes, code cleanup, additional config settings
|
|
85
82
|
- 0.7.0: More settings can be configured in the UI
|
|
86
83
|
- 0.6.0: Confidence scores can now be displayed and saved. Retraining your models is recommended
|
|
87
84
|
since the new calibration feature will make the confidence scores more accurate.
|