Skip to content

Instantly share code, notes, and snippets.

@sylvchev
Forked from gemeinl/to_scikit_learn_API.ipynb
Created June 15, 2021 11:21
Show Gist options
  • Save sylvchev/270f1377a997d06a56cf49bea36ed013 to your computer and use it in GitHub Desktop.
Save sylvchev/270f1377a997d06a56cf49bea36ed013 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"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# 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": 5,
"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": 6,
"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": 7,
"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": 8,
"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.0413\n",
" 2 0.2500 \u001b[32m1.1950\u001b[0m 0.2500 7.2211 0.2248\n",
" 3 0.2500 \u001b[32m1.0809\u001b[0m 0.2500 \u001b[31m5.8693\u001b[0m 0.2272\n",
" 4 \u001b[36m0.2569\u001b[0m \u001b[32m1.0008\u001b[0m \u001b[35m0.2535\u001b[0m \u001b[31m4.5076\u001b[0m 0.2266\n"
]
}
],
"source": [
"pipe = pipe.fit(dataset, classifier__epochs=n_epochs)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"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