--- a +++ b/models.py @@ -0,0 +1,645 @@ +""" +Copyright (C) 2022 King Saud University, Saudi Arabia +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +Author: Hamdi Altaheri +""" + +#%% +import tensorflow as tf +from tensorflow.keras.models import Model, Sequential +from tensorflow.keras.layers import Dense, Dropout, Activation, AveragePooling2D, MaxPooling2D +from tensorflow.keras.layers import Conv1D, Conv2D, SeparableConv2D, DepthwiseConv2D +from tensorflow.keras.layers import BatchNormalization, LayerNormalization, Flatten +from tensorflow.keras.layers import Add, Concatenate, Lambda, Input, Permute +from tensorflow.keras.regularizers import L2 +from tensorflow.keras.constraints import max_norm + +from tensorflow.keras import backend as K + +from attention_models import attention_block + +#%% The proposed ATCNet model, https://doi.org/10.1109/TII.2022.3197419 +def ATCNet_(n_classes, in_chans = 22, in_samples = 1125, n_windows = 5, attention = 'mha', + eegn_F1 = 16, eegn_D = 2, eegn_kernelSize = 64, eegn_poolSize = 7, eegn_dropout=0.3, + tcn_depth = 2, tcn_kernelSize = 4, tcn_filters = 32, tcn_dropout = 0.3, + tcn_activation = 'elu', fuse = 'average'): + + """ ATCNet model from Altaheri et al 2023. + See details at https://ieeexplore.ieee.org/abstract/document/9852687 + + Notes + ----- + The initial values in this model are based on the values identified by + the authors + + References + ---------- + .. H. Altaheri, G. Muhammad, and M. Alsulaiman. "Physics-informed + attention temporal convolutional network for EEG-based motor imagery + classification." IEEE Transactions on Industrial Informatics, + vol. 19, no. 2, pp. 2249-2258, (2023) + https://doi.org/10.1109/TII.2022.3197419 + """ + input_1 = Input(shape = (1,in_chans, in_samples)) # TensorShape([None, 1, 22, 1125]) + input_2 = Permute((3,2,1))(input_1) + + dense_weightDecay = 0.5 + conv_weightDecay = 0.009 + conv_maxNorm = 0.6 + from_logits = False + + numFilters = eegn_F1 + F2 = numFilters*eegn_D + + block1 = Conv_block_(input_layer = input_2, F1 = eegn_F1, D = eegn_D, + kernLength = eegn_kernelSize, poolSize = eegn_poolSize, + weightDecay = conv_weightDecay, maxNorm = conv_maxNorm, + in_chans = in_chans, dropout = eegn_dropout) + block1 = Lambda(lambda x: x[:,:,-1,:])(block1) + + # Sliding window + sw_concat = [] # to store concatenated or averaged sliding window outputs + for i in range(n_windows): + st = i + end = block1.shape[1]-n_windows+i+1 + block2 = block1[:, st:end, :] + + # Attention_model + if attention is not None: + if (attention == 'se' or attention == 'cbam'): + block2 = Permute((2, 1))(block2) # shape=(None, 32, 16) + block2 = attention_block(block2, attention) + block2 = Permute((2, 1))(block2) # shape=(None, 16, 32) + else: block2 = attention_block(block2, attention) + + # Temporal convolutional network (TCN) + block3 = TCN_block_(input_layer = block2, input_dimension = F2, depth = tcn_depth, + kernel_size = tcn_kernelSize, filters = tcn_filters, + weightDecay = conv_weightDecay, maxNorm = conv_maxNorm, + dropout = tcn_dropout, activation = tcn_activation) + # Get feature maps of the last sequence + block3 = Lambda(lambda x: x[:,-1,:])(block3) + + # Outputs of sliding window: Average_after_dense or concatenate_then_dense + if(fuse == 'average'): + sw_concat.append(Dense(n_classes, kernel_regularizer=L2(dense_weightDecay))(block3)) + elif(fuse == 'concat'): + if i == 0: + sw_concat = block3 + else: + sw_concat = Concatenate()([sw_concat, block3]) + + if(fuse == 'average'): + if len(sw_concat) > 1: # more than one window + sw_concat = tf.keras.layers.Average()(sw_concat[:]) + else: # one window (# windows = 1) + sw_concat = sw_concat[0] + elif(fuse == 'concat'): + sw_concat = Dense(n_classes, kernel_regularizer=L2(dense_weightDecay))(sw_concat) + + if from_logits: # No activation here because we are using from_logits=True + out = Activation('linear', name = 'linear')(sw_concat) + else: # Using softmax activation + out = Activation('softmax', name = 'softmax')(sw_concat) + + return Model(inputs = input_1, outputs = out) + +#%% Convolutional (CV) block used in the ATCNet model +def Conv_block(input_layer, F1=4, kernLength=64, poolSize=8, D=2, in_chans=22, dropout=0.1): + """ Conv_block + + Notes + ----- + This block is the same as EEGNet with SeparableConv2D replaced by Conv2D + The original code for this model is available at: https://github.com/vlawhern/arl-eegmodels + See details at https://arxiv.org/abs/1611.08024 + """ + F2= F1*D + block1 = Conv2D(F1, (kernLength, 1), padding = 'same',data_format='channels_last',use_bias = False)(input_layer) + block1 = BatchNormalization(axis = -1)(block1) + block2 = DepthwiseConv2D((1, in_chans), use_bias = False, + depth_multiplier = D, + data_format='channels_last', + depthwise_constraint = max_norm(1.))(block1) + block2 = BatchNormalization(axis = -1)(block2) + block2 = Activation('elu')(block2) + block2 = AveragePooling2D((8,1),data_format='channels_last')(block2) + block2 = Dropout(dropout)(block2) + block3 = Conv2D(F2, (16, 1), + data_format='channels_last', + use_bias = False, padding = 'same')(block2) + block3 = BatchNormalization(axis = -1)(block3) + block3 = Activation('elu')(block3) + + block3 = AveragePooling2D((poolSize,1),data_format='channels_last')(block3) + block3 = Dropout(dropout)(block3) + return block3 + +def Conv_block_(input_layer, F1=4, kernLength=64, poolSize=8, D=2, in_chans=22, + weightDecay = 0.009, maxNorm = 0.6, dropout=0.25): + """ Conv_block + + Notes + ----- + using different regularization methods. + """ + + F2= F1*D + block1 = Conv2D(F1, (kernLength, 1), padding = 'same', data_format='channels_last', + kernel_regularizer=L2(weightDecay), + + # In a Conv2D layer with data_format="channels_last", the weight tensor has shape + # (rows, cols, input_depth, output_depth), set axis to [0, 1, 2] to constrain + # the weights of each filter tensor of size (rows, cols, input_depth). + kernel_constraint = max_norm(maxNorm, axis=[0,1,2]), + use_bias = False)(input_layer) + block1 = BatchNormalization(axis = -1)(block1) # bn_axis = -1 if data_format() == 'channels_last' else 1 + + block2 = DepthwiseConv2D((1, in_chans), + depth_multiplier = D, + data_format='channels_last', + depthwise_regularizer=L2(weightDecay), + depthwise_constraint = max_norm(maxNorm, axis=[0,1,2]), + use_bias = False)(block1) + block2 = BatchNormalization(axis = -1)(block2) + block2 = Activation('elu')(block2) + block2 = AveragePooling2D((8,1),data_format='channels_last')(block2) + block2 = Dropout(dropout)(block2) + + block3 = Conv2D(F2, (16, 1), + data_format='channels_last', + kernel_regularizer=L2(weightDecay), + kernel_constraint = max_norm(maxNorm, axis=[0,1,2]), + use_bias = False, padding = 'same')(block2) + block3 = BatchNormalization(axis = -1)(block3) + block3 = Activation('elu')(block3) + + block3 = AveragePooling2D((poolSize,1),data_format='channels_last')(block3) + block3 = Dropout(dropout)(block3) + return block3 + +#%% Temporal convolutional (TC) block used in the ATCNet model +def TCN_block(input_layer,input_dimension,depth,kernel_size,filters,dropout,activation='relu'): + """ TCN_block from Bai et al 2018 + Temporal Convolutional Network (TCN) + + Notes + ----- + THe original code available at https://github.com/locuslab/TCN/blob/master/TCN/tcn.py + This implementation has a slight modification from the original code + and it is taken from the code by Ingolfsson et al at https://github.com/iis-eth-zurich/eeg-tcnet + See details at https://arxiv.org/abs/2006.00622 + + References + ---------- + .. Bai, S., Kolter, J. Z., & Koltun, V. (2018). + An empirical evaluation of generic convolutional and recurrent networks + for sequence modeling. + arXiv preprint arXiv:1803.01271. + """ + + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=1,activation='linear', + padding = 'causal',kernel_initializer='he_uniform')(input_layer) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=1,activation='linear', + padding = 'causal',kernel_initializer='he_uniform')(block) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + if(input_dimension != filters): + conv = Conv1D(filters,kernel_size=1,padding='same')(input_layer) + added = Add()([block,conv]) + else: + added = Add()([block,input_layer]) + out = Activation(activation)(added) + + for i in range(depth-1): + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=2**(i+1),activation='linear', + padding = 'causal',kernel_initializer='he_uniform')(out) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=2**(i+1),activation='linear', + padding = 'causal',kernel_initializer='he_uniform')(block) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + added = Add()([block, out]) + out = Activation(activation)(added) + + return out + +def TCN_block_(input_layer,input_dimension,depth,kernel_size,filters, dropout, + weightDecay = 0.009, maxNorm = 0.6, activation='relu'): + """ TCN_block from Bai et al 2018 + Temporal Convolutional Network (TCN) + + Notes + ----- + using different regularization methods + """ + + block = Conv1D(filters, kernel_size=kernel_size, dilation_rate=1, activation='linear', + kernel_regularizer=L2(weightDecay), + kernel_constraint = max_norm(maxNorm, axis=[0,1]), + + padding = 'causal',kernel_initializer='he_uniform')(input_layer) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=1,activation='linear', + kernel_regularizer=L2(weightDecay), + kernel_constraint = max_norm(maxNorm, axis=[0,1]), + + padding = 'causal',kernel_initializer='he_uniform')(block) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + if(input_dimension != filters): + conv = Conv1D(filters,kernel_size=1, + kernel_regularizer=L2(weightDecay), + kernel_constraint = max_norm(maxNorm, axis=[0,1]), + + padding='same')(input_layer) + added = Add()([block,conv]) + else: + added = Add()([block,input_layer]) + out = Activation(activation)(added) + + for i in range(depth-1): + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=2**(i+1),activation='linear', + kernel_regularizer=L2(weightDecay), + kernel_constraint = max_norm(maxNorm, axis=[0,1]), + + padding = 'causal',kernel_initializer='he_uniform')(out) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + block = Conv1D(filters,kernel_size=kernel_size,dilation_rate=2**(i+1),activation='linear', + kernel_regularizer=L2(weightDecay), + kernel_constraint = max_norm(maxNorm, axis=[0,1]), + + padding = 'causal',kernel_initializer='he_uniform')(block) + block = BatchNormalization()(block) + block = Activation(activation)(block) + block = Dropout(dropout)(block) + added = Add()([block, out]) + out = Activation(activation)(added) + + return out + + +#%% Reproduced TCNet_Fusion model: https://doi.org/10.1016/j.bspc.2021.102826 +def TCNet_Fusion(n_classes, Chans=22, Samples=1125, layers=2, kernel_s=4, filt=12, + dropout=0.3, activation='elu', F1=24, D=2, kernLength=32, dropout_eeg=0.3): + """ TCNet_Fusion model from Musallam et al 2021. + See details at https://doi.org/10.1016/j.bspc.2021.102826 + + Notes + ----- + The initial values in this model are based on the values identified by + the authors + + References + ---------- + .. Musallam, Y.K., AlFassam, N.I., Muhammad, G., Amin, S.U., Alsulaiman, + M., Abdul, W., Altaheri, H., Bencherif, M.A. and Algabri, M., 2021. + Electroencephalography-based motor imagery classification + using temporal convolutional network fusion. + Biomedical Signal Processing and Control, 69, p.102826. + """ + input1 = Input(shape = (1,Chans, Samples)) + input2 = Permute((3,2,1))(input1) + regRate=.25 + + numFilters = F1 + F2= numFilters*D + + EEGNet_sep = EEGNet(input_layer=input2,F1=F1,kernLength=kernLength,D=D,Chans=Chans,dropout=dropout_eeg) + block2 = Lambda(lambda x: x[:,:,-1,:])(EEGNet_sep) + FC = Flatten()(block2) + + outs = TCN_block(input_layer=block2,input_dimension=F2,depth=layers,kernel_size=kernel_s,filters=filt,dropout=dropout,activation=activation) + + Con1 = Concatenate()([block2,outs]) + out = Flatten()(Con1) + Con2 = Concatenate()([out,FC]) + dense = Dense(n_classes, name = 'dense',kernel_constraint = max_norm(regRate))(Con2) + softmax = Activation('softmax', name = 'softmax')(dense) + + return Model(inputs=input1,outputs=softmax) + + +#%% Reproduced EEGTCNet model: https://arxiv.org/abs/2006.00622 +def EEGTCNet(n_classes, Chans=22, Samples=1125, layers=2, kernel_s=4, filt=12, dropout=0.3, activation='elu', F1=8, D=2, kernLength=32, dropout_eeg=0.2): + """ EEGTCNet model from Ingolfsson et al 2020. + See details at https://arxiv.org/abs/2006.00622 + + The original code for this model is available at https://github.com/iis-eth-zurich/eeg-tcnet + + Notes + ----- + The initial values in this model are based on the values identified by the authors + + References + ---------- + .. Ingolfsson, T. M., Hersche, M., Wang, X., Kobayashi, N., + Cavigelli, L., & Benini, L. (2020, October). + Eeg-tcnet: An accurate temporal convolutional network + for embedded motor-imagery brain–machine interfaces. + In 2020 IEEE International Conference on Systems, + Man, and Cybernetics (SMC) (pp. 2958-2965). IEEE. + """ + input1 = Input(shape = (1,Chans, Samples)) + input2 = Permute((3,2,1))(input1) + regRate=.25 + numFilters = F1 + F2= numFilters*D + + EEGNet_sep = EEGNet(input_layer=input2,F1=F1,kernLength=kernLength,D=D,Chans=Chans,dropout=dropout_eeg) + block2 = Lambda(lambda x: x[:,:,-1,:])(EEGNet_sep) + outs = TCN_block(input_layer=block2,input_dimension=F2,depth=layers,kernel_size=kernel_s,filters=filt,dropout=dropout,activation=activation) + out = Lambda(lambda x: x[:,-1,:])(outs) + dense = Dense(n_classes, name = 'dense',kernel_constraint = max_norm(regRate))(out) + softmax = Activation('softmax', name = 'softmax')(dense) + + return Model(inputs=input1,outputs=softmax) + +#%% Reproduced MBEEG_SENet model: https://doi.org/10.3390/diagnostics12040995 +def MBEEG_SENet(nb_classes, Chans, Samples, D=2): + """ MBEEG_SENet model from Altuwaijri et al 2022. + See details at https://doi.org/10.3390/diagnostics12040995 + + Notes + ----- + The initial values in this model are based on the values identified by + the authors + + References + ---------- + .. G. Altuwaijri, G. Muhammad, H. Altaheri, & M. Alsulaiman. + A Multi-Branch Convolutional Neural Network with Squeeze-and-Excitation + Attention Blocks for EEG-Based Motor Imagery Signals Classification. + Diagnostics, 12(4), 995, (2022). + https://doi.org/10.3390/diagnostics12040995 + """ + + input1 = Input(shape = (1,Chans, Samples)) + input2 = Permute((3,2,1))(input1) + regRate=.25 + + EEGNet_sep1 = EEGNet(input_layer=input2, F1=4, kernLength=16, D=D, Chans=Chans, dropout=0) + EEGNet_sep2 = EEGNet(input_layer=input2, F1=8, kernLength=32, D=D, Chans=Chans, dropout=0.1) + EEGNet_sep3 = EEGNet(input_layer=input2, F1=16, kernLength=64, D=D, Chans=Chans, dropout=0.2) + + SE1 = attention_block(EEGNet_sep1, 'se', ratio=4) + SE2 = attention_block(EEGNet_sep2, 'se', ratio=4) + SE3 = attention_block(EEGNet_sep3, 'se', ratio=2) + + + FC1 = Flatten()(SE1) + FC2 = Flatten()(SE2) + FC3 = Flatten()(SE3) + + CON = Concatenate()([FC1,FC2,FC3]) + + dense1 = Dense(nb_classes, name = 'dense1',kernel_constraint = max_norm(regRate))(CON) + softmax = Activation('softmax', name = 'softmax')(dense1) + + return Model(inputs=input1,outputs=softmax) + + + +#%% Reproduced EEGNeX model: https://arxiv.org/abs/2207.12369 +def EEGNeX_8_32(n_timesteps, n_features, n_outputs): + """ EEGNeX model from Chen et al 2022. + See details at https://arxiv.org/abs/2207.12369 + + The original code for this model is available at https://github.com/chenxiachan/EEGNeX + + References + ---------- + .. Chen, X., Teng, X., Chen, H., Pan, Y., & Geyer, P. (2022). + Toward reliable signals decoding for electroencephalogram: + A benchmark study to EEGNeX. arXiv preprint arXiv:2207.12369. + """ + + model = Sequential() + model.add(Input(shape=(1, n_features, n_timesteps))) + + model.add(Conv2D(filters=8, kernel_size=(1, 32), use_bias = False, padding='same', data_format="channels_first")) + model.add(LayerNormalization()) + model.add(Activation(activation='elu')) + model.add(Conv2D(filters=32, kernel_size=(1, 32), use_bias = False, padding='same', data_format="channels_first")) + model.add(LayerNormalization()) + model.add(Activation(activation='elu')) + + model.add(DepthwiseConv2D(kernel_size=(n_features, 1), depth_multiplier=2, use_bias = False, depthwise_constraint=max_norm(1.), data_format="channels_first")) + model.add(LayerNormalization()) + model.add(Activation(activation='elu')) + model.add(AveragePooling2D(pool_size=(1, 4), padding='same', data_format="channels_first")) + model.add(Dropout(0.5)) + + + model.add(Conv2D(filters=32, kernel_size=(1, 16), use_bias = False, padding='same', dilation_rate=(1, 2), data_format='channels_first')) + model.add(LayerNormalization()) + model.add(Activation(activation='elu')) + + model.add(Conv2D(filters=8, kernel_size=(1, 16), use_bias = False, padding='same', dilation_rate=(1, 4), data_format='channels_first')) + model.add(LayerNormalization()) + model.add(Activation(activation='elu')) + model.add(Dropout(0.5)) + + model.add(Flatten()) + model.add(Dense(n_outputs, kernel_constraint=max_norm(0.25))) + model.add(Activation(activation='softmax')) + + # save a plot of the model + # plot_model(model, show_shapes=True, to_file='EEGNeX_8_32.png') + model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) + return model + +#%% Reproduced EEGNet model: https://arxiv.org/abs/1611.08024 +def EEGNet_classifier(n_classes, Chans=22, Samples=1125, F1=8, D=2, kernLength=64, dropout_eeg=0.25): + input1 = Input(shape = (1,Chans, Samples)) + input2 = Permute((3,2,1))(input1) + regRate=.25 + + eegnet = EEGNet(input_layer=input2, F1=F1, kernLength=kernLength, D=D, Chans=Chans, dropout=dropout_eeg) + eegnet = Flatten()(eegnet) + dense = Dense(n_classes, name = 'dense',kernel_constraint = max_norm(regRate))(eegnet) + softmax = Activation('softmax', name = 'softmax')(dense) + + return Model(inputs=input1, outputs=softmax) + +def EEGNet(input_layer, F1=8, kernLength=64, D=2, Chans=22, dropout=0.25): + """ EEGNet model from Lawhern et al 2018 + See details at https://arxiv.org/abs/1611.08024 + + The original code for this model is available at: https://github.com/vlawhern/arl-eegmodels + + Notes + ----- + The initial values in this model are based on the values identified by the authors + + References + ---------- + .. Lawhern, V. J., Solon, A. J., Waytowich, N. R., Gordon, + S. M., Hung, C. P., & Lance, B. J. (2018). + EEGNet: A Compact Convolutional Network for EEG-based + Brain-Computer Interfaces. + arXiv preprint arXiv:1611.08024. + """ + F2= F1*D + block1 = Conv2D(F1, (kernLength, 1), padding = 'same',data_format='channels_last',use_bias = False)(input_layer) + block1 = BatchNormalization(axis = -1)(block1) + block2 = DepthwiseConv2D((1, Chans), use_bias = False, + depth_multiplier = D, + data_format='channels_last', + depthwise_constraint = max_norm(1.))(block1) + block2 = BatchNormalization(axis = -1)(block2) + block2 = Activation('elu')(block2) + block2 = AveragePooling2D((8,1),data_format='channels_last')(block2) + block2 = Dropout(dropout)(block2) + block3 = SeparableConv2D(F2, (16, 1), + data_format='channels_last', + use_bias = False, padding = 'same')(block2) + block3 = BatchNormalization(axis = -1)(block3) + block3 = Activation('elu')(block3) + block3 = AveragePooling2D((8,1),data_format='channels_last')(block3) + block3 = Dropout(dropout)(block3) + return block3 + + +#%% Reproduced DeepConvNet model: https://doi.org/10.1002/hbm.23730 +def DeepConvNet(nb_classes, Chans = 64, Samples = 256, + dropoutRate = 0.5): + """ Keras implementation of the Deep Convolutional Network as described in + Schirrmeister et. al. (2017), Human Brain Mapping. + See details at https://onlinelibrary.wiley.com/doi/full/10.1002/hbm.23730 + + The original code for this model is available at: https://github.com/braindecode/braindecode + + Notes + ----- + The initial values in this model are based on the values identified by the authors + + This implementation is taken from code by the Army Research Laboratory (ARL) + at https://github.com/vlawhern/arl-eegmodels + + References + ---------- + .. Schirrmeister, R. T., Springenberg, J. T., Fiederer, L. D. J., + Glasstetter, M., Eggensperger, K., Tangermann, M., ... & Ball, T. (2017). + Deep learning with convolutional neural networks for EEG decoding + and visualization. Human brain mapping, 38(11), 5391-5420. + + """ + + # start the model + # input_main = Input((Chans, Samples, 1)) + input_main = Input((1, Chans, Samples)) + input_2 = Permute((2,3,1))(input_main) + + block1 = Conv2D(25, (1, 10), + input_shape=(Chans, Samples, 1), + kernel_constraint = max_norm(2., axis=(0,1,2)))(input_2) + block1 = Conv2D(25, (Chans, 1), + kernel_constraint = max_norm(2., axis=(0,1,2)))(block1) + block1 = BatchNormalization(epsilon=1e-05, momentum=0.9)(block1) + block1 = Activation('elu')(block1) + block1 = MaxPooling2D(pool_size=(1, 3), strides=(1, 3))(block1) + block1 = Dropout(dropoutRate)(block1) + + block2 = Conv2D(50, (1, 10), + kernel_constraint = max_norm(2., axis=(0,1,2)))(block1) + block2 = BatchNormalization(epsilon=1e-05, momentum=0.9)(block2) + block2 = Activation('elu')(block2) + block1 = MaxPooling2D(pool_size=(1, 3), strides=(1, 3))(block1) + block2 = Dropout(dropoutRate)(block2) + + block3 = Conv2D(100, (1, 10), + kernel_constraint = max_norm(2., axis=(0,1,2)))(block2) + block3 = BatchNormalization(epsilon=1e-05, momentum=0.9)(block3) + block3 = Activation('elu')(block3) + block1 = MaxPooling2D(pool_size=(1, 3), strides=(1, 3))(block1) + block3 = Dropout(dropoutRate)(block3) + + block4 = Conv2D(200, (1, 10), + kernel_constraint = max_norm(2., axis=(0,1,2)))(block3) + block4 = BatchNormalization(epsilon=1e-05, momentum=0.9)(block4) + block4 = Activation('elu')(block4) + block1 = MaxPooling2D(pool_size=(1, 3), strides=(1, 3))(block1) + block4 = Dropout(dropoutRate)(block4) + + flatten = Flatten()(block4) + + dense = Dense(nb_classes, kernel_constraint = max_norm(0.5))(flatten) + softmax = Activation('softmax')(dense) + + return Model(inputs=input_main, outputs=softmax) + +#%% need these for ShallowConvNet +def square(x): + return K.square(x) + +def log(x): + return K.log(K.clip(x, min_value = 1e-7, max_value = 10000)) + +#%% Reproduced ShallowConvNet model: https://doi.org/10.1002/hbm.23730 +def ShallowConvNet(nb_classes, Chans = 64, Samples = 128, dropoutRate = 0.5): + """ Keras implementation of the Shallow Convolutional Network as described + in Schirrmeister et. al. (2017), Human Brain Mapping. + See details at https://onlinelibrary.wiley.com/doi/full/10.1002/hbm.23730 + + The original code for this model is available at: https://github.com/braindecode/braindecode + + Notes + ----- + The initial values in this model are based on the values identified by the authors + + This implementation is taken from code by the Army Research Laboratory (ARL) + at https://github.com/vlawhern/arl-eegmodels + + References + ---------- + .. Schirrmeister, R. T., Springenberg, J. T., Fiederer, L. D. J., + Glasstetter, M., Eggensperger, K., Tangermann, M., ... & Ball, T. (2017). + Deep learning with convolutional neural networks for EEG decoding + and visualization. Human brain mapping, 38(11), 5391-5420. + + """ + # start the model + # input_main = Input((Chans, Samples, 1)) + input_main = Input((1, Chans, Samples)) + input_2 = Permute((2,3,1))(input_main) + + block1 = Conv2D(40, (1, 25), + input_shape=(Chans, Samples, 1), + kernel_constraint = max_norm(2., axis=(0,1,2)))(input_2) + block1 = Conv2D(40, (Chans, 1), use_bias=False, + kernel_constraint = max_norm(2., axis=(0,1,2)))(block1) + block1 = BatchNormalization(epsilon=1e-05, momentum=0.9)(block1) + block1 = Activation(square)(block1) + block1 = AveragePooling2D(pool_size=(1, 75), strides=(1, 15))(block1) + block1 = Activation(log)(block1) + block1 = Dropout(dropoutRate)(block1) + flatten = Flatten()(block1) + dense = Dense(nb_classes, kernel_constraint = max_norm(0.5))(flatten) + softmax = Activation('softmax')(dense) + + return Model(inputs=input_main, outputs=softmax) \ No newline at end of file