def get_deep_learning_model(model_args, valid_dataset): cuda = torch.cuda.is_available() device = model_args["device"] if cuda else 'cpu' if cuda: torch.backends.cudnn.benchmark = True seed = model_args["seed"] # = 20200220 random seed to make results reproducible # Set random seed to be able to reproduce results if seed: set_random_seeds(seed=seed, cuda=cuda) if model_args["model_type"] == "ShallowFBCSPNet": model = ShallowFBCSPNet( model_args["n_chans"], model_args["n_classes"] + 1, input_window_samples=model_args["input_window_samples"], final_conv_length='auto', ) elif model_args["model_type"] == "SleepStager": model = model = SleepStager( n_channels=model_args["n_chans"], sfreq=model_args["sfreq"], n_classes=model_args["n_classes"] + 1, input_size_s=model_args["input_window_samples"] / model_args["sfreq"], ) else: raise ValueError("Boom !") if cuda: model.cuda() clf = EEGClassifier( model, criterion=model_args["criterion"], optimizer=torch.optim.AdamW, # using test_sample for validation train_split=predefined_split(valid_dataset), optimizer__lr=model_args["lr"], optimizer__weight_decay=model_args["weight_decay"], batch_size=model_args["batch_size"], callbacks=[ "accuracy", ("lr_scheduler", LRScheduler('CosineAnnealingLR', T_max=model_args["n_epochs"] - 1)), ("early_stopping", EarlyStopping(monitor='valid_loss', patience=model_args["patience"])) ], device=device, iterator_train__num_workers=20, iterator_train__pin_memory=True) # torch.in torch.out return clf
def test_eeg_classifier_clonable(): preds = np.array([ [[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]], [[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], [[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]], [[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]], ]) clf = EEGClassifier(MockModule(preds), cropped=False, callbacks=[ "accuracy", ("lr_scheduler", LRScheduler('CosineAnnealingLR', T_max=1)) ], criterion=CroppedLoss, criterion__loss_function=nll_loss, optimizer=optim.Adam, batch_size=32) clone(clf) clf.initialize() clone(clf)
def test_trialwise_predict_and_predict_proba(): preds = np.array([ [0.125, 0.875], [1., 0.], [0.8, 0.2], [0.9, 0.1], ]) clf = EEGClassifier(MockModule(preds), optimizer=optim.Adam, batch_size=32) clf.initialize() np.testing.assert_array_equal(preds.argmax(1), clf.predict(MockDataset())) np.testing.assert_array_equal(preds, clf.predict_proba(MockDataset()))
def test_cropped_predict_and_predict_proba_not_aggregate_predictions(): preds = np.array([ [[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]], [[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], [[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]], [[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]], ]) clf = EEGClassifier(MockModule(preds), cropped=True, criterion=CroppedLoss, criterion__loss_function=nll_loss, optimizer=optim.Adam, batch_size=32, aggregate_predictions=False) clf.initialize() np.testing.assert_array_equal(preds.argmax(1), clf.predict(MockDataset())) np.testing.assert_array_equal(preds, clf.predict_proba(MockDataset()))
def test_predict_trials(): preds = np.array([ [[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]], [[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], [[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]], [[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]], ]) clf = EEGClassifier(MockModule(preds), cropped=False, criterion=CroppedLoss, criterion__loss_function=nll_loss, optimizer=optim.Adam, batch_size=32) clf.initialize() with pytest.warns(UserWarning, match="This method was designed to predict " "trials in cropped mode."): clf.predict_trials(MockDataset(), return_targets=False)
def test_cropped_predict_and_predict_proba(): preds = np.array([ [[0.2, 0.1, 0.1, 0.1], [0.8, 0.9, 0.9, 0.9]], [[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], [[1.0, 1.0, 1.0, 0.2], [0.0, 0.0, 0.0, 0.8]], [[0.9, 0.8, 0.9, 1.0], [0.1, 0.2, 0.1, 0.0]], ]) clf = EEGClassifier(MockModule(preds), cropped=True, criterion=CroppedLoss, criterion__loss_function=nll_loss, optimizer=optim.Adam, batch_size=32) clf.initialize() # for cropped decoding classifier returns one label for each trial (averaged over all crops) np.testing.assert_array_equal( preds.mean(-1).argmax(1), clf.predict(MockDataset())) # for cropped decoding classifier returns values for each trial (average over all crops) np.testing.assert_array_equal(preds.mean(-1), clf.predict_proba(MockDataset()))
def exp(subject_id): import torch test_subj = np.r_[subject_id] print('test subj:' + str(test_subj)) # train_subj = np.setdiff1d(np.r_[1:10], test_subj) train_subj = np.setdiff1d(np.r_[1, 3, 7, 8], test_subj) tr = [] val = [] for ids in train_subj: train_size = int(0.99 * len(splitted[ids])) test_size = len(splitted[ids]) - train_size tr_i, val_i = torch.utils.data.random_split(splitted[ids], [train_size, test_size]) tr.append(tr_i) val.append(val_i) train_set = torch.utils.data.ConcatDataset(tr) valid_set = torch.utils.data.ConcatDataset(val) valid_set = BaseConcatDataset([splitted[ids] for ids in test_subj]) ###################################################################### # Create model # ------------ # ###################################################################### # 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>`__. # import torch from braindecode.util import set_random_seeds from braindecode.models import ShallowFBCSPNet, Deep4Net cuda = torch.cuda.is_available( ) # check if GPU is available, if True chooses to use it device = 'cuda:0' 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 = 3 # 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', # ) from mynetworks import Deep4Net_origin, ConvClfNet, FcClfNet model = Deep4Net( n_chans, n_classes, input_window_samples=input_window_samples, final_conv_length="auto", ) # # embedding_net = Deep4Net_origin(4, 22, input_window_samples) # model = FcClfNet(embedding_net) # # print(model) # Send model to GPU if cuda: model.cuda() ###################################################################### # Training # -------- # ###################################################################### # 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/>`__. # ###################################################################### # **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. # 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 = 8 n_epochs = 100 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) ###################################################################### # Plot Results # ------------ # ###################################################################### # Now we use the history stored by Skorch throughout training to plot # accuracy and loss curves. # 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() # plt.show() return df
train_bal_acc = EpochScoring(scoring='balanced_accuracy', on_train=True, name='train_bal_acc', lower_is_better=False) valid_bal_acc = EpochScoring(scoring='balanced_accuracy', on_train=False, name='valid_bal_acc', lower_is_better=False) callbacks = [('train_bal_acc', train_bal_acc), ('valid_bal_acc', valid_bal_acc)] clf = EEGClassifier( model, criterion=torch.nn.CrossEntropyLoss, optimizer=torch.optim.Adam, train_split=predefined_split(valid_set), # using valid_set for validation optimizer__lr=lr, batch_size=batch_size, callbacks=callbacks, 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) ###################################################################### # Plot results # ------------ # ###################################################################### # We use the history stored by Skorch during training to plot the performance of
if re.compile('/Users/long/').match(location): my_callbacks = [ "accuracy", ("lr_scheduler", LRScheduler('CosineAnnealingLR', T_max=n_epochs - 1)), ('on_epoch_begin_callback', on_epoch_begin_callback), ('on_batch_end_callback', on_batch_end_callback), ] elif re.compile('/content/drive').match(location): my_callbacks = [ "accuracy", ("lr_scheduler", LRScheduler('CosineAnnealingLR', T_max=n_epochs - 1)), ] clf = EEGClassifier( net, #criterion=torch.nn.NLLLoss, #torch.nn.NLLLoss/CrossEntropyLoss criterion=torch.nn.CrossEntropyLoss, optimizer=torch.optim.Adam, #optimizer=torch.optim.AdamW, train_split=predefined_split( valid_set ), # using valid_set for validation; None means no validate:both train and test on training dataset. optimizer__lr=lr, optimizer__weight_decay=weight_decay, batch_size=batch_size, callbacks=my_callbacks, 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)
batch_size = 64 n_epochs = 100 from skorch.callbacks import Checkpoint, EarlyStopping, EpochScoring early_stopping = EarlyStopping(patience=30) cp = Checkpoint(dirname='', f_criterion=None, f_optimizer=None, f_history=None) clf = EEGClassifier( model, # criterion=torch.nn.NLLLoss, criterion=torch.nn.CrossEntropyLoss, 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)),('cp', cp),('patience', early_stopping) ], 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) clf.load_params(checkpoint=cp) # Load the model with the lowest valid_loss import os os.remove('./params.pt') # Delete parameters file ######################################################################
def test_variable_length_trials_cropped_decoding(): cuda = False set_random_seeds(seed=20210726, cuda=cuda) # create fake tuh abnormal dataset tuh = _TUHAbnormalMock(path='') # fake variable length trials by cropping first recording splits = tuh.split([[i] for i in range(len(tuh.datasets))]) preprocess( concat_ds=splits['0'], preprocessors=[ Preprocessor('crop', tmax=300), ], ) variable_tuh = BaseConcatDataset( [splits[str(i)] for i in range(len(tuh.datasets))]) # make sure we actually have different length trials assert any(np.diff([ds.raw.n_times for ds in variable_tuh.datasets]) != 0) # create windows variable_tuh_windows = create_fixed_length_windows( concat_ds=variable_tuh, window_size_samples=1000, window_stride_samples=1000, drop_last_window=False, mapping={ True: 1, False: 0 }, ) # create train and valid set splits = variable_tuh_windows.split( [[i] for i in range(len(variable_tuh_windows.datasets))]) variable_tuh_windows_train = BaseConcatDataset( [splits[str(i)] for i in range(len(tuh.datasets) - 1)]) variable_tuh_windows_valid = BaseConcatDataset( [splits[str(len(tuh.datasets) - 1)]]) for x, y, ind in variable_tuh_windows_train: break train_split = predefined_split(variable_tuh_windows_valid) # initialize a model model = ShallowFBCSPNet( in_chans=x.shape[0], n_classes=len(tuh.description.pathological.unique()), ) to_dense_prediction_model(model) if cuda: model.cuda() # create and train a classifier clf = EEGClassifier( model, cropped=True, criterion=CroppedLoss, criterion__loss_function=torch.nn.functional.nll_loss, optimizer=torch.optim.Adam, batch_size=32, callbacks=['accuracy'], train_split=train_split, ) clf.fit(variable_tuh_windows_train, y=None, epochs=3) # make sure it does what we expect np.testing.assert_allclose( clf.history[:, 'train_loss'], np.array([ 0.689495325088501, 0.1353449523448944, 0.006638816092163324, ]), rtol=1e-1, atol=1e-1, ) np.testing.assert_allclose( clf.history[:, 'valid_loss'], np.array([ 2.925871, 3.611423, 4.23494, ]), rtol=1e-1, atol=1e-1, )
args.input_window_samples)[2] valid_set, train_set = windowing_data(dataset, args) del dataset # ============================================================================= # Training the model # ============================================================================= clf = EEGClassifier( model, cropped=True, criterion=CroppedLoss, criterion__loss_function=torch.nn.functional.nll_loss, optimizer=torch.optim.AdamW, train_split=predefined_split(valid_set), optimizer__lr=args.lr, optimizer__weight_decay=args.wd, iterator_train__shuffle=True, batch_size=args.batch_size, callbacks=['accuracy'], device=args.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=args.n_epochs) # ============================================================================= # Storing the results into a dictionary and saving # =============================================================================
name='train_acc', lower_is_better=False) valid_acc = EpochScoring(scoring='accuracy', on_train=False, name='valid_acc', lower_is_better=False) callbacks = [('cp', cp), ('patience', early_stopping), ('train_acc', train_acc), ('valid_acc', valid_acc)] clf = EEGClassifier(model, criterion=torch.nn.BCEWithLogitsLoss, optimizer=torch.optim.Adam, max_epochs=n_epochs, iterator_train__shuffle=False, iterator_train__sampler=train_sampler, iterator_valid__sampler=valid_sampler, iterator_train__num_workers=num_workers, iterator_valid__num_workers=num_workers, train_split=predefined_split(splitted['valid']), optimizer__lr=lr, batch_size=batch_size, callbacks=callbacks, device=device) # Model training for a specified number of epochs. `y` is None as it is already # supplied in the dataset. clf.fit(splitted['train'], y=None) clf.load_params(checkpoint=cp) # Load the model with the lowest valid_loss os.remove('./params.pt') # Delete parameters file ###################################################################### # Visualizing the results
preload=True, ) splitted = windows_dataset.split('session') train_set = splitted['session_T'] valid_set = splitted['session_E'] clf = EEGClassifier( model, cropped=False, criterion=torch.nn.NLLLoss, optimizer=torch.optim.AdamW, train_split=predefined_split(valid_set), optimizer__lr=lr, optimizer__weight_decay=weight_decay, iterator_train__shuffle=True, batch_size=batch_size, callbacks=[ "accuracy", # seems n_epochs -1 leads to desired behavior of lr=0 after end of training? ("lr_scheduler", LRScheduler('CosineAnnealingLR', T_max=n_epochs - 1)), ], device=device, ) clf.fit(windows_dataset, y=None, epochs=n_epochs) ############################################################################### # Plot Results ignore_keys = [
def exp(subject_id): test_subj = np.r_[subject_id] print('test subj:' + str(test_subj)) train_subj = np.setdiff1d(np.r_[1:11], test_subj) train_set = BaseConcatDataset([splitted[ids] for ids in train_subj]) valid_set = BaseConcatDataset([splitted[ids] for ids in test_subj]) # # model = ShallowFBCSPNet( n_chans, n_classes, input_window_samples=input_window_samples, final_conv_length=30, ) # # # embedding_net = Deep4Net_origin(4, 22, input_window_samples) # model = FcClfNet(embedding_net) print(model) # Send model to GPU if cuda: model.cuda() from braindecode.models.util import to_dense_prediction_model, get_output_shape to_dense_prediction_model(model) ###################################################################### # Training # -------- # ###################################################################### # In difference to trialwise decoding, we now should supply # ``cropped=True`` to the EEGClassifier, and ``CroppedLoss`` as the # criterion, as well as ``criterion__loss_function`` as the loss function # applied to the meaned predictions. # ###################################################################### # .. 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. # from skorch.callbacks import LRScheduler from skorch.helper import predefined_split from braindecode import EEGClassifier from braindecode.training.losses import CroppedLoss from braindecode.training.scoring import trial_preds_from_window_preds # # 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 = 400 n_epochs = 100 clf = EEGClassifier( model, cropped=True, criterion=CroppedLoss, criterion__loss_function=torch.nn.functional.nll_loss, optimizer=torch.optim.AdamW, train_split=predefined_split(valid_set), optimizer__lr=lr, optimizer__weight_decay=weight_decay, iterator_train__shuffle=True, 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) ###################################################################### # Plot Results # ------------ # ###################################################################### # This is again the same code as in trialwise decoding. # # .. note:: # Note that we drop further in the classification error and # loss as in the trialwise decoding tutorial. # 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() return df
def exp(subject_id): dataset = MOABBDataset(dataset_name="BNCI2014001", subject_ids=subject_id) from braindecode.datautil.preprocess import exponential_moving_standardize from braindecode.datautil.preprocess import MNEPreproc, NumpyPreproc, preprocess low_cut_hz = 0. # 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 = [ # keep only EEG sensors MNEPreproc(fn='pick_types', eeg=True, meg=False, stim=False), # convert from volt to microvolt, directly modifying the numpy array NumpyPreproc(fn=lambda x: x * 1e6), # bandpass filter MNEPreproc(fn='filter', l_freq=low_cut_hz, h_freq=high_cut_hz), # exponential moving standardization # NumpyPreproc(fn=exponential_moving_standardize, factor_new=factor_new, # init_block_size=init_block_size) ] # Transform the data preprocess(dataset, preprocessors) ###################################################################### # Create model and compute windowing parameters # --------------------------------------------- # ###################################################################### # In contrast to trialwise decoding, we first have to create the model # before we can cut the dataset into windows. This is because we need to # know the receptive field of the network to know how large the window # stride should be. # ###################################################################### # We first choose the compute/input window size that will be fed to the # network during training This has to be larger than the networks # receptive field size and can otherwise be chosen for computational # efficiency (see explanations in the beginning of this tutorial). Here we # choose 1000 samples, which are 4 seconds for the 250 Hz sampling rate. # input_window_samples = 1000 ###################################################################### # Now we create the model. To enable it to be used in cropped decoding # efficiently, we manually set the length of the final convolution layer # to some length that makes the receptive field of the ConvNet smaller # than ``input_window_samples`` (see ``final_conv_length=30`` in the model # definition). # import torch from braindecode.util import set_random_seeds from braindecode.models import ShallowFBCSPNet, Deep4Net cuda = torch.cuda.is_available( ) # check if GPU is available, if True chooses to use it device = 'cuda:1' if cuda else 'cpu' if cuda: torch.backends.cudnn.benchmark = True seed = 20190706 # 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 from dataset n_chans = dataset[0][0].shape[0] # model = Deep4Net( # n_chans, # n_classes, # input_window_samples=input_window_samples, # final_conv_length="auto", # ) # # # # embedding_net = Deep4Net_origin(4, 22, input_window_samples) # model = FcClfNet(embedding_net) model = ShallowFBCSPNet( n_chans, n_classes, input_window_samples=input_window_samples, final_conv_length=30, ) print(model) # Send model to GPU if cuda: model.cuda(device) ###################################################################### # And now we transform model with strides to a model that outputs dense # prediction, so we can use it to obtain predictions for all # crops. # from braindecode.models.util import to_dense_prediction_model, get_output_shape to_dense_prediction_model(model) n_preds_per_input = get_output_shape(model, n_chans, input_window_samples)[2] print("n_preds_per_input : ", n_preds_per_input) print(model) ###################################################################### # Cut the data into windows # ------------------------- # ###################################################################### # In contrast to trialwise decoding, we have to supply an explicit window size and window stride to the # ``create_windows_from_events`` function. # 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, window_size_samples=input_window_samples, window_stride_samples=n_preds_per_input, drop_last_window=False, preload=True, ) ###################################################################### # Split the dataset # ----------------- # # This code is the same as in trialwise decoding. # from braindecode.datasets.base import BaseConcatDataset splitted = windows_dataset.split('session') train_set = splitted['session_T'] valid_set = splitted['session_E'] ###################################################################### # In difference to trialwise decoding, we now should supply # ``cropped=True`` to the EEGClassifier, and ``CroppedLoss`` as the # criterion, as well as ``criterion__loss_function`` as the loss function # applied to the meaned predictions. # ###################################################################### # .. 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. # from skorch.callbacks import LRScheduler from skorch.helper import predefined_split from braindecode import EEGClassifier from braindecode.training.losses import CroppedLoss from braindecode.training.scoring import trial_preds_from_window_preds # # 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 = 8 n_epochs = 100 clf = EEGClassifier( model, cropped=True, criterion=CroppedLoss, criterion__loss_function=torch.nn.functional.nll_loss, optimizer=torch.optim.AdamW, train_split=predefined_split(valid_set), optimizer__lr=lr, optimizer__weight_decay=weight_decay, iterator_train__shuffle=True, 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) ###################################################################### # Plot Results # ------------ # ###################################################################### # This is again the same code as in trialwise decoding. # # .. note:: # Note that we drop further in the classification error and # loss as in the trialwise decoding tutorial. # 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() return df
# For deep4 they should be: # lr = 1 * 0.01 # weight_decay = 0.5 * 0.001 batch_size = 64 n_epochs = 4 clf = EEGClassifier( model, cropped=True, criterion=CroppedLoss, criterion__loss_function=torch.nn.functional.nll_loss, optimizer=torch.optim.AdamW, train_split=predefined_split(valid_set), optimizer__lr=lr, optimizer__weight_decay=weight_decay, iterator_train__shuffle=True, 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) ###################################################################### # Plot Results # ------------
lr = 0.0625 * 0.01 weight_decay = 0 batch_size = 64 n_epochs = 4 clf = EEGClassifier( model, iterator_train= AugmentedDataLoader, # This tells EEGClassifier to use a custom DataLoader iterator_train__transforms=transforms, # This sets the augmentations to use 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) ###################################################################### # Manually composing Transforms # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_cropped_decoding(): # 5,6,7,10,13,14 are codes for executed and imagined hands/feet subject_id = 1 event_codes = [5, 6, 9, 10, 13, 14] # This will download the files if you don't have them yet, # and then return the paths to the files. physionet_paths = mne.datasets.eegbci.load_data( subject_id, event_codes, update_path=False ) # Load each of the files parts = [ mne.io.read_raw_edf( path, preload=True, stim_channel="auto", verbose="WARNING" ) for path in physionet_paths ] # Concatenate them raw = concatenate_raws(parts) # Find the events in this dataset events, _ = mne.events_from_annotations(raw) # Use only EEG channels eeg_channel_inds = mne.pick_types( raw.info, meg=False, eeg=True, stim=False, eog=False, exclude="bads" ) # Extract trials, only using EEG channels epoched = mne.Epochs( raw, events, dict(hands=2, feet=3), tmin=1, tmax=4.1, proj=False, picks=eeg_channel_inds, baseline=None, preload=True, ) # Convert data from volt to millivolt # Pytorch expects float32 for input and int64 for labels. X = (epoched.get_data() * 1e6).astype(np.float32) y = (epoched.events[:, 2] - 2).astype(np.int64) # 2,3 -> 0,1 # Set if you want to use GPU # You can also use torch.cuda.is_available() to determine if cuda is available on your machine. cuda = False set_random_seeds(seed=20170629, cuda=cuda) # This will determine how many crops are processed in parallel input_time_length = 450 n_classes = 2 in_chans = X.shape[1] # final_conv_length determines the size of the receptive field of the ConvNet model = ShallowFBCSPNet( in_chans=in_chans, n_classes=n_classes, input_time_length=input_time_length, final_conv_length=12, ) to_dense_prediction_model(model) if cuda: model.cuda() # Perform forward pass to determine how many outputs per input n_preds_per_input = get_output_shape(model, in_chans, input_time_length)[2] train_set = CroppedXyDataset(X[:60], y[:60], input_time_length=input_time_length, n_preds_per_input=n_preds_per_input) valid_set = CroppedXyDataset(X[60:], y=y[60:], input_time_length=input_time_length, n_preds_per_input=n_preds_per_input) train_split = predefined_split(valid_set) clf = EEGClassifier( model, cropped=True, criterion=CroppedLoss, criterion__loss_function=torch.nn.functional.nll_loss, optimizer=optim.Adam, train_split=train_split, batch_size=32, callbacks=['accuracy'], ) clf.fit(train_set, y=None, epochs=4) np.testing.assert_allclose( clf.history[:, 'train_loss'], np.array( [ 1.455306, 1.455934, 1.210563, 1.065806 ] ), rtol=1e-4, atol=1e-5, ) np.testing.assert_allclose( clf.history[:, 'valid_loss'], np.array( [ 2.547288, 1.51785, 1.394036, 1.064355 ] ), rtol=1e-4, atol=1e-4, ) np.testing.assert_allclose( clf.history[:, 'train_accuracy'], np.array( [ 0.5, 0.5, 0.5, 0.533333 ] ), rtol=1e-4, atol=1e-5, ) np.testing.assert_allclose( clf.history[:, 'valid_accuracy'], np.array( [ 0.533333, 0.466667, 0.533333, 0.5 ] ), rtol=1e-4, atol=1e-5, )
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) ###################################################################### # Plot Results # ------------ #
name='train_bal_acc', lower_is_better=False) valid_bal_acc = EpochScoring(scoring='balanced_accuracy', on_train=False, name='valid_bal_acc', lower_is_better=False) callbacks = [('train_bal_acc', train_bal_acc), ('valid_bal_acc', valid_bal_acc)] clf = EEGClassifier( model, criterion=torch.nn.CrossEntropyLoss, criterion__weight=torch.Tensor(class_weights).to(device), optimizer=torch.optim.Adam, iterator_train__shuffle=False, iterator_train__sampler=train_sampler, iterator_valid__sampler=valid_sampler, train_split=predefined_split(valid_set), # using valid_set for validation optimizer__lr=lr, batch_size=batch_size, callbacks=callbacks, 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) ###################################################################### # Plot results # ------------ # # We use the history stored by Skorch during training to plot the performance of
def train(subject_id): subject_range = [subject_id] ##### subject_range = [x for x in range(1, 10)] dataset = MOABBDataset(dataset_name="BNCI2014001", subject_ids=subject_range) ###################################################################### # Preprocessing 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, eog=False, 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('set_eeg_reference', ref_channels='average', ch_type='eeg'), Preprocessor('resample', sfreq=125), ## Preprocessor(exponential_moving_standardize, # Exponential moving standardization ## factor_new=factor_new, init_block_size=init_block_size) ## Preprocessor('pick_channels', ch_names=short_ch_names, ordered=True), ] # Transform the data preprocess(dataset, preprocessors) ###################################################################### # Cut Compute Windows # ~~~~~~~~~~~~~~~~~~~ trial_start_offset_seconds = -0.0 # 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, # picks=["Fz", "FC3", "FC1", "FCz", "FC2", "FC4", "C5", "C3", "C1", "Cz", "C2", "C4", "C6", "CP3", "CP1", "CPz", "CP2", "CP4", "P1", "Pz", "P2", "POz"], trial_start_offset_samples=trial_start_offset_samples, trial_stop_offset_samples=0, preload=True, ) ###################################################################### # Split dataset into train and valid splitted = windows_dataset.split('session') train_set = splitted['session_T'] valid_set = splitted['session_E'] ###################################################################### # Create model 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') """ model = EEGNetv1( n_chans, n_classes, input_window_samples=input_window_samples, final_conv_length="auto", pool_mode="mean", second_kernel_size=(2, 32), third_kernel_size=(8, 4), drop_prob=0.25) """ """ model = HybridNet(n_chans, n_classes, input_window_samples=input_window_samples) """ """ model = TCN(n_chans, n_classes, n_blocks=6, n_filters=32, kernel_size=9, drop_prob=0.0, add_log_softmax=True) """ """ model = EEGNetv4(n_chans, n_classes, input_window_samples=input_window_samples, final_conv_length="auto", pool_mode="mean", F1=8, D=2, F2=16, # usually set to F1*D (?) kernel_length=64, third_kernel_size=(8, 4), drop_prob=0.2) """ if cuda: model.cuda() ###################################################################### # Training # These values we found good for shallow network: lr = 0.01 # 0.0625 * 0.01 weight_decay = 0.0005 # For deep4 they should be: # lr = 1 * 0.01 # weight_decay = 0.5 * 0.001 batch_size = 64 n_epochs = 80 clf = EEGClassifier( model, criterion=torch.nn.NLLLoss, optimizer=torch.optim.SGD, #AdamW, train_split=predefined_split( valid_set), # using valid_set for validation optimizer__lr=lr, optimizer__momentum=0.9, 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) results_columns = [ 'train_loss', 'valid_loss', 'train_accuracy', 'valid_accuracy' ] df = pd.DataFrame(clf.history[:, results_columns], columns=results_columns, index=clf.history[:, 'epoch']) val_accs = df['valid_accuracy'].values max_val_acc = 100.0 * np.max(val_accs) return max_val_acc