Skip to content

Instantly share code, notes, and snippets.

@gemeinl
Last active June 15, 2021 11:21
Show Gist options
  • Select an option

  • Save gemeinl/d64c014debb5f58e4feacb57a8656ed0 to your computer and use it in GitHub Desktop.

Select an option

Save gemeinl/d64c014debb5f58e4feacb57a8656ed0 to your computer and use it in GitHub Desktop.
Braindecode with sci-kit learn pipeline chaining
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/gemeinl/anaconda3/envs/new_braindecode/lib/python3.7/site-packages/sklearn/utils/deprecation.py:144: FutureWarning: The sklearn.metrics.scorer module is deprecated in version 0.22 and will be removed in version 0.24. The corresponding classes / functions should instead be imported from sklearn.metrics. Anything that cannot be imported from sklearn.metrics is now part of the private API.\n",
" warnings.warn(message, FutureWarning)\n"
]
}
],
"source": [
"import torch\n",
"from sklearn.pipeline import Pipeline\n",
"from skorch.callbacks import LRScheduler\n",
"from skorch.helper import predefined_split\n",
"from sklearn.base import TransformerMixin\n",
"\n",
"from braindecode import EEGClassifier\n",
"from braindecode.util import set_random_seeds\n",
"from braindecode.models import ShallowFBCSPNet\n",
"from braindecode.datautil.preprocess import exponential_moving_standardize\n",
"from braindecode.datasets.moabb import MOABBDataset\n",
"from braindecode.datautil.windowers import (\n",
" create_windows_from_events, create_fixed_length_windows)\n",
"from braindecode.datautil.preprocess import (\n",
" MNEPreproc, NumpyPreproc, preprocess)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class Preprocessor(TransformerMixin):\n",
" def fit(self, X, y=None):\n",
" return self\n",
" \n",
"\n",
"class EventWindower(Preprocessor):\n",
" def __init__(self, *args, **kwargs):\n",
" self.args=args\n",
" self.kwargs=kwargs\n",
" \n",
" def transform(self, X):\n",
" return create_windows_from_events(\n",
" concat_ds=X, *self.args, **self.kwargs)\n",
" \n",
" \n",
"class FixedLengthWindower(Preprocessor):\n",
" def __init__(self, *args, **kwargs):\n",
" self.args=args\n",
" self.kwargs=kwargs\n",
" \n",
" def transform(self, X):\n",
" return create_fixed_length_windows(\n",
" concat_ds=X, *self.args, **self.kwargs)\n",
"\n",
" \n",
"class MNETransformer(Preprocessor):\n",
" def __init__(self, fn, **kwargs):\n",
" self.pre = MNEPreproc(fn=fn, **kwargs)\n",
" \n",
" def transform(self, X):\n",
" preprocess(X, [self.pre])\n",
" return X\n",
"\n",
" \n",
"class NumpyTransformer(Preprocessor):\n",
" def __init__(self, fn, **kwargs):\n",
" self.pre = NumpyPreproc(fn=fn, **kwargs)\n",
" \n",
" def transform(self, X):\n",
" preprocess(X, [self.pre])\n",
" return X"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Known from experimental design\n",
"sfreq = 250 \n",
"n_classes = 4\n",
"n_chans = 22\n",
"original_trial_duration = 4\n",
"\n",
"# Preprocessing parameters\n",
"low_cut_hz = 4. # low cut frequency for filtering\n",
"high_cut_hz = 38. # high cut frequency for filtering\n",
"# Parameters for exponential moving standardization\n",
"factor_new = 1e-3\n",
"init_block_size = 1000\n",
"trial_start_offset_seconds = -0.5\n",
"# Calculate the trial start offset in samples.\n",
"trial_start_offset_samples = int(trial_start_offset_seconds * sfreq)\n",
"\n",
"\n",
"# Model parameters\n",
"seed = 20200220 # random seed to make results reproducible\n",
"input_window_samples = int(original_trial_duration * sfreq - trial_start_offset_samples)\n",
"\n",
"\n",
"# Training parameters\n",
"batch_size = 64\n",
"n_epochs = 4\n",
"# These values we found good for shallow network:\n",
"lr = 0.0625 * 0.01\n",
"weight_decay = 0\n",
"# For deep4 they should be:\n",
"# lr = 1 * 0.01\n",
"# weight_decay = 0.5 * 0.001"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"create a model"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"cuda = torch.cuda.is_available() # check if GPU is available, if True chooses to use it\n",
"device = 'cuda' if cuda else 'cpu'\n",
"if cuda:\n",
" torch.backends.cudnn.benchmark = True\n",
"\n",
"# Set random seed to be able to reproduce results\n",
"set_random_seeds(seed=seed, cuda=cuda)\n",
"\n",
"model = ShallowFBCSPNet(\n",
" n_chans,\n",
" n_classes,\n",
" input_window_samples=input_window_samples,\n",
" final_conv_length='auto',\n",
")\n",
"\n",
"# Send model to GPU\n",
"if cuda:\n",
" model.cuda()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"chain all preprocessing steps as well as classifier in a pipeline"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"pipe = Pipeline([\n",
" (\"pick_channels\", MNETransformer(\n",
" fn='pick_types', \n",
" eeg=True, \n",
" meg=False, \n",
" stim=False)\n",
" ),\n",
" (\"convert_to_microvolts\", NumpyTransformer(\n",
" fn=lambda x: x * 1e6)\n",
" ),\n",
" (\"bandpass\", MNETransformer(\n",
" fn='filter', \n",
" l_freq=low_cut_hz, \n",
" h_freq=high_cut_hz)\n",
" ),\n",
" (\"standardize\", NumpyTransformer(\n",
" fn=exponential_moving_standardize, \n",
" factor_new=factor_new,\n",
" init_block_size=init_block_size)\n",
" ),\n",
" (\"create_compute_windows\", EventWindower(\n",
" trial_start_offset_samples=trial_start_offset_samples,\n",
" trial_stop_offset_samples=0, preload=True)\n",
" ),\n",
" (\"classifier\", EEGClassifier(\n",
" model,\n",
" criterion=torch.nn.NLLLoss,\n",
" optimizer=torch.optim.AdamW,\n",
" train_split=lambda X, y: (X.split(\"session\")[\"session_T\"], \n",
" X.split(\"session\")[\"session_E\"]),\n",
" optimizer__lr=lr,\n",
" optimizer__weight_decay=weight_decay,\n",
" batch_size=batch_size,\n",
" callbacks=[\n",
" \"accuracy\", \n",
" (\"lr_scheduler\", LRScheduler('CosineAnnealingLR', T_max=n_epochs - 1)),\n",
" ],\n",
" device=device)),\n",
"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"load some data"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n",
"48 events found\n",
"Event IDs: [1 2 3 4]\n"
]
}
],
"source": [
"subject_id = 3\n",
"dataset = MOABBDataset(dataset_name=\"BNCI2014001\", subject_ids=[subject_id])\n",
"assert all([ds.raw.info['sfreq'] == sfreq for ds in dataset.datasets])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"perform fit"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Filtering raw data in 1 contiguous segment\n",
"Setting up band-pass filter from 4 - 38 Hz\n",
"\n",
"FIR filter parameters\n",
"---------------------\n",
"Designing a one-pass, zero-phase, non-causal bandpass filter:\n",
"- Windowed time-domain design (firwin) method\n",
"- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
"- Lower passband edge: 4.00\n",
"- Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)\n",
"- Upper passband edge: 38.00 Hz\n",
"- Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)\n",
"- Filter length: 413 samples (1.652 sec)\n",
"\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
"Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']\n",
"48 matching events found\n",
"No baseline correction applied\n",
"Adding metadata with 4 columns\n",
"0 projection items activated\n",
"Loading data for 48 events and 1125 original time points ...\n",
"0 bad epochs dropped\n",
"0 bad epochs dropped\n",
" epoch train_accuracy train_loss valid_accuracy valid_loss dur\n",
"------- ---------------- ------------ ---------------- ------------ ------\n",
" 1 \u001b[36m0.2500\u001b[0m \u001b[32m1.5919\u001b[0m \u001b[35m0.2500\u001b[0m \u001b[31m6.2938\u001b[0m 1.0590\n",
" 2 0.2500 \u001b[32m1.1950\u001b[0m 0.2500 7.2212 0.2257\n",
" 3 0.2500 \u001b[32m1.0809\u001b[0m 0.2500 \u001b[31m5.8696\u001b[0m 0.2249\n",
" 4 \u001b[36m0.2569\u001b[0m \u001b[32m1.0008\u001b[0m \u001b[35m0.2535\u001b[0m \u001b[31m4.5077\u001b[0m 0.2237\n"
]
}
],
"source": [
"pipe = pipe.fit(dataset, classifier__epochs=n_epochs)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x216 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"from matplotlib.lines import Line2D\n",
"import pandas as pd\n",
"\n",
"# Extract loss and accuracy values for plotting from history object\n",
"results_columns = ['train_loss', 'valid_loss', 'train_accuracy', 'valid_accuracy']\n",
"df = pd.DataFrame(pipe.steps[-1][1].history[:, results_columns], columns=results_columns,\n",
" index=pipe.steps[-1][1].history[:, 'epoch'])\n",
"\n",
"# get percent of misclass for better visual comparison to loss\n",
"df = df.assign(train_misclass=100 - 100 * df.train_accuracy,\n",
" valid_misclass=100 - 100 * df.valid_accuracy)\n",
"\n",
"plt.style.use('seaborn')\n",
"fig, ax1 = plt.subplots(figsize=(8, 3))\n",
"df.loc[:, ['train_loss', 'valid_loss']].plot(\n",
" ax=ax1, style=['-', ':'], marker='o', color='tab:blue', legend=False, fontsize=14)\n",
"\n",
"ax1.tick_params(axis='y', labelcolor='tab:blue', labelsize=14)\n",
"ax1.set_ylabel(\"Loss\", color='tab:blue', fontsize=14)\n",
"\n",
"ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis\n",
"\n",
"df.loc[:, ['train_misclass', 'valid_misclass']].plot(\n",
" ax=ax2, style=['-', ':'], marker='o', color='tab:red', legend=False)\n",
"ax2.tick_params(axis='y', labelcolor='tab:red', labelsize=14)\n",
"ax2.set_ylabel(\"Misclassification Rate [%]\", color='tab:red', fontsize=14)\n",
"ax2.set_ylim(ax2.get_ylim()[0], 85) # make some room for legend\n",
"ax1.set_xlabel(\"Epoch\", fontsize=14)\n",
"\n",
"# where some data has already been plotted to ax\n",
"handles = []\n",
"handles.append(Line2D([0], [0], color='black', linewidth=1, linestyle='-', label='Train'))\n",
"handles.append(Line2D([0], [0], color='black', linewidth=1, linestyle=':', label='Valid'))\n",
"plt.legend(handles, [h.get_label() for h in handles], fontsize=14)\n",
"plt.tight_layout()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "new_braindecode",
"language": "python",
"name": "new_braindecode"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment