
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "auto_examples/plot_bcic_iv_2a_moabb_trial.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        Click :ref:`here <sphx_glr_download_auto_examples_plot_bcic_iv_2a_moabb_trial.py>`
        to download the full example code

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_auto_examples_plot_bcic_iv_2a_moabb_trial.py:


Trialwise Decoding on BCIC IV 2a Dataset
========================================

This tutorial shows you how to train and test deep learning models with
Braindecode in a classical EEG setting: you have trials of data with
labels (e.g., Right Hand, Left Hand, etc.).

.. GENERATED FROM PYTHON SOURCE LINES 12-15

Loading and preprocessing the dataset
-------------------------------------


.. GENERATED FROM PYTHON SOURCE LINES 18-21

Loading
~~~~~~~


.. GENERATED FROM PYTHON SOURCE LINES 24-35

First, we load the data. In this tutorial, we use the functionality of
braindecode to load datasets through
`MOABB <https://github.com/NeuroTechX/moabb>`__ to load the BCI
Competition IV 2a data.

.. note::
   To load your own datasets either via mne or from
   preprocessed X/y numpy arrays, see `MNE Dataset
   Tutorial <./plot_mne_dataset_example.html>`__ and `Numpy Dataset
   Tutorial <./plot_custom_dataset_example.html>`__.


.. GENERATED FROM PYTHON SOURCE LINES 35-42

.. code-block:: default


    from braindecode.datasets.moabb import MOABBDataset

    subject_id = 3
    dataset = MOABBDataset(dataset_name="BNCI2014001", subject_ids=[subject_id])






.. rst-class:: sphx-glr-script-out

 Out:

 .. code-block:: none

    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]
    48 events found
    Event IDs: [1 2 3 4]




.. GENERATED FROM PYTHON SOURCE LINES 43-46

Preprocessing
~~~~~~~~~~~~~


.. GENERATED FROM PYTHON SOURCE LINES 49-62

Now we apply preprocessing like bandpass filtering to our dataset. You
can either apply functions provided by
`mne.Raw <https://mne.tools/stable/generated/mne.io.Raw.html>`__ or
`mne.Epochs <https://mne.tools/0.11/generated/mne.Epochs.html#mne.Epochs>`__
or apply your own functions, either to the MNE object or the underlying
numpy array.

.. note::
   These prepocessings are now directly applied to the loaded
   data, and not on-the-fly applied as transformations in
   PyTorch-libraries like
   `torchvision <https://pytorch.org/docs/stable/torchvision/index.html>`__.


.. GENERATED FROM PYTHON SOURCE LINES 62-84

.. code-block:: default


    from braindecode.datautil.preprocess import (
        exponential_moving_standardize, preprocess, Preprocessor)

    low_cut_hz = 4.  # low cut frequency for filtering
    high_cut_hz = 38.  # high cut frequency for filtering
    # Parameters for exponential moving standardization
    factor_new = 1e-3
    init_block_size = 1000

    preprocessors = [
        Preprocessor('pick_types', eeg=True, meg=False, stim=False),  # Keep EEG sensors
        Preprocessor(lambda x: x * 1e6),  # Convert from V to uV
        Preprocessor('filter', l_freq=low_cut_hz, h_freq=high_cut_hz),  # Bandpass filter
        Preprocessor(exponential_moving_standardize,  # Exponential moving standardization
                     factor_new=factor_new, init_block_size=init_block_size)
    ]

    # Transform the data
    preprocess(dataset, preprocessors)






.. rst-class:: sphx-glr-script-out

 Out:

 .. code-block:: none

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)

    Filtering raw data in 1 contiguous segment
    Setting up band-pass filter from 4 - 38 Hz

    FIR filter parameters
    ---------------------
    Designing a one-pass, zero-phase, non-causal bandpass filter:
    - Windowed time-domain design (firwin) method
    - Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
    - Lower passband edge: 4.00
    - Lower transition bandwidth: 2.00 Hz (-6 dB cutoff frequency: 3.00 Hz)
    - Upper passband edge: 38.00 Hz
    - Upper transition bandwidth: 9.50 Hz (-6 dB cutoff frequency: 42.75 Hz)
    - Filter length: 413 samples (1.652 sec)





.. GENERATED FROM PYTHON SOURCE LINES 85-88

Cut Compute Windows
~~~~~~~~~~~~~~~~~~~


.. GENERATED FROM PYTHON SOURCE LINES 91-97

Now we cut out compute windows, the inputs for the deep networks during
training. In the case of trialwise decoding, we just have to decide if
we want to cut out some part before and/or after the trial. For this
dataset, in our work, it often was beneficial to also cut out 500 ms
before the trial.


.. GENERATED FROM PYTHON SOURCE LINES 97-118

