Skip to content

Commit e8b47ab

Browse files
authored
Initial commit
0 parents  commit e8b47ab

17 files changed

+2568
-0
lines changed

.flake8

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#########################
2+
# Flake8 Configuration #
3+
# (.flake8) #
4+
#########################
5+
[flake8]
6+
ignore =
7+
# asserts are ok when testing.
8+
S101
9+
# pickle
10+
S301
11+
# pickle
12+
S403
13+
S404
14+
S603
15+
# Line break before binary operator (flake8 is wrong)
16+
W503
17+
# Ignore the spaces black puts before columns.
18+
E203
19+
# allow path extensions for testing.
20+
E402
21+
DAR101
22+
DAR201
23+
# flake and pylance disagree on linebreaks in strings.
24+
N400
25+
exclude =
26+
.tox,
27+
.git,
28+
__pycache__,
29+
docs/source/conf.py,
30+
build,
31+
dist,
32+
tests/fixtures/*,
33+
*.pyc,
34+
*.bib,
35+
*.egg-info,
36+
.cache,
37+
.eggs,
38+
data.
39+
max-line-length = 120
40+
max-complexity = 20
41+
import-order-style = pycharm
42+
application-import-names =
43+
seleqt
44+
tests

.github/workflows/test.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Tests
2+
3+
on: [ push, pull_request ]
4+
5+
jobs:
6+
lint:
7+
name: Lint
8+
runs-on: ubuntu-latest
9+
strategy:
10+
matrix:
11+
python-version: [3.10.8]
12+
steps:
13+
- uses: actions/checkout@v2
14+
- name: Set up Python ${{ matrix.python-version }}
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
- name: Install dependencies
19+
run: pip install nox
20+
- name: Run flake8
21+
run: nox -s lint
22+
- name: Run mypy
23+
run: nox -s typing

.gitignore

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
*.mat
2+
.vscode/
3+
.pytest_cache/
4+
5+
# Byte-compiled / optimized / DLL files
6+
__pycache__/
7+
*.py[cod]
8+
*$py.class
9+
10+
# C extensions
11+
*.so
12+
13+
# Distribution / packaging
14+
.Python
15+
build/
16+
develop-eggs/
17+
dist/
18+
downloads/
19+
eggs/
20+
.eggs/
21+
lib/
22+
lib64/
23+
parts/
24+
sdist/
25+
var/
26+
wheels/
27+
share/python-wheels/
28+
*.egg-info/
29+
.installed.cfg
30+
*.egg
31+
MANIFEST
32+
33+
# PyInstaller
34+
# Usually these files are written by a python script from a template
35+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
36+
*.manifest
37+
*.spec
38+
39+
# Installer logs
40+
pip-log.txt
41+
pip-delete-this-directory.txt
42+
43+
# Unit test / coverage reports
44+
htmlcov/
45+
.tox/
46+
.nox/
47+
.coverage
48+
.coverage.*
49+
.cache
50+
nosetests.xml
51+
coverage.xml
52+
*.cover
53+
*.py,cover
54+
.hypothesis/
55+
.pytest_cache/
56+
cover/
57+
58+
# Translations
59+
*.mo
60+
*.pot
61+
62+
# Django stuff:
63+
*.log
64+
local_settings.py
65+
db.sqlite3
66+
db.sqlite3-journal
67+
68+
# Flask stuff:
69+
instance/
70+
.webassets-cache
71+
72+
# Scrapy stuff:
73+
.scrapy
74+
75+
# Sphinx documentation
76+
docs/_build/
77+
78+
# PyBuilder
79+
.pybuilder/
80+
target/
81+
82+
# Jupyter Notebook
83+
.ipynb_checkpoints
84+
85+
# IPython
86+
profile_default/
87+
ipython_config.py
88+
89+
# pyenv
90+
# For a library or package, you might want to ignore these files since the code is
91+
# intended to run in multiple environments; otherwise, check them in:
92+
# .python-version
93+
94+
# pipenv
95+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
97+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
98+
# install all needed dependencies.
99+
#Pipfile.lock
100+
101+
# poetry
102+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
103+
# This is especially recommended for binary packages to ensure reproducibility, and is more
104+
# commonly ignored for libraries.
105+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
106+
#poetry.lock
107+
108+
# pdm
109+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
110+
#pdm.lock
111+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
112+
# in version control.
113+
# https://pdm.fming.dev/#use-with-ide
114+
.pdm.toml
115+
116+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117+
__pypackages__/
118+
119+
# Celery stuff
120+
celerybeat-schedule
121+
celerybeat.pid
122+
123+
# SageMath parsed files
124+
*.sage.py
125+
126+
# Environments
127+
.env
128+
.venv
129+
env/
130+
venv/
131+
ENV/
132+
env.bak/
133+
venv.bak/
134+
135+
# Spyder project settings
136+
.spyderproject
137+
.spyproject
138+
139+
# Rope project settings
140+
.ropeproject
141+
142+
# mkdocs documentation
143+
/site
144+
145+
# mypy
146+
.mypy_cache/
147+
.dmypy.json
148+
dmypy.json
149+
150+
# Pyre type checker
151+
.pyre/
152+
153+
# pytype static type analyzer
154+
.pytype/
155+
156+
# Cython debug symbols
157+
cython_debug/
158+
159+
# PyCharm
160+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
161+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162+
# and can be added to the global gitignore or merged into this file. For a more nuclear
163+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
164+
#.idea/

README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Exercise: Brain-Wave decoding with Convnets
2+
3+
In this exercise we will apply CNN to decode Brain-Wave or EEG data.
4+
The data-set was recorded at the University of Freiburg and appeared in [Deep learning with convolutional neural networks for EEG decoding and visualization](https://onlinelibrary.wiley.com/doi/10.1002/hbm.23730).
5+
The authors describe it as follows:
6+
7+
"Our “High-Gamma Dataset” is a 128-electrode dataset of which we later only use 44 sensors covering the
8+
motor cortex, (see Section 2.7.1), obtained from 14 healthy subjects (6 female, 2 left-handed, age $27.2\pm3.6$
9+
(mean $\pm$ std)) with roughly 1000 ($963.1\pm150.9$, mean $\pm$ std) four-second trials of executed movements divided
10+
into 13 runs per subject. The four classes of movements were movements of either the left hand, the right
11+
hand, both feet, and rest (no movement, but same type of visual cue as for the other classes)."
12+
13+
14+
### Setting up dependencies.
15+
Let's set up a conda environment for this exercise.
16+
Assuming conda is installed, please run:
17+
18+
```bash
19+
conda create --name braindecode python=3.9
20+
```
21+
Activate the environment:
22+
``` bash
23+
conda activate braindecode
24+
```
25+
Install the exercise dependencies:
26+
``` bash
27+
pip install -r requirements.txt
28+
```
29+
30+
31+
32+
### Getting the data
33+
34+
The dataset is available [online](https://gin.g-node.org/robintibor/high-gamma-dataset). We have prepared a download script for you.
35+
To get the data run
36+
```bash
37+
cd data
38+
python download.py
39+
```
40+
Be patient. The script will take approximately an hour to complete.
41+
Run `nox -s test` to check if the data-loading works properly.
42+
The plot below shows the first four EEG sensors of a recording.
43+
44+
![EEG-signal example](./figures/eeg-signal-example.png)
45+
46+
Examine the data yourself.
47+
48+
## Your task:
49+
Train a CNN to recognize the four actions given only the EEG data.
50+
Preprocessing code from the paper-authors is already ready for in `src/util.py`. To get started have a look at `src/train_brain_decoder.py` and load the data via:
51+
```python
52+
from src.load_eeg import load_train_valid_test
53+
low_cut_hz = 0
54+
subject_id = 1
55+
56+
train_filename = os.path.join('./data', 'train/{:d}.mat'.format(subject_id))
57+
test_filename = os.path.join('./data',
58+
'test/{:d}.mat'.format(subject_id))
59+
60+
# Create the dataset
61+
train_set, valid_set, test_set = load_train_valid_test(
62+
train_filename=train_filename, test_filename=test_filename,
63+
low_cut_hz=low_cut_hz)
64+
65+
```
66+
67+
Use as much of your code from the last two days as possible. You can re-use your image-processing code treating the EEG signals as tensors with and 44 "color"-channels and a time dimension. Implement your solution in `src/train_brain_decoder.py`. Implement everything marked with a `TODO`.
68+
69+
What happens if you train and test on two different study-participants?
70+
71+
Solution for participant 1:
72+
73+
![accuracy-plot](./figures/brain_decode_accuracy.png)

data/download.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from tqdm import tqdm
2+
import urllib.request
3+
from pathlib import Path
4+
5+
data_location = 'https://gin.g-node.org/robintibor/high-gamma-dataset/raw/master/data/'
6+
7+
files = [str(i)+'.mat' for i in range(1,15)]
8+
train_files = [(data_location + 'train/' + file, 'train/' + file) for file in files]
9+
test_files = [(data_location + 'test/' + file, 'test/' + file) for file in files]
10+
11+
12+
13+
def load_and_store(file_list):
14+
for online_path, local_path in tqdm(file_list, desc='downloading'):
15+
urllib.request.urlretrieve(online_path, local_path)
16+
17+
print('dowloading... this will take a while. Please be patient.')
18+
Path("train/").mkdir(exist_ok=True)
19+
load_and_store(train_files)
20+
print('downloading test data.')
21+
Path("test/").mkdir(exist_ok=True)
22+
load_and_store(test_files)

figures/brain_decode_accuracy.png

24.8 KB
Loading

figures/eeg-signal-example.png

85.6 KB
Loading

noxfile.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""This module implements our CI function calls."""
2+
3+
import nox
4+
5+
6+
@nox.session(name="test")
7+
def run_test(session):
8+
"""Run pytest."""
9+
session.install("-r", "requirements.txt")
10+
session.run("pytest")
11+
session.install("pytest")
12+
13+
14+
@nox.session(name="testgit")
15+
def run_test_git(session):
16+
"""Run pytest."""
17+
session.install("-r", "requirements.txt")
18+
session.install("pytest")
19+
session.run("pytest", "./tests/test_brain_decoder.py", "./tests/test_function.py")
20+
21+
22+
@nox.session(name="lint")
23+
def lint(session):
24+
"""Check code conventions."""
25+
session.install("flake8==4.0.1")
26+
session.install(
27+
"flake8-black",
28+
"flake8-docstrings",
29+
"flake8-bugbear",
30+
"flake8-broken-line",
31+
"pep8-naming",
32+
"pydocstyle",
33+
"darglint",
34+
)
35+
session.install("bandit==1.7.2")
36+
session.run("flake8", "src/train_brain_decoder.py", "tests", "noxfile.py")
37+
38+
39+
@nox.session(name="typing")
40+
def mypy(session):
41+
"""Check type hints."""
42+
session.install("-r", "requirements.txt")
43+
session.install("mypy")
44+
session.run(
45+
"mypy",
46+
"--install-types",
47+
"--non-interactive",
48+
"--ignore-missing-imports",
49+
"--no-strict-optional",
50+
"--no-warn-return-any",
51+
"--implicit-reexport",
52+
"--allow-untyped-calls",
53+
"src/train_brain_decoder.py",
54+
)
55+
56+
57+
@nox.session(name="format")
58+
def format(session):
59+
"""Fix common convention problems automatically."""
60+
session.install("black")
61+
session.install("isort")
62+
session.run("isort", "src", "tests", "noxfile.py")
63+
session.run("black", "src", "tests", "noxfile.py")

0 commit comments

Comments
 (0)