Skip to content

Commit

Permalink
Merge pull request #3 from bruAristimunha/master
Browse files Browse the repository at this point in the history
new version 0.8
  • Loading branch information
bruAristimunha authored Nov 10, 2023
2 parents 2390df7 + 71e6441 commit 9416c82
Show file tree
Hide file tree
Showing 670 changed files with 233,915 additions and 98,001 deletions.
4 changes: 4 additions & 0 deletions 0.8/.buildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: bdb4e5dbb6b518a9114118ae0dc38d4c
tags: 645f666f9bcd5a90fca523b33c5a78b7
Empty file added 0.8/.nojekyll
Empty file.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n# Sleep staging on the Sleep Physionet dataset using Chambon2018 network\n\nThis tutorial shows how to train and test a sleep staging neural network with\nBraindecode. We adapt the time distributed approach of [1]_ to learn on\nsequences of EEG windows using the openly accessible Sleep Physionet dataset\n[2]_ [3]_.\n\n## References\n.. [1] Chambon, S., Galtier, M., Arnal, P., Wainrib, G. and Gramfort, A.\n (2018)A Deep Learning Architecture for Temporal Sleep Stage\n Classification Using Multivariate and Multimodal Time Series.\n IEEE Trans. on Neural Systems and Rehabilitation Engineering 26:\n (758-769)\n\n.. [2] B Kemp, AH Zwinderman, B Tuk, HAC Kamphuisen, JJL Obery\u00e9. Analysis of\n a sleep-dependent neuronal feedback loop: the slow-wave\n microcontinuity of the EEG. IEEE-BME 47(9):1185-1194 (2000).\n\n.. [3] Goldberger AL, Amaral LAN, Glass L, Hausdorff JM, Ivanov PCh,\n Mark RG, Mietus JE, Moody GB, Peng C-K, Stanley HE. (2000)\n PhysioBank, PhysioToolkit, and PhysioNet: Components of a New\n Research Resource for Complex Physiologic Signals.\n Circulation 101(23):e215-e220\n"
"\n# Sleep staging on the Sleep Physionet dataset using Chambon2018 network\n\nThis tutorial shows how to train and test a sleep staging neural network with\nBraindecode. We adapt the time distributed approach of [1]_ to learn on\nsequences of EEG windows using the openly accessible Sleep Physionet dataset\n[2]_ [3]_.\n"
]
},
{
Expand Down Expand Up @@ -62,7 +51,7 @@
},
"outputs": [],
"source": [
"from braindecode.preprocessing import preprocess, Preprocessor, scale\n\nhigh_cut_hz = 30\n\npreprocessors = [\n Preprocessor(scale, factor=1e6, apply_on_array=True),\n Preprocessor('filter', l_freq=None, h_freq=high_cut_hz)\n]\n\n# Transform the data\npreprocess(dataset, preprocessors)"
"from braindecode.preprocessing import preprocess, Preprocessor\nfrom numpy import multiply\n\nhigh_cut_hz = 30\nfactor = 1e6\n\npreprocessors = [\n Preprocessor(lambda data: multiply(data, factor), apply_on_array=True), # Convert from V to uV\n Preprocessor('filter', l_freq=None, h_freq=high_cut_hz)\n]\n\n# Transform the data\npreprocess(dataset, preprocessors)"
]
},
{
Expand All @@ -80,7 +69,7 @@
},
"outputs": [],
"source": [
"from braindecode.preprocessing import create_windows_from_events\n\n\nmapping = { # We merge stages 3 and 4 following AASM standards.\n 'Sleep stage W': 0,\n 'Sleep stage 1': 1,\n 'Sleep stage 2': 2,\n 'Sleep stage 3': 3,\n 'Sleep stage 4': 3,\n 'Sleep stage R': 4\n}\n\nwindow_size_s = 30\nsfreq = 100\nwindow_size_samples = window_size_s * sfreq\n\nwindows_dataset = create_windows_from_events(\n dataset,\n trial_start_offset_samples=0,\n trial_stop_offset_samples=0,\n window_size_samples=window_size_samples,\n window_stride_samples=window_size_samples,\n preload=True,\n mapping=mapping\n)"
"from braindecode.preprocessing import create_windows_from_events\n\nmapping = { # We merge stages 3 and 4 following AASM standards.\n 'Sleep stage W': 0,\n 'Sleep stage 1': 1,\n 'Sleep stage 2': 2,\n 'Sleep stage 3': 3,\n 'Sleep stage 4': 3,\n 'Sleep stage R': 4\n}\n\nwindow_size_s = 30\nsfreq = 100\nwindow_size_samples = window_size_s * sfreq\n\nwindows_dataset = create_windows_from_events(\n dataset,\n trial_start_offset_samples=0,\n trial_stop_offset_samples=0,\n window_size_samples=window_size_samples,\n window_stride_samples=window_size_samples,\n preload=True,\n mapping=mapping\n)"
]
},
{
Expand Down Expand Up @@ -134,7 +123,7 @@
},
"outputs": [],
"source": [
"import numpy as np\nfrom braindecode.samplers import SequenceSampler\n\nn_windows = 3 # Sequences of 3 consecutive windows\nn_windows_stride = 3 # Maximally overlapping sequences\n\ntrain_sampler = SequenceSampler(train_set.get_metadata(), n_windows, n_windows_stride)\nvalid_sampler = SequenceSampler(valid_set.get_metadata(), n_windows, n_windows_stride)\n\n# Print number of examples per class\nprint('Training examples: ', len(train_sampler))\nprint('Validation examples: ', len(valid_sampler))"
"import numpy as np\nfrom braindecode.samplers import SequenceSampler\n\nn_windows = 3 # Sequences of 3 consecutive windows\nn_windows_stride = 3 # Maximally overlapping sequences\n\ntrain_sampler = SequenceSampler(\n train_set.get_metadata(), n_windows, n_windows_stride, randomize=True\n)\nvalid_sampler = SequenceSampler(valid_set.get_metadata(), n_windows, n_windows_stride)\n\n# Print number of examples per class\nprint('Training examples: ', len(train_sampler))\nprint('Validation examples: ', len(valid_sampler))"
]
},
{
Expand Down Expand Up @@ -188,7 +177,7 @@
},
"outputs": [],
"source": [
"import torch\nfrom torch import nn\nfrom braindecode.util import set_random_seeds\nfrom braindecode.models import SleepStagerChambon2018, TimeDistributed\n\ncuda = torch.cuda.is_available() # check if GPU is available\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nif cuda:\n torch.backends.cudnn.benchmark = True\n# Set random seed to be able to roughly reproduce results\n# Note that with cudnn benchmark set to True, GPU indeterminism\n# may still make results substantially different between runs.\n# To obtain more consistent results at the cost of increased computation time,\n# you can set `cudnn_benchmark=False` in `set_random_seeds`\n# or remove `torch.backends.cudnn.benchmark = True`\nset_random_seeds(seed=31, cuda=cuda)\n\nn_classes = 5\n# Extract number of channels and time steps from dataset\nn_channels, input_size_samples = train_set[0][0].shape\n\nfeat_extractor = SleepStagerChambon2018(\n n_channels,\n sfreq,\n n_classes=n_classes,\n input_size_s=input_size_samples / sfreq,\n return_feats=True\n)\n\nmodel = nn.Sequential(\n TimeDistributed(feat_extractor), # apply model on each 30-s window\n nn.Sequential( # apply linear layer on concatenated feature vectors\n nn.Flatten(start_dim=1),\n nn.Dropout(0.5),\n nn.Linear(feat_extractor.len_last_layer * n_windows, n_classes)\n )\n)\n\n# Send model to GPU\nif cuda:\n model.cuda()"
"import torch\nfrom torch import nn\nfrom braindecode.util import set_random_seeds\nfrom braindecode.models import SleepStagerChambon2018, TimeDistributed\n\ncuda = torch.cuda.is_available() # check if GPU is available\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nif cuda:\n torch.backends.cudnn.benchmark = True\n# Set random seed to be able to roughly reproduce results\n# Note that with cudnn benchmark set to True, GPU indeterminism\n# may still make results substantially different between runs.\n# To obtain more consistent results at the cost of increased computation time,\n# you can set `cudnn_benchmark=False` in `set_random_seeds`\n# or remove `torch.backends.cudnn.benchmark = True`\nset_random_seeds(seed=31, cuda=cuda)\n\nn_classes = 5\n# Extract number of channels and time steps from dataset\nn_channels, input_size_samples = train_set[0][0].shape\n\nfeat_extractor = SleepStagerChambon2018(\n n_channels,\n sfreq,\n n_outputs=n_classes,\n n_times=input_size_samples,\n return_feats=True\n)\n\nmodel = nn.Sequential(\n TimeDistributed(feat_extractor), # apply model on each 30-s window\n nn.Sequential( # apply linear layer on concatenated feature vectors\n nn.Flatten(start_dim=1),\n nn.Dropout(0.5),\n nn.Linear(feat_extractor.len_last_layer * n_windows, n_classes)\n )\n)\n\n# Send model to GPU\nif cuda:\n model.cuda()"
]
},
{
Expand All @@ -206,7 +195,7 @@
},
"outputs": [],
"source": [
"from skorch.helper import predefined_split\nfrom skorch.callbacks import EpochScoring\nfrom braindecode import EEGClassifier\n\nlr = 1e-3\nbatch_size = 32\nn_epochs = 10\n\ntrain_bal_acc = EpochScoring(\n scoring='balanced_accuracy', on_train=True, name='train_bal_acc',\n lower_is_better=False)\nvalid_bal_acc = EpochScoring(\n scoring='balanced_accuracy', on_train=False, name='valid_bal_acc',\n lower_is_better=False)\ncallbacks = [\n ('train_bal_acc', train_bal_acc),\n ('valid_bal_acc', valid_bal_acc)\n]\n\nclf = EEGClassifier(\n model,\n criterion=torch.nn.CrossEntropyLoss,\n criterion__weight=torch.Tensor(class_weights).to(device),\n optimizer=torch.optim.Adam,\n iterator_train__shuffle=False,\n iterator_train__sampler=train_sampler,\n iterator_valid__sampler=valid_sampler,\n train_split=predefined_split(valid_set), # using valid_set for validation\n optimizer__lr=lr,\n batch_size=batch_size,\n callbacks=callbacks,\n device=device\n)\n# Model training for a specified number of epochs. `y` is None as it is already\n# supplied in the dataset.\nclf.fit(train_set, y=None, epochs=n_epochs)"
"from skorch.helper import predefined_split\nfrom skorch.callbacks import EpochScoring\nfrom braindecode import EEGClassifier\n\nlr = 1e-3\nbatch_size = 32\nn_epochs = 10\n\ntrain_bal_acc = EpochScoring(\n scoring='balanced_accuracy', on_train=True, name='train_bal_acc',\n lower_is_better=False)\nvalid_bal_acc = EpochScoring(\n scoring='balanced_accuracy', on_train=False, name='valid_bal_acc',\n lower_is_better=False)\ncallbacks = [\n ('train_bal_acc', train_bal_acc),\n ('valid_bal_acc', valid_bal_acc)\n]\n\nclf = EEGClassifier(\n model,\n criterion=torch.nn.CrossEntropyLoss,\n criterion__weight=torch.Tensor(class_weights).to(device),\n optimizer=torch.optim.Adam,\n iterator_train__shuffle=False,\n iterator_train__sampler=train_sampler,\n iterator_valid__sampler=valid_sampler,\n train_split=predefined_split(valid_set), # using valid_set for validation\n optimizer__lr=lr,\n batch_size=batch_size,\n callbacks=callbacks,\n device=device,\n classes=np.unique(y_train),\n)\n# Model training for a specified number of epochs. `y` is None as it is already\n# supplied in the dataset.\nclf.fit(train_set, y=None, epochs=n_epochs)"
]
},
{
Expand Down Expand Up @@ -242,7 +231,25 @@
},
"outputs": [],
"source": [
"from sklearn.metrics import confusion_matrix, classification_report\n\ny_true = [valid_set[[i]][1][0] for i in range(len(valid_sampler))]\ny_pred = clf.predict(valid_set)\n\nprint(confusion_matrix(y_true, y_pred))\nprint(classification_report(y_true, y_pred))"
"from sklearn.metrics import confusion_matrix, classification_report\nfrom braindecode.visualization import plot_confusion_matrix\n\ny_true = [valid_set[[i]][1][0] for i in range(len(valid_sampler))]\ny_pred = clf.predict(valid_set)\n\nconfusion_mat = confusion_matrix(y_true, y_pred)\n\nplot_confusion_matrix(confusion_mat=confusion_mat,\n class_names=['Wake', 'N1', 'N2', 'N3', 'REM'])\n\nprint(classification_report(y_true, y_pred))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we can also visualize the hypnogram of the recording we used for\nvalidation, with the predicted sleep stages overlaid on top of the true\nsleep stages. We can see that the model cannot correctly identify the\ndifferent sleep stages with this amount of training.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n\nfig, ax = plt.subplots(figsize=(15, 5))\nax.plot(y_true, color='b', label='Expert annotations')\nax.plot(y_pred.flatten(), color='r', label='Predict annotations', alpha=0.5)\nax.set_xlabel('Time (epochs)')\nax.set_ylabel('Sleep stage')"
]
},
{
Expand All @@ -251,6 +258,13 @@
"source": [
"Our model was able to learn despite the low amount of data that was available\n(only two recordings in this example) and reached a balanced accuracy of\nabout 36% in a 5-class classification task (chance-level = 20%) on held-out\ndata.\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>To further improve performance, more recordings should be included in the\n training set, and hyperparameters should be selected accordingly.\n Increasing the sequence length was also shown in [1]_ to help improve\n performance, especially when few EEG channels are available.</p></div>\n\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## References\n\n.. [1] Chambon, S., Galtier, M., Arnal, P., Wainrib, G. and Gramfort, A.\n (2018)A Deep Learning Architecture for Temporal Sleep Stage\n Classification Using Multivariate and Multimodal Time Series.\n IEEE Trans. on Neural Systems and Rehabilitation Engineering 26:\n (758-769)\n\n.. [2] B Kemp, AH Zwinderman, B Tuk, HAC Kamphuisen, JJL Obery\u00e9. Analysis of\n a sleep-dependent neuronal feedback loop: the slow-wave\n microcontinuity of the EEG. IEEE-BME 47(9):1185-1194 (2000).\n\n.. [3] Goldberger AL, Amaral LAN, Glass L, Hausdorff JM, Ivanov PCh,\n Mark RG, Mietus JE, Moody GB, Peng C-K, Stanley HE. (2000)\n PhysioBank, PhysioToolkit, and PhysioNet: Components of a New\n Research Resource for Complex Physiologic Signals.\n Circulation 101(23):e215-e220\n\n"
]
}
],
"metadata": {
Expand All @@ -269,7 +283,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.12"
"version": "3.9.5"
}
},
"nbformat": 4,
Expand Down
Loading

0 comments on commit 9416c82

Please sign in to comment.