.. code-block:: default


    import numpy as np
    from braindecode.datautil.windowers import create_windows_from_events

    trial_start_offset_seconds = -0.5
    # Extract sampling frequency, check that they are same in all datasets
    sfreq = dataset.datasets[0].raw.info['sfreq']
    assert all([ds.raw.info['sfreq'] == sfreq for ds in dataset.datasets])
    # Calculate the trial start offset in samples.
    trial_start_offset_samples = int(trial_start_offset_seconds * sfreq)

    # Create windows using braindecode function for this. It needs parameters to define how
    # trials should be used.
    windows_dataset = create_windows_from_events(
        dataset,
        trial_start_offset_samples=trial_start_offset_samples,
        trial_stop_offset_samples=0,
        preload=True,
    )






.. rst-class:: sphx-glr-script-out

 Out:

 .. code-block:: none

    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped
    Used Annotations descriptions: ['feet', 'left_hand', 'right_hand', 'tongue']
    Adding metadata with 4 columns
    Replacing existing metadata with 4 columns
    48 matching events found
    No baseline correction applied
    0 projection items activated
    Loading data for 48 events and 1125 original time points ...
    0 bad epochs dropped




.. GENERATED FROM PYTHON SOURCE LINES 119-122

Split dataset into train and valid
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


.. GENERATED FROM PYTHON SOURCE LINES 125-129

We can easily split the dataset using additional info stored in the
description attribute, in this case ``session`` column. We select
``session_T`` for training and ``session_E`` for validation.


.. GENERATED FROM PYTHON SOURCE LINES 129-135

.. code-block:: default


    splitted = windows_dataset.split('session')
    train_set = splitted['session_T']
    valid_set = splitted['session_E']









.. GENERATED FROM PYTHON SOURCE LINES 136-139

Create model
------------


.. GENERATED FROM PYTHON SOURCE LINES 142-151

Now we create the deep learning model! Braindecode comes with some
predefined convolutional neural network architectures for raw
time-domain EEG. Here, we use the shallow ConvNet model from `Deep
learning with convolutional neural networks for EEG decoding and
visualization <https://arxiv.org/abs/1703.05051>`__. These models are
pure `PyTorch <https://pytorch.org>`__ deep learning models, therefore
to use your own model, it just has to be a normal PyTorch
`nn.Module <https://pytorch.org/docs/stable/nn.html#torch.nn.Module>`__.


.. GENERATED FROM PYTHON SOURCE LINES 151-182

.. code-block:: default


    import torch
    from braindecode.util import set_random_seeds
    from braindecode.models import ShallowFBCSPNet

    cuda = torch.cuda.is_available()  # check if GPU is available, if True chooses to use it
    device = 'cuda' if cuda else 'cpu'
    if cuda:
        torch.backends.cudnn.benchmark = True
    seed = 20200220  # random seed to make results reproducible
    # Set random seed to be able to reproduce results
    set_random_seeds(seed=seed, cuda=cuda)

    n_classes = 4
    # Extract number of chans and time steps from dataset
    n_chans = train_set[0][0].shape[0]
    input_window_samples = train_set[0][0].shape[1]

    model = ShallowFBCSPNet(
        n_chans,
        n_classes,
        input_window_samples=input_window_samples,
        final_conv_length='auto',
    )

    # Send model to GPU
    if cuda:
        model.cuda()










.. GENERATED FROM PYTHON SOURCE LINES 183-186

Training
--------


.. GENERATED FROM PYTHON SOURCE LINES 189-194

Now we train the network! EEGClassifier is a Braindecode object
responsible for managing the training of neural networks. It inherits
from skorch.NeuralNetClassifier, so the training logic is the same as in
`Skorch <https://skorch.readthedocs.io/en/stable/>`__.


.. GENERATED FROM PYTHON SOURCE LINES 197-202

**Note**: In this tutorial, we use some default parameters that we
have found to work well for motor decoding, however we strongly
encourage you to perform your own hyperparameter optimization using
cross validation on your training data.


.. GENERATED FROM PYTHON SOURCE LINES 202-236

.. code-block:: default


    from skorch.callbacks import LRScheduler
    from skorch.helper import predefined_split

    from braindecode import EEGClassifier
    # These values we found good for shallow network:
    lr = 0.0625 * 0.01
    weight_decay = 0

    # For deep4 they should be:
    # lr = 1 * 0.01
    # weight_decay = 0.5 * 0.001

    batch_size = 64
    n_epochs = 4

    clf = EEGClassifier(
        model,
        criterion=torch.nn.NLLLoss,
        optimizer=torch.optim.AdamW,
        train_split=predefined_split(valid_set),  # using valid_set for validation
        optimizer__lr=lr,
        optimizer__weight_decay=weight_decay,
        batch_size=batch_size,
        callbacks=[
            "accuracy", ("lr_scheduler", LRScheduler('CosineAnnealingLR', T_max=n_epochs - 1)),
        ],
        device=device,
    )
    # Model training for a specified number of epochs. `y` is None as it is already supplied
    # in the dataset.
    clf.fit(train_set, y=None, epochs=n_epochs)






