<jupyter_start><jupyter_text>Clinical BCI Challenge-WCCI2020- [website link](https://sites.google.com/view/bci-comp-wcci/?fbclid=IwAR37WLQ_xNd5qsZvktZCT8XJerHhmVb_bU5HDu69CnO85DE3iF0fs57vQ6M) - [Dataset Link](https://github.com/5anirban9/Clinical-Brain-Computer-Interfaces-Challenge-WCCI-2020-Glasgow)   - [Braindecode Tutorial](https://braindecode.org/auto_examples/plot_bcic_iv_2a_moabb_trial.html)<jupyter_code>import braindecode
import mne
from scipy.io import loadmat
import scipy
import sklearn
import numpy as np
import pandas as pd
import glob
from mne.decoding import CSP
import os
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC, SVC
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV, StratifiedShuffleSplit
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as lda
import warnings
warnings.filterwarnings('ignore') # to ignore warnings
verbose = False                    # global variable to suppress output display of MNE functions
mne.set_log_level(verbose=verbose) # to suppress large info outputs
n_jobs = None  # for multicore parallel processing, set it to 1 if cause memory issues, for full utilization set to -1<jupyter_output><empty_output><jupyter_text>Data Loading and Conversion to MNE Datatypes[Mike Cohen Tutorials link for EEG Preprocessing](https://www.youtube.com/watch?v=uWB5tjhataY&list=PLn0OLiymPak2gDD-VDA90w9_iGDgOOb2o)<jupyter_code>current_folder = globals()['_dh'][0]  # a hack to get path of current folder in which jupyter file is located
data_path = os.path.join(current_folder, 'Data')
training_files   = glob.glob(data_path + '/*T.mat')
len(training_files)     # if  return zero,then no file is loaded
# we have modified the labels values from [1, 2] to [0, 1] as pytorch 
# expects labels/classes to be in [0, n_classes-1] format
def get_mne_epochs(filepath, verbose=verbose, t_start=2, fs=512, mode='train'):
    '''
    This function reads the EEG data from .mat file and convert it to MNE-Python Compatible epochs
    data structure. It takes data from [0, 8] sec range and return it by setting t = 0 at cue onset
    i.e. 3 seconds and dropping first two seconds so the output data is in [-1.0, 5.0] sec range. The
    Details can be found in the preprocessing section of the attached document
    '''
    mat_data = loadmat(filepath) # read .mat file
    eeg_data= mat_data['RawEEGData']
    idx_start = fs*t_start      
    eeg_data = eeg_data[:, :, idx_start:]
    event_id = {'left-hand': 0, 'right-hand': 1} # pytorch expects labels in [0, n_classes-1]
    channel_names = ['F3', 'FC3', 'C3', 'CP3', 'P3', 'FCz', 'CPz', 'F4', 'FC4', 'C4', 'CP4', 'P4']
    info = mne.create_info(ch_names=channel_names, sfreq=fs, ch_types='eeg')
    epochs = mne.EpochsArray(eeg_data, info, verbose=verbose, tmin=t_start-3.0)
    epochs.set_montage('standard_1020')
    epochs.filter(1., None) 
    epochs.apply_baseline(baseline=(-.250, 0)) # linear baseline correction
    
    if mode == 'train': # this in only applicable for training data
        epochs.event_id = event_id 
        epochs.events[:,2] = mat_data['Labels'].ravel() - 1    
    return epochs 

def get_labels(filepath):
    mat_data = loadmat(filepath) # read .mat file
    return mat_data['Labels'].ravel() - 1
epochs, labels = get_mne_epochs(training_files[0], verbose=verbose), get_labels(training_files[0])
data = epochs.get_data()
print('Shape of EEG Data: ', data.shape, '\t Shape of Labels: ', labels.shape)<jupyter_output>Shape of EEG Data:  (80, 12, 3072) 	 Shape of Labels:  (80,)<jupyter_text>Training Data<jupyter_code># loading original data
epochs_list_train = []
for i in training_files:
    epochs_list_train.append(get_mne_epochs(i, verbose=verbose))<jupyter_output><empty_output><jupyter_text>Deep Learning with Braindecode   It's Training Time with [0.5, 4.5] sec and 2sec window with 125ms stride<jupyter_code>from braindecode.datautil import create_from_mne_epochs

# convert epochs to braindecode compatible datastructure 
# 2sec windows with 0.125 sec stride
window_size = 1024 #50 # 3072
window_stride = 64 #256 # 50

windows_datasets_list = []
for epoch in epochs_list_train:
    windows_datasets_list.append(
            create_from_mne_epochs(
            [epoch.crop(tmin=0.5, tmax=4.5, include_tmax=False)], # [0.5, 4.5] s, expects list of epochs
            window_size_samples = window_size,
            window_stride_samples = window_stride,
            drop_last_window = False
        )
    )
def get_windows_datasets_labels(windows_dataset):
    labels = []
    for i in range(len(windows_dataset.datasets)):
        labels.extend(windows_dataset.datasets[i].y)
    return np.array(labels) 

for windows_dataset in windows_datasets_list:
    windows_dataset.description = pd.DataFrame(data=get_windows_datasets_labels(windows_dataset), 
                                           columns=['labels'])
print("Total Windows in a Single Dataset: ", len(windows_datasets_list[0].description))
from braindecode.datautil.preprocess import exponential_moving_standardize
from braindecode.datautil.preprocess import MNEPreproc, NumpyPreproc, preprocess

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

def custom_exp_moving_std_fn(epochs, factor_new=factor_new, init_block_size=init_block_size):
    data = epochs.get_data()
    for i in range(len(data)):
        epochs._data[i] = exponential_moving_standardize(data[i], 
                        factor_new=factor_new, init_block_size=init_block_size)
    return epochs

preprocessors = [
    # keep only EEG sensors
    MNEPreproc(fn='pick_types', eeg=True, meg=False, stim=False),
    # bandpass filter
    MNEPreproc(fn='filter', l_freq=low_cut_hz, h_freq=high_cut_hz),
    # exponential moving standardization
    MNEPreproc(fn=custom_exp_moving_std_fn, factor_new=factor_new,
        init_block_size=init_block_size)
]
for windows_dataset in windows_datasets_list: 
    preprocess(windows_dataset, preprocessors)
batch_size = 32 #64
n_epochs = 25 #25 #20 #25 use few epochs for quick verification
# Creating a model
import torch
from braindecode.util import set_random_seeds
from braindecode.models import ShallowFBCSPNet, EEGNetv4

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=2
# Extract number of chans and time steps from dataset
n_chans = windows_datasets_list[0][0][0].shape[0]
input_window_samples = windows_datasets_list[0][0][0].shape[1]

model = EEGNetv4(
    n_chans,
    n_classes,
    input_window_samples = window_size, #input_window_samples,
    final_conv_length='auto',
)

# Send model to GPU
if cuda:
    model.cuda()
# Training time
from skorch.callbacks import LRScheduler
from skorch.helper import predefined_split
from braindecode import EEGClassifier

lr = 1 * 0.02 #0.01 
weight_decay = 0.5 * 0.001

clfs_list = []
for i in range(len(epochs_list_train)):
    clfs_list.append(
        EEGClassifier(
                    model,
                    criterion=torch.nn.NLLLoss,
                    optimizer=torch.optim.AdamW,
                    #train_split=predefined_split(train_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,
                    )
                )
def training_function(subject_index=0):
    print('\n', '#'*25, 'Training for Subject:', subject_index+1, '#'*25, '\n')
    dataset = windows_datasets_list[subject_index]
    clfs_list[subject_index].fit(dataset, y=dataset.description.labels, epochs=n_epochs);
    best_validation_acc = clfs_list[subject_index].callbacks_[4][1].best_score_ # a hack to get best validation accuracy
    best_validation_kappa = (2*best_validation_acc)-1
    print("Best Cross Validation Kappa Score: {:.2f}".format(best_validation_kappa))
for subject in range(len(training_files)):
    training_function(subject)<jupyter_output>######################### Training for Subject: 1 ######################### 

  epoch    train_accuracy    train_loss    valid_accuracy    valid_loss      lr     dur
-------  ----------------  ------------  ----------------  ------------  ------  ------
      1            [36m0.5239[0m        [32m0.6587[0m            [35m0.5000[0m        [31m4.7591[0m  0.0200  9.0020
      2            [36m0.6517[0m        [32m0.3797[0m            [35m0.5074[0m        [31m2.9087[0m  0.0199  8.2990
      3            [36m0.7160[0m        [32m0.2881[0m            [35m0.5221[0m        3.3227  0.0197  9.0790
      4            [36m0.7647[0m        [32m0.2320[0m            [35m0.5257[0m        [31m2.4932[0m  0.0192  8.5510
      5            [36m0.9384[0m        [32m0.1507[0m            [35m0.7096[0m        [31m1.3055[0m  0.0187  8.8320
      6            0.5653        [32m0.1403[0m            0.5000        4.3148  0.0179  8.6180
      7            [36m0.9550[0m  [...]
{'fraction_non_alphanumeric': 0.10987310430207366, 'fraction_numerical': 0.04673475704116373, 'mean_word_length': 3.4224452554744524, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '77481', 'license': 'MIT', 'n_tokens_mistral': 3547, 'n_tokens_neox': 3237, 'n_words': 765, 'path': 'EEG-HandGrasp-Classification/5a. Within Subject Classification with EEGNet .ipynb', 'repo_name': 'SulemanRasheed/Research', 'size': '9693'}