.. rst-class:: sphx-glr-script-out

 Out:

 .. code-block:: none

      epoch    train_accuracy    train_loss    valid_accuracy    valid_loss      lr     dur
    -------  ----------------  ------------  ----------------  ------------  ------  ------
          1            [36m0.2500[0m        [32m1.6266[0m            [35m0.2500[0m        [31m6.7547[0m  0.0006  1.2799
          2            0.2500        [32m1.2292[0m            0.2500        6.9929  0.0005  0.4282
          3            0.2500        [32m1.0460[0m            0.2500        [31m5.5934[0m  0.0002  0.4284
          4            0.2500        1.0613            0.2500        [31m4.2572[0m  0.0000  0.4272

    <class 'braindecode.classifier.EEGClassifier'>[initialized](
      module_=ShallowFBCSPNet(
        (ensuredims): Ensure4d()
        (dimshuffle): Expression(expression=transpose_time_to_spat) 
        (conv_time): Conv2d(1, 40, kernel_size=(25, 1), stride=(1, 1))
        (conv_spat): Conv2d(40, 40, kernel_size=(1, 22), stride=(1, 1), bias=False)
        (bnorm): BatchNorm2d(40, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (conv_nonlin_exp): Expression(expression=square) 
        (pool): AvgPool2d(kernel_size=(75, 1), stride=(15, 1), padding=0)
        (pool_nonlin_exp): Expression(expression=safe_log) 
        (drop): Dropout(p=0.5, inplace=False)
        (conv_classifier): Conv2d(40, 4, kernel_size=(69, 1), stride=(1, 1))
        (softmax): LogSoftmax(dim=1)
        (squeeze): Expression(expression=squeeze_final_output) 
      ),
    )



.. GENERATED FROM PYTHON SOURCE LINES 237-240

Plot Results
------------


.. GENERATED FROM PYTHON SOURCE LINES 243-246

Now we use the history stored by Skorch throughout training to plot
accuracy and loss curves.


.. GENERATED FROM PYTHON SOURCE LINES 246-283

.. code-block:: default


    import matplotlib.pyplot as plt
    from matplotlib.lines import Line2D
    import pandas as pd

    # Extract loss and accuracy values for plotting from history object
    results_columns = ['train_loss', 'valid_loss', 'train_accuracy', 'valid_accuracy']
    df = pd.DataFrame(clf.history[:, results_columns], columns=results_columns,
                      index=clf.history[:, 'epoch'])

    # get percent of misclass for better visual comparison to loss
    df = df.assign(train_misclass=100 - 100 * df.train_accuracy,
                   valid_misclass=100 - 100 * df.valid_accuracy)

    plt.style.use('seaborn')
    fig, ax1 = plt.subplots(figsize=(8, 3))
    df.loc[:, ['train_loss', 'valid_loss']].plot(
        ax=ax1, style=['-', ':'], marker='o', color='tab:blue', legend=False, fontsize=14)

    ax1.tick_params(axis='y', labelcolor='tab:blue', labelsize=14)
    ax1.set_ylabel("Loss", color='tab:blue', fontsize=14)

    ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis

    df.loc[:, ['train_misclass', 'valid_misclass']].plot(
        ax=ax2, style=['-', ':'], marker='o', color='tab:red', legend=False)
    ax2.tick_params(axis='y', labelcolor='tab:red', labelsize=14)
    ax2.set_ylabel("Misclassification Rate [%]", color='tab:red', fontsize=14)
    ax2.set_ylim(ax2.get_ylim()[0], 85)  # make some room for legend
    ax1.set_xlabel("Epoch", fontsize=14)

    # where some data has already been plotted to ax
    handles = []
    handles.append(Line2D([0], [0], color='black', linewidth=1, linestyle='-', label='Train'))
    handles.append(Line2D([0], [0], color='black', linewidth=1, linestyle=':', label='Valid'))
    plt.legend(handles, [h.get_label() for h in handles], fontsize=14)
    plt.tight_layout()



.. image:: /auto_examples/images/sphx_glr_plot_bcic_iv_2a_moabb_trial_001.png
    :alt: plot bcic iv 2a moabb trial
    :class: sphx-glr-single-img






.. rst-class:: sphx-glr-timing

   **Total running time of the script:** ( 0 minutes  6.608 seconds)


.. _sphx_glr_download_auto_examples_plot_bcic_iv_2a_moabb_trial.py:


.. only :: html

 .. container:: sphx-glr-footer
    :class: sphx-glr-footer-example



  .. container:: sphx-glr-download sphx-glr-download-python

     :download:`Download Python source code: plot_bcic_iv_2a_moabb_trial.py <plot_bcic_iv_2a_moabb_trial.py>`



  .. container:: sphx-glr-download sphx-glr-download-jupyter

     :download:`Download Jupyter notebook: plot_bcic_iv_2a_moabb_trial.ipynb <plot_bcic_iv_2a_moabb_trial.ipynb>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
