├── Baseline_Models.m ├── Concat_ANN.py ├── HDC_ANN.py ├── HDC_SNN.py ├── LICENSE ├── LSTM.py ├── README.md ├── config.py ├── create_figs_and_tables.m ├── create_train_test_split_MATLAB.py ├── data ├── README.md ├── motorway_dataset_window_64_proc_veh_DtA.pkl ├── secondary_dataset_window_64_proc_veh_DtA.pkl └── uah_dataset.mat ├── eval_baseline_models.m ├── getF1Score.m ├── main.py ├── model.py ├── requirements.txt └── utils.py /Baseline_Models.m: -------------------------------------------------------------------------------- 1 | %% simple baseline predicition models 2 | % scken, 2021 3 | % Copyright (C) 2021 Chair of Automation Technology / TU Chemnitz 4 | 5 | 6 | % parameter setup 7 | dim = 576; 8 | frac_scale = 6; 9 | 10 | disp('----------------------------') 11 | disp(['Baseline ' dataset]) 12 | disp(['Dim: ' num2str(dim)]) 13 | 14 | 15 | %% if dataset = full_crossval, create the training and test splits 16 | 17 | if contains(dataset,'full_crossval') 18 | %%% HDC encoding with kNN classifier 19 | 20 | % load the data with the python script 21 | ret = system(['python3 create_train_test_split_MATLAB.py --dataset=' dataset ' --preproc=1 --input_dim=' num2str(dim) ' --scale=' num2str(frac_scale)]); 22 | if ret==0 23 | load('temp_data.mat') 24 | else 25 | disp('Data could not converted') 26 | return 27 | end 28 | delete('temp_data.mat') 29 | 30 | for i=1:size(X_train,2) 31 | %%% 32 | % HDC with k-NN 33 | 34 | % load data into item memory 35 | VSA = vsa_env('vsa','FHRR','dim',dim); 36 | VSA.add_vector('vec',X_train{i}','name',num2cell(num2str(Y_train{i}))); 37 | 38 | % find k nearest neigbors 39 | tic 40 | [~, l, s] = VSA.find_k_nearest(X_test{i}',3); 41 | pred = []; 42 | 43 | for c=1:size(l,2) 44 | temp = str2num(cell2mat(l(:,c))); 45 | pred(end+1) = mode(temp); 46 | end 47 | disp('Time for testing k-NN:') 48 | toc 49 | disp('Accuracy of HDC k-NN method: ') 50 | f1 = getF1Score(Y_test{i},pred); 51 | disp(f1) 52 | 53 | end 54 | 55 | %%% 56 | % spectral features (FFT) with kNN 57 | 58 | ret = system(['python3 create_train_test_split_MATLAB.py --dataset=' dataset ' --preproc=0 --input_dim=' num2str(dim) ' --scale=' num2str(frac_scale)]); 59 | 60 | if ret==0 61 | load('temp_data.mat') 62 | else 63 | disp('Data could not converted') 64 | return 65 | end 66 | delete('temp_data.mat') 67 | 68 | for i=1:size(X_train,2) 69 | % fourier transformation 70 | X_train{i} = abs(fft(X_train{i},size(X_train{i},2),2)); 71 | X_test{i} = abs(fft(X_test{i},size(X_test{i},2),2)); 72 | 73 | % concat input 74 | X_train{i} = reshape(X_train{i},size(X_train{i},1),[]); 75 | X_test{i} = reshape(X_test{i},size(X_test{i},1),[]); 76 | 77 | Mdl = fitcknn(X_train{i},Y_train{i},'NumNeighbors',1,'Distance','Cityblock'); 78 | 79 | % testing 80 | pred = predict(Mdl, X_test{i}); 81 | 82 | disp('Accuracy of Spectral Features kNN method: ') 83 | f1 = getF1Score(Y_test{i},pred); 84 | disp(f1) 85 | 86 | end 87 | end 88 | 89 | %% HDC with SVM 90 | 91 | ret = system(['python3 create_train_test_split_MATLAB.py --dataset=' dataset ' --preproc=1 --input_dim=' num2str(dim) ' --scale=' num2str(frac_scale)]); 92 | 93 | if ret==0 94 | load('temp_data.mat') 95 | else 96 | disp('Data could not converted') 97 | return 98 | end 99 | delete('temp_data.mat') 100 | 101 | tic 102 | Mdl = fitcecoc(X_train,Y_train); 103 | disp('Time for training HDC-SVM:') 104 | toc 105 | 106 | % testing 107 | tic 108 | pred = predict(Mdl, X_test); 109 | disp('Time for testing HDC-SVM:') 110 | toc 111 | f1 = getF1Score(Y_test,pred); 112 | disp('Accuracy of HDC SVM:') 113 | disp(f1) 114 | 115 | % add result to table 116 | Result = table({'HDC-SVM'},f1,'VariableNames',{'Model','F1'}); 117 | 118 | %% HDC with k-NN 119 | 120 | % load data into item memory 121 | VSA = vsa_env('vsa','FHRR','dim',dim); 122 | VSA.add_vector('vec',X_train','name',num2cell(num2str(Y_train))); 123 | 124 | % find k nearest neigbors 125 | tic 126 | [~, l, s] = VSA.find_k_nearest(X_test',3); 127 | pred = []; 128 | 129 | for c=1:size(l,2) 130 | temp = str2num(cell2mat(l(:,c))); 131 | pred(end+1) = mode(temp); 132 | end 133 | disp('Time for testing k-NN:') 134 | toc 135 | disp('Accuracy of HDC k-NN method: ') 136 | f1 = getF1Score(Y_test,pred); 137 | disp(f1) 138 | 139 | % add to table 140 | % Result = table({'HDC-kNN'},acc,'VariableNames',{'Model','F1'}); 141 | Result.Model{end+1} = 'HDC-kNN'; 142 | Result.F1(end) = f1; 143 | 144 | %% concat with SVM 145 | 146 | ret = system(['python3 create_train_test_split_MATLAB.py --dataset=' dataset ' --preproc=0 --input_dim=' num2str(dim) ' --scale=' num2str(frac_scale)]); 147 | 148 | if ret==0 149 | load('temp_data.mat') 150 | else 151 | disp('Data could not converted') 152 | return 153 | end 154 | delete('temp_data.mat') 155 | 156 | % concat input 157 | X_train = reshape(X_train,size(X_train,1),[]); 158 | X_test = reshape(X_test,size(X_test,1),[]); 159 | 160 | 161 | Mdl = fitcecoc(X_train,Y_train); 162 | 163 | % testing 164 | pred = predict(Mdl, X_test); 165 | f1 = getF1Score(Y_test,pred); 166 | disp('Accuracy of Concat SVM method: ') 167 | disp(f1) 168 | 169 | % add to table 170 | Result.Model{end+1} = 'Concat-SVM'; 171 | Result.F1(end) = f1; 172 | 173 | 174 | %% concat with kNN 175 | 176 | % find optimal hyperparameter for concat model 177 | % rng(0) 178 | % Mdl_opt = fitcknn([X_train; X_test],[Y_train; Y_test],'OptimizeHyperparameters','auto',... 179 | % 'HyperparameterOptimizationOptions',... 180 | % struct('AcquisitionFunctionName','expected-improvement-plus')) 181 | % 182 | % Mdl = fitcknn(X_train,Y_train,'NumNeighbors',Mdl_opt.NumNeighbors,'Distance',Mdl_opt.Distance); 183 | 184 | Mdl = fitcknn(X_train,Y_train,'NumNeighbors',3,'Distance','Cityblock'); 185 | 186 | % testing 187 | pred = predict(Mdl, X_test); 188 | f1 = getF1Score(Y_test,pred); 189 | disp('Accuracy of Concat k-NN method: ') 190 | disp(f1) 191 | 192 | % add to table 193 | Result.Model{end+1} = 'Concat-kNN'; 194 | Result.F1(end) = f1; 195 | 196 | %% spectral features (FFT) with SVM 197 | 198 | ret = system(['python3 create_train_test_split_MATLAB.py --dataset=' dataset ' --preproc=0 --input_dim=' num2str(dim) ' --scale=' num2str(frac_scale)]); 199 | 200 | if ret==0 201 | load('temp_data.mat') 202 | else 203 | disp('Data could not converted') 204 | return 205 | end 206 | delete('temp_data.mat') 207 | 208 | % fourier transformation 209 | X_train = abs(fft(X_train,size(X_train,2),2)); 210 | X_test = abs(fft(X_test,size(X_test,2),2)); 211 | 212 | % concat input 213 | X_train = reshape(X_train,size(X_train,1),[]); 214 | X_test = reshape(X_test,size(X_test,1),[]); 215 | 216 | tic 217 | % Mdl = fitcecoc(X_train,Y_train,'Learners',svm_template); 218 | Mdl = fitcecoc(X_train,Y_train); 219 | disp('Time for training SVM-Stat:') 220 | toc 221 | 222 | % testing 223 | tic 224 | pred = predict(Mdl, X_test); 225 | disp('Time for testing SVM-Stat:') 226 | toc 227 | 228 | disp('Accuracy of Spectral Features SVM method: ') 229 | f1 = getF1Score(Y_test,pred); 230 | disp(f1) 231 | 232 | % add to table 233 | Result.Model{end+1} = 'Spect-SVM'; 234 | Result.F1(end) = f1; 235 | 236 | %%% 237 | % spectral features (FFT) with kNN 238 | 239 | ret = system(['python3 create_train_test_split_MATLAB.py --dataset=' dataset ' --preproc=0 --input_dim=' num2str(dim) ' --scale=' num2str(frac_scale)]); 240 | 241 | if ret==0 242 | load('temp_data.mat') 243 | else 244 | disp('Data could not converted') 245 | return 246 | end 247 | delete('temp_data.mat') 248 | 249 | % fourier transformation 250 | X_train = abs(fft(X_train,size(X_train,2),2)); 251 | X_test = abs(fft(X_test,size(X_test,2),2)); 252 | 253 | % concat input 254 | X_train = reshape(X_train,size(X_train,1),[]); 255 | X_test = reshape(X_test,size(X_test,1),[]); 256 | 257 | 258 | % find optimal hyperparameter for concat model 259 | % rng(0) 260 | % Mdl_opt = fitcknn([X_train; X_test],[Y_train; Y_test],'OptimizeHyperparameters','auto',... 261 | % 'HyperparameterOptimizationOptions',... 262 | % struct('AcquisitionFunctionName','expected-improvement-plus')) 263 | % 264 | % Mdl = fitcknn(X_train,Y_train,'NumNeighbors',Mdl_opt.NumNeighbors,'Distance',Mdl_opt.Distance); 265 | 266 | Mdl = fitcknn(X_train,Y_train,'NumNeighbors',1,'Distance','Cityblock'); 267 | 268 | % testing 269 | pred = predict(Mdl, X_test); 270 | 271 | disp('Accuracy of Spectral Features kNN method: ') 272 | f1 = getF1Score(Y_test,pred); 273 | disp(f1) 274 | 275 | % add to table 276 | Result.Model{end+1} = 'Spect-kNN'; 277 | Result.F1(end) = f1; 278 | 279 | %% print results 280 | 281 | disp([dataset ' Dataset:']) 282 | disp(Result) 283 | 284 | -------------------------------------------------------------------------------- /Concat_ANN.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from config import Config 6 | from utils import * 7 | from model import HDC_ANN 8 | from sklearn.metrics import classification_report, f1_score 9 | from scipy.io import savemat, loadmat 10 | from sklearn import metrics 11 | from matplotlib import pyplot as plt 12 | from tensorflow.keras.utils import to_categorical 13 | from tensorflow.keras.callbacks import ModelCheckpoint 14 | import logging 15 | 16 | # config logger 17 | logger = logging.getLogger('log') 18 | 19 | def main_Concat_ANN(args): 20 | ''' 21 | implementation of a network that uses the concatenate sequences of al variables 22 | - input size of the network is 64*9 (64 timesteps and 9 sensors) = 576 23 | - network is the same as in HDC approach 24 | ''' 25 | config = Config() 26 | config.training_volume = args.training_volume 27 | config.input_dim = args.input_dim 28 | config.encoding_dim = args.encoding_dim 29 | if args.runtime_measurement: 30 | config.n_time_measures = 10 31 | else: 32 | config.n_time_measures = 1 33 | 34 | # load preprocessed data 35 | data = load_dataset(args.dataset,config) 36 | X_train = data[0] 37 | X_test = data[1] 38 | y_train = data[2] 39 | y_test = data[3] 40 | config = data[4] 41 | 42 | # if train test data not a list, create one 43 | if type(X_train)==list: 44 | print("given data is not a list") 45 | X_train_list = X_train 46 | X_test_list = X_test 47 | y_train_list = y_train 48 | y_test_list = y_test 49 | else: 50 | X_train_list =[X_train] 51 | X_test_list = [X_test] 52 | y_train_list = [y_train] 53 | y_test_list = [y_test] 54 | 55 | ####################################################################################### 56 | # statistical iteration 57 | ####################################################################################### 58 | acc_mean = [] 59 | f1_mean = [] 60 | 61 | for stat_it in range(args.stat_iterations): 62 | logger.info('Statistial iteration: ' + str(stat_it)) 63 | 64 | # train for each element in list (that is why we need list form, even if it contains only one element) 65 | logger.info('Training data contains ' + str(len(X_train)) + ' training instances...') 66 | scores = [] 67 | accs = [] 68 | for it in range(len(X_train_list)): 69 | logger.info(('.......')) 70 | logger.info('instance ' + str(it) + ':') 71 | 72 | X_train = X_train_list[it] 73 | X_test = X_test_list[it] 74 | y_train = y_train_list[it] 75 | y_test = y_test_list[it] 76 | 77 | # use only fraction of training samples (if given) 78 | X_train = X_train[1:int(X_train.shape[0] * config.training_volume), :] 79 | y_train = y_train[1:int(y_train.shape[0] * config.training_volume), :] 80 | 81 | # concatenate the input data 82 | X_train = np.reshape(X_train, (X_train.shape[0], -1)) 83 | X_test = np.reshape(X_test, (X_test.shape[0], -1)) 84 | 85 | config.input_dim = X_train.shape[1] 86 | 87 | logger.info('Training dataset shape: ' + str(X_train.shape) + str(y_train.shape)) 88 | logger.info('Test dataset shape: ' + str(X_test.shape) + str(y_test.shape)) 89 | 90 | config.n_classes = len(np.unique(y_train)) 91 | 92 | ####################################################################################### 93 | # keras model training 94 | ####################################################################################### 95 | 96 | model = HDC_ANN(config) 97 | model.summary() 98 | 99 | cb_time = TimingCallback() 100 | weight_fn = "./weights/Concat_ANN/%s_weights.h5" % args.dataset 101 | if not os.path.exists(weight_fn.rsplit('/', 1)[0]): 102 | os.makedirs(weight_fn.rsplit('/', 1)[0]) 103 | model_checkpoint = ModelCheckpoint(weight_fn, verbose=1, mode='auto', 104 | monitor='loss', save_best_only=True, save_weights_only=True) 105 | 106 | # compile model 107 | model.compile(optimizer='adam', 108 | loss='categorical_crossentropy', 109 | metrics=['accuracy']) 110 | history = model.fit(X_train, to_categorical(y_train), 111 | epochs=config.training_epochs, 112 | batch_size=config.batch_size, 113 | shuffle=True, 114 | callbacks=[cb_time, model_checkpoint], 115 | validation_data=(X_test, to_categorical(y_test))) 116 | 117 | # log training time 118 | epoch_time = cb_time.logs 119 | mean_epoch_time = np.mean(epoch_time) 120 | overall_time = np.sum(epoch_time) 121 | logger.info("Mean Epoch time: " + str(mean_epoch_time)) 122 | logger.info("overall training time: " + str(overall_time)) 123 | 124 | # load the best model weights 125 | model.load_weights(weight_fn) 126 | 127 | ############################################################################################# 128 | # evaluation of results 129 | ############################################################################################# 130 | 131 | # evaluate and print results 132 | pred_test = model.predict(X_test) 133 | pred_test_bool = np.argmax(pred_test, axis=1) 134 | 135 | logger.info('Accuracy on training data: ') 136 | report = classification_report(y_test.astype(int), pred_test_bool, output_dict=True) 137 | logger.info(classification_report(y_test.astype(int), pred_test_bool)) 138 | 139 | accs.append((report['accuracy'])) 140 | 141 | logger.info("Confusion matrix:") 142 | confusion_matrix = metrics.confusion_matrix(y_test.astype(int), pred_test_bool) 143 | logger.info(confusion_matrix) 144 | 145 | # f1 score 146 | f1 = f1_score(y_test.astype(int), pred_test_bool, average='weighted') 147 | scores.append(f1) 148 | logger.info("F1 Score: " + str(f1)) 149 | 150 | # save as mat files 151 | save_dic = {"report": report, "confusion_matrix": confusion_matrix, "config": config, "pred": pred_test, 152 | "label": y_test, "f1": f1} 153 | savemat("results/" + args.dataset + "/results_concatNet" + str(config.encoding_dim) + '_' + 154 | str(config.training_volume) + ".mat", save_dic) 155 | 156 | plt.plot(history.history['accuracy']) 157 | plt.plot(history.history['val_accuracy']) 158 | plt.title('model accuracy') 159 | plt.ylabel('accuracy') 160 | plt.xlabel('epoch') 161 | plt.legend(['train', 'val'], loc='upper left') 162 | plt.savefig("results/" + args.dataset + "/accuracy_history_concatNet" + str(config.encoding_dim) + '_' + 163 | str(config.training_volume) + "instance_" + str(it) + ".png") 164 | plt.show() 165 | 166 | plt.plot(history.history['loss']) 167 | plt.plot(history.history['val_loss']) 168 | plt.title('model loss') 169 | plt.ylabel('loss') 170 | plt.xlabel('epoch') 171 | plt.legend(['train', 'val'], loc='upper left') 172 | plt.savefig("results/" + args.dataset + "/loss_history_concatNet" + str(config.encoding_dim) + '_' + 173 | str(config.training_volume) + "instance_" + str(it) + ".png") 174 | plt.show() 175 | 176 | # add results to statistical result array 177 | acc_mean.append(np.mean(accs)) 178 | f1_mean.append(np.mean(scores)) 179 | 180 | logger.info('Accuracy results of statistical repetitions: ' + str(acc_mean)) 181 | logger.info('F1 scores of statistical repetitions: ' + str(f1_mean)) 182 | 183 | # write all scores to extra file 184 | logger.info('Mean Score: ' + str(np.mean(f1_mean))) 185 | logger.info('Mean Accuracy: ' + str(np.mean(acc_mean))) 186 | with open("results/results_" + args.dataset + "_Concat.txt", 'a') as file: 187 | file.write(str(config.input_dim) + '\t' 188 | + str(config.encoding_dim) + '\t' 189 | + str(args.stat_iterations) + '\t' 190 | + str(round(np.mean(f1_mean), 3)) + '\t' 191 | + str(round(np.mean(acc_mean), 3)) + '\t' 192 | + str(round(np.std(f1_mean), 3)) + '\t' 193 | + str(round(np.std(acc_mean), 3)) + '\n' 194 | ) -------------------------------------------------------------------------------- /HDC_ANN.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from config import Config 6 | from utils import * 7 | from model import HDC_ANN, HDC_ANN_tf 8 | from sklearn.metrics import classification_report, f1_score 9 | from sklearn.preprocessing import LabelEncoder 10 | from scipy.io import savemat, loadmat 11 | from sklearn import metrics 12 | from matplotlib import pyplot as plt 13 | from datetime import datetime 14 | from time import time 15 | from tensorflow.keras.utils import to_categorical 16 | from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau 17 | import logging 18 | 19 | # config logger 20 | logger = logging.getLogger('log') 21 | 22 | tf.compat.v1.disable_eager_execution() 23 | 24 | def main_HDC(args): 25 | ''' 26 | implementation of the HDC feed-forward network to predict the class of driving style 27 | - it uses preprocessed HDC encoding vectors 28 | ''' 29 | # set config parameter 30 | config = Config() 31 | config.training_volume = args.training_volume 32 | config.input_dim = args.input_dim 33 | config.encoding_dim = args.encoding_dim 34 | config.scale = args.scale 35 | config.m = 0 36 | config.s = 1 37 | if args.runtime_measurement: 38 | config.n_time_measures = 10 39 | else: 40 | config.n_time_measures = 1 41 | # if dimension is smaller than 1000, set dropout to 0.5 42 | if args.input_dim<1000: 43 | config.dropout = 0.5 44 | 45 | # load data set 46 | data = load_dataset(args.dataset,config) 47 | X_train = data[0] 48 | X_test = data[1] 49 | y_train = data[2] 50 | y_test = data[3] 51 | config = data[4] 52 | 53 | # if train test data not a list, create one (full_crossval data set loading returns a list of splits - therefore we 54 | # are handling all training set as lists, even if they only contain one set) 55 | if type(X_train)==list: 56 | print("given data is not a list") 57 | X_train_list = X_train 58 | X_test_list = X_test 59 | y_train_list = y_train 60 | y_test_list = y_test 61 | else: 62 | X_train_list =[X_train] 63 | X_test_list = [X_test] 64 | y_train_list = [y_train] 65 | y_test_list = [y_test] 66 | 67 | ####################################################################################### 68 | # statistical iteration 69 | ####################################################################################### 70 | acc_mean = [] 71 | f1_mean = [] 72 | 73 | for stat_it in range(args.stat_iterations): 74 | logger.info('Statistial iteration: ' + str(stat_it)) 75 | 76 | # train for each element in list (that is why we need list form, even if it contains only one element) 77 | logger.info('Training data contains ' + str(len(X_train_list)) + ' training instances...') 78 | scores = [] 79 | accs = [] 80 | for it in range(len(X_train_list)): 81 | logger.info(('.......')) 82 | logger.info('instance ' + str(it) + ':') 83 | 84 | X_train = X_train_list[it] 85 | X_test = X_test_list[it] 86 | y_train = y_train_list[it] 87 | y_test = y_test_list[it] 88 | 89 | # use only fraction of training samples (if given) 90 | X_train = X_train[1:int(X_train.shape[0] * config.training_volume), :] 91 | y_train = y_train[1:int(y_train.shape[0] * config.training_volume)] 92 | 93 | logger.info('Training dataset shape: ' + str(X_train.shape) + str(y_train.shape)) 94 | logger.info('Test dataset shape: ' + str(X_test.shape) + str(y_test.shape)) 95 | 96 | config.n_classes = len(np.unique(y_train)) 97 | config.n_inputs = X_train.shape[2] 98 | config.n_steps = X_train.shape[1] 99 | config.train_count = len(X_train) 100 | config.test_data_count = len(X_test) 101 | 102 | ####################################################################################### 103 | # create HDC vectors (encoding) 104 | ####################################################################################### 105 | tf.compat.v1.disable_eager_execution() 106 | # create HDC vectors 107 | t_train, X_train_HDC, traces_train, init_vecs = create_HDC_vectors(config, X_train) 108 | t_test, X_test_HDC, traces_test, init_vecs = create_HDC_vectors(config, X_test) 109 | preprocessing_time = t_train+t_test 110 | 111 | # normalize HDC encodings 112 | m = np.mean(X_train_HDC, axis=0) 113 | s = np.std(X_train_HDC,axis=0) 114 | config.m = m 115 | config.s = s 116 | X_train_HDC = np.divide(X_train_HDC - m,s) 117 | X_test_HDC = np.divide(X_test_HDC - m,s) 118 | 119 | ####################################################################################### 120 | # keras model training 121 | ####################################################################################### 122 | model = HDC_ANN(config) 123 | model.summary() 124 | 125 | # Create a TensorBoard callback 126 | logs = "logs/HDC_ts_" + datetime.now().strftime("%Y%m%d-%H%M%S") 127 | logs_pre = "logs/HDC_ts_preproc_" + datetime.now().strftime("%Y%m%d-%H%M%S") 128 | 129 | cb_time = TimingCallback() 130 | weight_fn = "./weights/HDC_ANN/%s_weights.h5" % args.dataset 131 | if not os.path.exists(weight_fn.rsplit('/', 1)[0]): 132 | os.makedirs(weight_fn.rsplit('/', 1)[0]) 133 | model_checkpoint = ModelCheckpoint(weight_fn, verbose=1, mode='auto', 134 | monitor='loss', save_best_only=True, save_weights_only=True) 135 | # compile model 136 | model.compile(optimizer='adam', 137 | loss='categorical_crossentropy', 138 | metrics=['accuracy']) 139 | 140 | if not args.test: 141 | # train model 142 | history = model.fit(X_train_HDC, to_categorical(y_train), 143 | epochs=config.training_epochs, 144 | batch_size=config.batch_size, 145 | shuffle=True, 146 | callbacks=[cb_time, model_checkpoint], 147 | validation_data=(X_test_HDC, to_categorical(y_test))) 148 | 149 | # log training time 150 | epoch_time = cb_time.logs 151 | mean_epoch_time = np.mean(epoch_time) 152 | overall_time = np.sum(epoch_time) 153 | logger.info("Mean Epoch time: " + str(mean_epoch_time)) 154 | logger.info("overall training time: " + str(overall_time)) 155 | 156 | plt.plot(history.history['accuracy']) 157 | plt.plot(history.history['val_accuracy']) 158 | plt.title('model accuracy') 159 | plt.ylabel('accuracy') 160 | plt.xlabel('epoch') 161 | plt.legend(['train', 'val'], loc='upper left') 162 | plt.savefig("results/" + args.dataset + "/accuracy_history_HDC_" + str(config.input_dim) + "_" + 163 | str(config.scale) + '_' + str(config.encoding_dim) + '_' + str(config.training_volume) + "instance_" + str(it) + ".png") 164 | plt.show() 165 | 166 | plt.plot(history.history['loss']) 167 | plt.plot(history.history['val_loss']) 168 | plt.title('model loss') 169 | plt.ylabel('loss') 170 | plt.xlabel('epoch') 171 | plt.legend(['train', 'val'], loc='upper left') 172 | plt.savefig("results/" + args.dataset + "/loss_history_HDC" + str(config.input_dim) + "_" + 173 | str(config.scale) + '_' + str(config.encoding_dim) + '_' + str(config.training_volume) + "instance_" + str(it) + ".png") 174 | plt.show() 175 | 176 | # load the best model weights 177 | model.load_weights(weight_fn) 178 | 179 | ############################################################################################# 180 | # evaluation of results 181 | ############################################################################################# 182 | # evaluate with tensorflow model (better comparability to LTSM TF model) 183 | X = tf.compat.v1.placeholder(tf.float32, [None, config.n_steps, config.n_inputs], name="X") 184 | # get weights of keras model 185 | weights = model.get_weights() 186 | W = {'hidden': weights[0], 'output': weights[2]} 187 | biases = {'hidden': weights[1], 'output': weights[3]} 188 | # create TF model 189 | tf_model = HDC_ANN_tf(X, config, init_vecs, W, biases) 190 | t_i=[] 191 | for i in range(config.n_time_measures): 192 | sess = tf.compat.v1.Session() 193 | t1 = time() 194 | pred_test = sess.run(tf_model, feed_dict={X: X_test}) 195 | inference_time = time() - t1 196 | t_i.append(inference_time) 197 | inference_time = np.median(t_i) 198 | 199 | logger.info("Preprocessing time for training: " + str(t_train)) 200 | logger.info("Inference time: " + str(inference_time)) 201 | logger.info("Inference time one sequence [ms]: " + str((inference_time*1000)/X_test.shape[0])) 202 | 203 | logger.info('Preprocessing time on dataset: ' + str(t_train) + ' + ' + str(t_test) + ' = ' + str( 204 | preprocessing_time)) 205 | pred_test_bool = np.argmax(pred_test, axis=1) 206 | 207 | logger.info('Accuracy on training data: ') 208 | report = classification_report(y_test.astype(int), pred_test_bool, output_dict=True) 209 | logger.info(classification_report(y_test.astype(int), pred_test_bool)) 210 | 211 | accs.append((report['accuracy'])) 212 | 213 | logger.info("Confusion matrix:") 214 | confusion_matrix = metrics.confusion_matrix(y_test.astype(int), pred_test_bool) 215 | logger.info(confusion_matrix) 216 | 217 | # f1 score 218 | f1 = f1_score(y_test.astype(int), pred_test_bool, average='weighted') 219 | scores.append(f1) 220 | logger.info("F1 Score: " + str(f1)) 221 | 222 | # add results to statistical result array 223 | acc_mean.append(np.mean(accs)) 224 | f1_mean.append(np.mean(scores)) 225 | 226 | # save as mat files 227 | save_dic = {"report": report, "confusion_matrix": confusion_matrix, "config": config, "pred": pred_test, 228 | "label": y_test, "f1_mean": np.mean(f1_mean), "acc_mean": np.mean(acc_mean)} 229 | savemat("results/" + args.dataset + "/results_HDC_" + str(config.input_dim) + "_" + str(config.scale) + "_" + 230 | str(config.encoding_dim) + '_' + str(config.training_volume) + ".mat", save_dic) 231 | 232 | logger.info('Accuracy results of statistical repetitions: ' + str(acc_mean)) 233 | logger.info('F1 scores of statistical repetitions: ' + str(f1_mean)) 234 | 235 | # write all scores to extra file 236 | logger.info('Mean Score: ' + str(np.mean(f1_mean))) 237 | logger.info('Mean Accuracy: ' + str(np.mean(acc_mean))) 238 | with open("results/results_" + args.dataset + "_HDC.txt", 'a') as file: 239 | file.write(str(config.input_dim) + '\t' 240 | + str(config.encoding_dim) + '\t' 241 | + str(config.scale) + '\t' 242 | + str(args.stat_iterations) + '\t' 243 | + str(round(np.mean(f1_mean),3)) + '\t' 244 | + str(round(np.mean(acc_mean),3)) + '\t' 245 | + str(round(np.std(f1_mean),3)) + '\t' 246 | + str(round(np.std(acc_mean),3)) + '\t' 247 | + str(args.training_volume) + '\n' 248 | ) 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /HDC_SNN.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from sklearn import metrics 6 | from config import Config 7 | from utils import * 8 | from sklearn.metrics import classification_report 9 | from scipy.io import savemat 10 | from sklearn import metrics 11 | import logging 12 | from time import time 13 | import sklearn 14 | import nengo_dl 15 | import nengo 16 | 17 | physical_devices = tf.config.list_physical_devices('GPU') 18 | try: 19 | tf.config.experimental.set_memory_growth(physical_devices[0], True) 20 | except: 21 | # Invalid device or cannot modify virtual devices once initialized. 22 | pass 23 | 24 | # config logger 25 | logger = logging.getLogger('log') 26 | 27 | tf.compat.v1.enable_eager_execution() 28 | 29 | def main_SNN(args): 30 | config = Config() 31 | config.training_volume = args.training_volume 32 | config.input_dim = args.input_dim 33 | config.encoding_dim = args.encoding_dim 34 | config.scale = args.scale 35 | if args.runtime_measurement: 36 | config.n_time_measures = 10 37 | else: 38 | config.n_time_measures = 1 39 | 40 | # nego net params 41 | do_rate = 0.5 42 | num_epochs = 200 43 | enc_dim = 1000 44 | minibatch_size = 500 45 | seed = 0 46 | 47 | # load dataset 48 | data = load_dataset(args.dataset,config) 49 | X_train = data[0] 50 | X_test = data[1] 51 | y_train = data[2] 52 | y_test = data[3] 53 | config = data[4] 54 | 55 | # if train test data not a list, create one 56 | if type(X_train)==list: 57 | print("given data is not a list") 58 | X_train_list = X_train 59 | X_test_list = X_test 60 | y_train_list = y_train 61 | y_test_list = y_test 62 | else: 63 | X_train_list =[X_train] 64 | X_test_list = [X_test] 65 | y_train_list = [y_train] 66 | y_test_list = [y_test] 67 | 68 | ####################################################################################### 69 | # statistical iteration 70 | ####################################################################################### 71 | acc_mean = [] 72 | f1_mean = [] 73 | 74 | for stat_it in range(args.stat_iterations): 75 | logger.info('Statistial iteration: ' + str(stat_it)) 76 | seed = stat_it 77 | 78 | # train for each element in list (that is why we need list form, even if it contains only one element) 79 | logger.info('Training data contains ' + str(len(X_train)) + ' training instances...') 80 | scores = [] 81 | accs = [] 82 | for it in range(len(X_train_list)): 83 | logger.info(('.......')) 84 | logger.info('instance ' + str(it) + ':') 85 | 86 | X_train = X_train_list[it] 87 | X_test = X_test_list[it] 88 | y_train = y_train_list[it] 89 | y_test = y_test_list[it] 90 | 91 | # use only fraction of training samples (if given) 92 | X_train = X_train[1:int(X_train.shape[0] * config.training_volume), :,:] 93 | y_train = y_train[1:int(y_train.shape[0] * config.training_volume)] 94 | 95 | y_train_oh = one_hot(y_train) 96 | y_test_oh = one_hot(y_test) 97 | 98 | # config.input_dim = X_train.shape[1] 99 | logger.info('Training dataset shape: ' + str(X_train.shape) + str(y_train.shape)) 100 | logger.info('Test dataset shape: ' + str(X_test.shape) + str(y_test.shape)) 101 | 102 | config.n_classes = len(np.unique(y_train)) 103 | config.n_inputs = X_train.shape[2] 104 | config.n_steps = X_train.shape[1] 105 | 106 | ####################################################################################### 107 | # create HDC vectors (encoding) 108 | ####################################################################################### 109 | # create HDC vectors 110 | t_train, X_train, traces_train, init_vecs = create_HDC_vectors(config, X_train) 111 | t_test, X_test, traces_test, init_vecs = create_HDC_vectors(config, X_test) 112 | preprocessing_time = t_train + t_test 113 | 114 | # normalize HDC encodings 115 | m = np.mean(X_train, axis=0) 116 | s = np.std(X_train,axis=0) 117 | config.m = m 118 | config.s = s 119 | X_train = np.divide(X_train - m,s) 120 | X_test = np.divide(X_test - m,s) 121 | 122 | ####################################################################################### 123 | # nengo model training 124 | ####################################################################################### 125 | 126 | net = nengo.Network(seed=seed + 1) 127 | 128 | with net: 129 | # set some default parameters for the neurons that will make 130 | # the training progress more smoothly 131 | net.config[nengo.Ensemble].max_rates = nengo.dists.Choice([100]) 132 | net.config[nengo.Ensemble].intercepts = nengo.dists.Choice([0]) 133 | net.config[nengo.Connection].synapse = None 134 | neuron_type = nengo.LIF(amplitude=0.01) 135 | 136 | # this is an optimization to improve the training speed, 137 | # since we won't require stateful behaviour in this example 138 | nengo_dl.configure_settings(stateful=False) 139 | 140 | # the input node that will be used to feed in input vectors 141 | inp = nengo.Node(np.zeros(config.input_dim)) 142 | 143 | x = nengo_dl.Layer(tf.keras.layers.Dropout(rate=do_rate))(inp) 144 | x = nengo_dl.Layer(neuron_type)(x) 145 | 146 | x = nengo_dl.Layer(tf.keras.layers.Dense(units=enc_dim))(x) 147 | x = nengo_dl.Layer(neuron_type)(x) 148 | 149 | out = nengo_dl.Layer(tf.keras.layers.Dense(units=len(y_train_oh[0])))(x) 150 | 151 | # we'll create two different output probes, one with a filter 152 | # (for when we're simulating the network over time and 153 | # accumulating spikes), and one without (for when we're 154 | # training the network using a rate-based approximation) 155 | out_p = nengo.Probe(out, label="out_p") 156 | out_p_filt = nengo.Probe(out, synapse=0.1, label="out_p_filt") 157 | 158 | sim = nengo_dl.Simulator(net, minibatch_size=minibatch_size, device="/gpu:0") 159 | 160 | # run training 161 | sim.compile( 162 | optimizer=tf.optimizers.RMSprop(0.001), 163 | loss={out_p: tf.losses.CategoricalCrossentropy(from_logits=True)}, 164 | ) 165 | 166 | # add single timestep to training data 167 | X_train = X_train[:, None, :] 168 | y_train_oh = y_train_oh[:, None] 169 | 170 | # when testing our network with spiking neurons we will need to run it 171 | # over time, so we repeat the input/target data for a number of 172 | # timesteps. 173 | n_steps = 30 174 | X_test = np.tile(X_test[:, None, :], (1, n_steps, 1)) 175 | y_test_oh = np.tile(y_test_oh[:, None], (n_steps, 1)) 176 | 177 | def classification_accuracy(y_true, y_pred): 178 | return tf.metrics.categorical_accuracy(y_true[:, -1], y_pred[:, -1]) 179 | 180 | accuracy = sim.evaluate(X_test, {out_p_filt: y_test_oh}, verbose=0)["loss"], 181 | print("Accuracy before training:", accuracy) 182 | 183 | cb_time = TimingCallback() 184 | sim.fit(X_train, 185 | {out_p: y_train_oh}, 186 | epochs=num_epochs, 187 | callbacks=[cb_time], 188 | ) 189 | # log training time 190 | epoch_time = cb_time.logs 191 | mean_epoch_time = np.mean(epoch_time) 192 | training_time = np.sum(epoch_time) 193 | 194 | # save the parameters to file 195 | # sim.save_params("./nengo_dl_params") 196 | 197 | ############################################################################################# 198 | # evaluation of results 199 | ############################################################################################# 200 | 201 | sim.compile(loss={out_p_filt: classification_accuracy}) 202 | 203 | # runtime measurement 204 | t=[] 205 | for i in range(config.n_time_measures): 206 | t1 = time() 207 | accuracy = sim.evaluate(X_test, {out_p_filt: y_test_oh}, verbose=0)["loss"], 208 | inference_time = time() - t1 209 | t.append(inference_time) 210 | inference_time = np.mean(t) 211 | print("Accuracy after training:", accuracy) 212 | accs.append(accuracy) 213 | 214 | sim2 = nengo_dl.Simulator(net, minibatch_size=1, device="/gpu:0") 215 | 216 | y_pred = sim2.predict(X_test) 217 | y_pred_am = np.argmax(y_pred[out_p_filt][:,-1,:], axis=1) 218 | y_pred_am.shape = (y_pred_am.shape[0],1) 219 | f1_score = sklearn.metrics.f1_score(y_test, 220 | y_pred_am, 221 | average='micro') 222 | f1_score_weighted = sklearn.metrics.f1_score( 223 | y_test, 224 | y_pred_am, 225 | average='weighted' 226 | ) 227 | 228 | logger.info("Training time: " + str(training_time)) 229 | logger.info("Mean epoch time: " + str(mean_epoch_time)) 230 | logger.info("Inference time: " + str(inference_time)) 231 | 232 | logger.info("Preprocessing time for training: " + str(t_train)) 233 | logger.info("Preprocessing time for testing: " + str(t_test)) 234 | logger.info("Inference time one sequence [ms]: " + str((inference_time*1000+t_test*1000)/X_test.shape[0])) 235 | 236 | 237 | print('Accuracy on training data: ') 238 | report = classification_report(y_test, y_pred_am, output_dict=True) 239 | logger.info(classification_report(y_test, y_pred_am)) 240 | 241 | print("Confusion matrix:") 242 | confusion_matrix = metrics.confusion_matrix(y_test, y_pred_am) 243 | print(confusion_matrix) 244 | 245 | # f1 score 246 | scores.append(f1_score_weighted) 247 | logger.info("F1 Score: " + str(f1_score_weighted)) 248 | 249 | # close simulator 250 | sim.close() 251 | sim2.close() 252 | 253 | 254 | # add results to statistical result array 255 | acc_mean.append(np.mean(accs)) 256 | f1_mean.append(np.mean(scores)) 257 | 258 | # save as mat files 259 | save_dic = {"report": report, "confusion_matrix": confusion_matrix, "config": config, "pred": y_pred, 260 | "label": y_test, "f1_mean": np.mean(f1_mean)} 261 | savemat("results/" + args.dataset + "/results_Nengo_net_" + str(config.input_dim) + "_" + str(config.scale) + "_" + 262 | str(config.encoding_dim) + '_' + str(config.training_volume) + ".mat", save_dic) 263 | 264 | logger.info('Accuracy results of statistical repetitions: ' + str(acc_mean)) 265 | logger.info('F1 scores of statistical repetitions: ' + str(f1_mean)) 266 | 267 | # write all scores to extra file 268 | logger.info('Mean Score: ' + str(np.mean(f1_mean))) 269 | logger.info('Mean Accuracy: ' + str(np.mean(acc_mean))) 270 | with open("results/results_" + args.dataset + "_SNN.txt", 'a') as file: 271 | file.write(str(config.input_dim) + '\t' 272 | + str(config.encoding_dim) + '\t' 273 | + str(config.scale) + '\t' 274 | + str(args.stat_iterations) + '\t' 275 | + str(round(np.mean(f1_mean),3)) + '\t' 276 | + str(round(np.mean(acc_mean),3)) + '\t' 277 | + str(round(np.std(f1_mean),3)) + '\t' 278 | + str(round(np.std(acc_mean),3)) + '\n' 279 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /LSTM.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from config import Config 6 | from utils import * 7 | from model import HDC_ANN, LSTM_Network 8 | from sklearn.metrics import classification_report, f1_score 9 | from scipy.io import savemat, loadmat 10 | from sklearn import metrics 11 | import os 12 | from time import time 13 | from tensorflow.python.client import timeline 14 | import logging 15 | import matplotlib.pyplot as plt 16 | 17 | # config logger 18 | logger = logging.getLogger('log') 19 | 20 | 21 | def main_LSTM(args): 22 | ''' 23 | implementation of the original LSTM approach (https://github.com/KhaledSaleh/driving_behaviour_classification) 24 | ''' 25 | # set config params specific for the original code 26 | config = Config() 27 | config.training_volume = args.training_volume 28 | config.input_dim = args.input_dim 29 | config.encoding_dim = args.encoding_dim 30 | config.scale = args.scale 31 | if args.runtime_measurement: 32 | config.n_time_measures = 10 33 | else: 34 | config.n_time_measures = 1 35 | 36 | # load preprocessed data 37 | data = load_dataset(args.dataset,config) 38 | X_train = data[0] 39 | X_test = data[1] 40 | y_train = data[2] 41 | y_test = data[3] 42 | config = data[4] 43 | 44 | logs = [] 45 | # if train test data not a list, create one 46 | if type(X_train)==list: 47 | print("given data is not a list") 48 | X_train_list = X_train 49 | X_test_list = X_test 50 | y_train_list = y_train 51 | y_test_list = y_test 52 | else: 53 | X_train_list =[X_train] 54 | X_test_list = [X_test] 55 | y_train_list = [y_train] 56 | y_test_list = [y_test] 57 | 58 | ####################################################################################### 59 | # statistical iteration 60 | ####################################################################################### 61 | acc_mean = [] 62 | f1_mean = [] 63 | 64 | for stat_it in range(args.stat_iterations): 65 | logger.info('Statistial iteration: ' + str(stat_it)) 66 | 67 | # train for each element in list (that is why we need list form, even if it contains only one element) 68 | logger.info('Training data contains ' + str(len(X_train_list)) + ' training instances...') 69 | scores = [] 70 | accs = [] 71 | for it in range(len(X_train_list)): 72 | logger.info(('.......')) 73 | logger.info('instance ' + str(it) + ':') 74 | 75 | X_train = X_train_list[it] 76 | X_test = X_test_list[it] 77 | y_train = y_train_list[it] 78 | y_test = y_test_list[it] 79 | 80 | # use only fraction of training samples (if given) 81 | X_train = X_train[1:int(X_train.shape[0] * config.training_volume), :] 82 | y_train = y_train[1:int(y_train.shape[0] * config.training_volume), :] 83 | 84 | config.n_inputs = X_train.shape[2] 85 | config.train_count = len(X_train) 86 | config.test_data_count = len(X_test) 87 | config.n_steps = len(X_train[0]) 88 | config.n_classes = len(np.unique(y_train)) 89 | 90 | logger.info('Training dataset shape: ' + str(X_train.shape) + str(y_train.shape)) 91 | logger.info('Test dataset shape: ' + str(X_test.shape) + str(y_test.shape)) 92 | graph = tf.Graph() 93 | with graph.as_default(): 94 | 95 | X = tf.compat.v1.placeholder(tf.float32, [None, config.n_steps, config.n_inputs], name="X") 96 | Y = tf.compat.v1.placeholder(tf.float32, [None, config.n_classes], name="Y") 97 | 98 | pred_Y = LSTM_Network(X, config) 99 | 100 | # Loss,optimizer,evaluation 101 | l2 = config.lambda_loss_amount * \ 102 | sum(tf.nn.l2_loss(tf_var) for tf_var in tf.compat.v1.trainable_variables()) 103 | # Softmax loss and L2 104 | cost = tf.reduce_mean( 105 | tf.nn.softmax_cross_entropy_with_logits(logits=pred_Y, labels=Y), name="cost") + l2 106 | optimizer = tf.compat.v1.train.AdamOptimizer( 107 | learning_rate=config.learning_rate).minimize(cost) 108 | 109 | correct_pred = tf.equal(tf.argmax(pred_Y, 1), tf.argmax(Y, 1)) 110 | accuracy = tf.reduce_mean(tf.cast(correct_pred, dtype=tf.float32)) 111 | 112 | saver = tf.compat.v1.train.Saver() 113 | 114 | with tf.compat.v1.Session(graph=graph, config=tf.compat.v1.ConfigProto(log_device_placement=False)) as sess: 115 | if not args.test: 116 | init_op = tf.compat.v1.global_variables_initializer() 117 | sess.run(init_op) 118 | best_accuracy = 0.0 119 | # Start training for each batch and loop epochs 120 | for i in range(config.training_epochs): 121 | starttime = time() 122 | for start, end in zip(range(0, config.train_count, config.batch_size), 123 | range(config.batch_size, config.train_count + 1, config.batch_size)): 124 | sess.run(optimizer, feed_dict={X: X_train[start:end], 125 | Y: one_hot(y_train[start:end],config.n_classes)}) 126 | saver.save(sess, os.path.join("./weights", 'LSTM_model')) 127 | # Test completely at every epoch: calculate accuracy 128 | pred_out, accuracy_out, loss_out = sess.run([pred_Y, accuracy, cost], feed_dict={ 129 | X: X_test, Y: one_hot(y_test, config.n_classes)}) 130 | logs.append(time() - starttime) 131 | print("Training iter: {},".format(i) + \ 132 | " Test accuracy : {},".format(accuracy_out) + \ 133 | " Loss : {}".format(loss_out)) 134 | best_accuracy = max(best_accuracy, accuracy_out) 135 | print("") 136 | mean_epoch_time = np.mean(logs) 137 | overall_time = np.sum(logs) 138 | logger.info("Mean Epoch time: " + str(mean_epoch_time)) 139 | logger.info("overall training time: " + str(overall_time)) 140 | logger.info("Final test accuracy: {}".format(accuracy_out)) 141 | logger.info("Best epoch's test accuracy: {}".format(best_accuracy)) 142 | 143 | print("") 144 | # start testing the trained model 145 | else: 146 | saver.restore(sess, os.path.join("./weights", 'LSTM_model')) 147 | t1 = time() 148 | pred_out, accuracy_out, loss_out = sess.run([pred_Y, accuracy, cost], feed_dict={ 149 | X: X_test, Y: one_hot(y_test,config.n_classes)}) 150 | inference_time = time() - t1 151 | print(" Test accuracy : {},".format(accuracy_out) + \ 152 | " Loss : {}".format(loss_out)) 153 | 154 | ############################################################################################# 155 | # evaluation of results 156 | ############################################################################################# 157 | 158 | pred_test_bool = pred_out.argmax(1) 159 | 160 | # runtime measurement 161 | t=[] 162 | traces = [] 163 | options = tf.compat.v1.RunOptions(trace_level=tf.compat.v1.RunOptions.FULL_TRACE) 164 | run_metadata = tf.compat.v1.RunMetadata() 165 | for i in range(config.n_time_measures): 166 | with tf.compat.v1.Session(graph=graph, config=tf.compat.v1.ConfigProto(log_device_placement=False)) as Sess: 167 | init_op = tf.compat.v1.global_variables_initializer() 168 | Sess.run(init_op) 169 | t1 = time() 170 | Sess.run([pred_Y, accuracy, cost], feed_dict={ 171 | X: X_test, Y: one_hot(y_test, config.n_classes)}, options=options, run_metadata=run_metadata) 172 | inference_time = time() - t1 173 | fetched_timeline = timeline.Timeline(run_metadata.step_stats) 174 | chrome_trace = fetched_timeline.generate_chrome_trace_format() 175 | traces.append(chrome_trace) 176 | t.append(inference_time) 177 | with open('./logs/LSTM_ts_preproc_timeline_test.json', 'w') as f: 178 | f.write(traces[-1]) 179 | inference_time = np.median(inference_time) 180 | logger.info("Inference time: " + str(inference_time)) 181 | logger.info("Inference time of one sequence [ms]: " + str(inference_time*1000/X_test.shape[0])) 182 | 183 | logger.info('Accuracy on training data: ') 184 | report = classification_report(y_test.astype(int), pred_test_bool, output_dict=True) 185 | logger.info(classification_report(y_test.astype(int), pred_test_bool)) 186 | 187 | accs.append((report['accuracy'])) 188 | 189 | logger.info("Confusion matrix:") 190 | confusion_matrix = metrics.confusion_matrix(y_test.astype(int), pred_test_bool) 191 | logger.info(confusion_matrix) 192 | 193 | # f1 score 194 | f1 = f1_score(y_test.astype(int), pred_test_bool, average='weighted') 195 | scores.append(f1) 196 | logger.info("F1 Score: " + str(f1)) 197 | 198 | # add results to statistical result array 199 | acc_mean.append(np.mean(accs)) 200 | f1_mean.append(np.mean(scores)) 201 | 202 | # save as mat files 203 | save_dic = {"report": report, "confusion_matrix": confusion_matrix, "config": config, "pred": pred_out, 204 | "label": y_test, "f1": np.mean(f1_mean), "acc_mean": np.mean(acc_mean)} 205 | savemat("results/" + args.dataset + "/results_origNet_" + str(config.training_volume) + ".mat", save_dic) 206 | 207 | logger.info('Accuracy results of statistical repetitions: ' + str(acc_mean)) 208 | logger.info('F1 scores of statistical repetitions: ' + str(f1_mean)) 209 | 210 | # write all scores to extra file 211 | logger.info('Mean Score: ' + str(np.mean(f1_mean))) 212 | logger.info('Mean Accuracy: ' + str(np.mean(acc_mean))) 213 | with open("results/results_" + args.dataset + "_LSTM.txt", 'a') as file: 214 | file.write(str(args.stat_iterations) + '\t' 215 | + str(round(np.mean(f1_mean), 3)) + '\t' 216 | + str(round(np.mean(acc_mean), 3)) + '\t' 217 | + str(round(np.std(f1_mean), 3)) + '\t' 218 | + str(round(np.std(acc_mean), 3)) + '\t' 219 | + str(args.training_volume) + '\n' 220 | ) 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VSA for driving behaviour classification 2 | 3 | This repository is mainly based on the code of https://github.com/KhaledSaleh/driving_behaviour_classification 4 | 5 | It has 3 different models: 6 | - a LSTM model (original model from [1]) 7 | - a feed-forward model (ANN) for HDC encodings 8 | - a spiking neural model (SNN) for HDC encodings 9 | 10 | [1] K. Saleh, M. Hossny, and S. Nahavandi, “Driving behavior classification based on sensor data fusion using LSTM recurrent neural networks,” in International Conference on Intelligent Transportation Systems (ITSC), 2017. 11 | 12 | Tested with the Python packages listed in requirements.txt. 13 | 14 | ## Usage 15 | * first, clone the Repo `git clone https://github.com/TUC-ProAut/HDC_driving_style_classification.git` 16 | 17 | ### Train the networks (Python) 18 | 1. Run `python3 main.py --help` to check the available command line args. 19 | 2. Run ANN with HDC encodings: 20 | * `python3 main.py --HDC_ANN True` (use --dataset argument to select between full, motorway, secondary or full_crossval) 21 | 3. Run ANN with concatenated input sequences: 22 | * `python3 main.py --Concat_ANN True` 23 | 4. Run the original LSTM model from https://github.com/KhaledSaleh/driving_behaviour_classification 24 | * `python3 main.py --LSTM True` 25 | 5. Run SNN with HDC encodings: 26 | * `python3 main.py --HDC_SNN True` 27 | 28 | The results are written to the log file logs/main_log.log 29 | 30 | ### Data efficiency experiment (Python) 31 | 1. Run `python3 main.py --data_efficiency True --HDC_ANN True` for the appropriate network as in section above 32 | 33 | ### (Optional) Hyper-parameter analysis for HDC encodings (Python) 34 | 1. Run `python3 main.py --hyperparams_experiment True --HDC_ANN True` 35 | 36 | ### Run Baseline models (MATLAB) 37 | 1. Run `eval_baseline_models.m` 38 | 39 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | class Config(object): 4 | """ 5 | define a class to store parameters, 6 | the input should be feature mat of training and testing 7 | """ 8 | 9 | def __init__(self): 10 | 11 | # Trainging 12 | self.learning_rate = 0.0025 13 | self.lambda_loss_amount = 0.0015 14 | self.training_epochs = 2000 15 | self.batch_size = 1500 16 | self.training_volume = 1 17 | 18 | # network structure 19 | self.n_classes = 3 20 | self.n_steps = 64 21 | self.n_hidden = 100 22 | self.dropout = 0.75 23 | 24 | 25 | -------------------------------------------------------------------------------- /create_figs_and_tables.m: -------------------------------------------------------------------------------- 1 | %% create figures and tables 2 | % scken, 2021 3 | % Copyright (C) 2021 Chair of Automation Technology / TU Chemnitz 4 | 5 | clear all 6 | close all 7 | 8 | n_dim = [512 1024 2048]; 9 | scale = [2 4 6 8]; 10 | encoding_dim = [20 40 60 80 100]; 11 | HDC_network = 'True'; 12 | dataset = 'full'; 13 | 14 | f1_tensor = zeros([3 3 3]); 15 | 16 | %% iterate over all results of hyper-parameter analysis (table 3) 17 | 18 | for d=1:numel(n_dim) 19 | for s=1:numel(scale) 20 | for e=1:numel(encoding_dim) 21 | % load file 22 | load(['results/' dataset '/results_HDC_' num2str(n_dim(d)) '_' num2str(scale(s)) '_' num2str(encoding_dim(e)) '_1.0.mat']) 23 | 24 | f1_tensor(d,s,e) = round(report.accuracy,2); 25 | end 26 | end 27 | end 28 | 29 | % create result table 30 | subtable = {}; 31 | 32 | % create Rownames 33 | rownames = {}; 34 | for s=1:numel(scale) 35 | rownames{end +1} = ['scale = ' num2str(scale(s))]; 36 | end 37 | 38 | % variable names 39 | varnames = {}; 40 | for e=1:numel(encoding_dim) 41 | varnames{end+1} = [num2str(encoding_dim(e))]; 42 | end 43 | 44 | for d=1:numel(n_dim) 45 | subtable{d} = array2table(squeeze(f1_tensor(d,:,:)), ... 46 | 'VariableNames',varnames); 47 | end 48 | 49 | tab = table(subtable{:},'VariableNames',{'\# Dimensions = 512', '\# Dimensions = 1024', '\# Dimensions = 2048'},'RowNames',rownames) 50 | table2latex(tab,[3 2],0.5,'tables/results_vsa.tex') 51 | 52 | %% evaluate model size (table 4) 53 | 54 | param_array = zeros([numel(n_dim) numel(encoding_dim)]); 55 | 56 | for d=1:numel(n_dim) 57 | for e=1:numel(encoding_dim) 58 | % load file 59 | load(['results/' dataset '/results_HDC_' num2str(n_dim(d)) '_8_' num2str(encoding_dim(e)) '_1.0.mat']) 60 | start_idx = findstr(model_summary,'Trainable params: '); 61 | start_idx = start_idx + 18; 62 | end_idx = findstr(model_summary,'Non-trainable'); 63 | n_params = str2num(replace(model_summary(start_idx:end_idx-1),',','')); 64 | 65 | param_array(d,e) = n_params; 66 | end 67 | end 68 | 69 | 70 | %% plot different scales of fractional binding (fig. 2) 71 | 72 | vsa = 'FHRR'; 73 | dim = 2048; 74 | 75 | VSA = vsa_env('vsa',vsa,'dim',dim); 76 | init_vector = VSA.add_vector(); 77 | values = -1.5:0.01:1.5; 78 | line_type = {':';'-';'--';'-.'}; 79 | 80 | sim_values = zeros([numel(scale) numel(values)]); 81 | 82 | leg = {}; 83 | 84 | for s=1:numel(scale) 85 | encoded_values = VSA.frac_binding(init_vector,values * scale(s)); 86 | ref_value = VSA.frac_binding(init_vector,0); 87 | sim_values(s,:) = VSA.sim(ref_value, encoded_values); 88 | 89 | plot(values,sim_values(s,:),line_type{s}) 90 | hold on 91 | leg{end+1} = ['scaling = ' num2str(scale(s))]; 92 | end 93 | 94 | grid on 95 | title('Similarity of encoded scalar value 0 to neighboring values') 96 | xlabel('scalar value') 97 | ylabel('similarity to encoded value 0') 98 | legend(leg) 99 | set(gcf,'color','w') 100 | saveas(gcf,'images/similarity_plot_frac_binding.png') 101 | export_fig('images/similarity_plot_frac_binding.pdf','-dpdf') 102 | 103 | 104 | %% plot data efficiency (fig. 6) 105 | 106 | figure() 107 | n_dim = 2048; 108 | scale = 10; 109 | encoding_dim = 40; 110 | dataset = 'full'; 111 | training_volume = {'0.2'; '0.4'; '0.6'; '0.8'; '1.0'}; 112 | f1_array_orig = []; 113 | f1_array_VSA = []; 114 | 115 | % original network 116 | for t=1:numel(training_volume) 117 | % load file 118 | load(['results/' dataset '/results_origNet_' training_volume{t} '.mat']) 119 | 120 | f1_array_orig(end+1) = report.accuracy; 121 | end 122 | 123 | % VSA network 124 | for t=1:numel(training_volume) 125 | % load file 126 | load(['results/' dataset '/results_HDC_' num2str(n_dim) '_' num2str(scale) '_' num2str(encoding_dim) '_' training_volume{t} '.mat']) 127 | 128 | f1_array_VSA(end+1) = report.accuracy; 129 | end 130 | 131 | plot(str2num(cell2mat(training_volume)), f1_array_orig,'--','LineWidth',2) 132 | hold on 133 | plot(str2num(cell2mat(training_volume)), f1_array_VSA,'-.','LineWidth',2) 134 | grid on 135 | xlabel('training volume') 136 | ylabel('F_1 Score') 137 | title('Data efficiency') 138 | set(gcf,'color','w') 139 | set(gcf,'Position',[100 100 500 200]) 140 | legend({'LSTM-ANN'; 'HDC-ANN'},'Location','SouthEast') 141 | saveas(gcf,'images/data_efficiency.png') 142 | export_fig('images/data_efficiency.pdf','-dpdf') 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /create_train_test_split_MATLAB.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from argparse import ArgumentParser 6 | from utils import * 7 | from scipy.io import savemat, loadmat 8 | from sklearn.model_selection import train_test_split 9 | import numpy as np 10 | from config import Config 11 | 12 | 13 | if __name__ == '__main__': 14 | parser = ArgumentParser(description=__doc__) 15 | parser.add_argument('--dataset', '-d', 16 | help='Which split of the dataset to train/test the model on?' \ 17 | '(i.e. full, motorway, secondary or full_crossval)', 18 | default='full_crossval') 19 | parser.add_argument('--preproc', 20 | default='1') 21 | parser.add_argument('--input_dim', 22 | help='Defines the input dimension of the VSA model' \ 23 | '(possible values are 512, 1024 or 2048)', 24 | default=2048) 25 | parser.add_argument('--scale', 26 | help='scaling of the scalar encoding with fractional binding ' \ 27 | '(possible values are 2, 4, 6, 8 and 10)', 28 | default=6) 29 | 30 | args = parser.parse_args() 31 | 32 | config = Config() 33 | config.input_dim = int(args.input_dim) 34 | config.scale = float(args.scale) 35 | 36 | if args.preproc=='1': 37 | data = load_dataset(args.dataset,config) 38 | if type(data[0]) == list: 39 | X_train = [] 40 | X_test = [] 41 | y_train = [] 42 | y_test = [] 43 | for i in range(len(data[0])): 44 | config.n_time_measures = 1 45 | config.n_inputs = data[0][i].shape[2] 46 | config.n_steps = data[0][i].shape[1] 47 | t_train, X_train_, traces_train, init_vecs = create_HDC_vectors(config, data[0][i]) 48 | t_test, X_test_, traces_test, init_vecs = create_HDC_vectors(config, data[1][i]) 49 | 50 | # normalize HDC encodings 51 | m = np.mean(X_train_, axis=0) 52 | s = np.std(X_train_, axis=0) 53 | config.m = m 54 | config.s = s 55 | X_train_ = np.divide(X_train_ - m, s) 56 | X_test_ = np.divide(X_test_ - m, s) 57 | 58 | y_train_ = data[2][i] 59 | y_test_ = data[3][i] 60 | 61 | X_train.append(X_train_) 62 | X_test.append(X_test_) 63 | y_train.append(y_train_) 64 | y_test.append(y_test_) 65 | 66 | else: 67 | config.n_time_measures = 1 68 | config.n_inputs = data[0].shape[2] 69 | config.n_steps = data[0].shape[1] 70 | t_train, X_train, traces_train, init_vecs = create_HDC_vectors(config, data[0]) 71 | t_test, X_test, traces_test, init_vecs = create_HDC_vectors(config, data[1]) 72 | 73 | # normalize HDC encodings 74 | m = np.mean(X_train, axis=0) 75 | s = np.std(X_train, axis=0) 76 | config.m = m 77 | config.s = s 78 | X_train = np.divide(X_train - m, s) 79 | X_test = np.divide(X_test - m, s) 80 | 81 | y_train = data[2] 82 | y_test = data[3] 83 | else: 84 | data = load_dataset(args.dataset, config) 85 | 86 | if type(data[0]) == list: 87 | X_train = [] 88 | X_test = [] 89 | y_train = [] 90 | y_test = [] 91 | for i in range(len(data[0])): 92 | X_train_ = data[0][i] 93 | X_test_ = data[1][i] 94 | y_train_ = data[2][i] 95 | y_test_ = data[3][i] 96 | 97 | X_train.append(X_train_) 98 | X_test.append(X_test_) 99 | y_train.append(y_train_) 100 | y_test.append(y_test_) 101 | else: 102 | X_train = data[0] 103 | X_test = data[1] 104 | y_train = data[2] 105 | y_test = data[3] 106 | 107 | savemat('temp_data.mat',{'X_train':X_train, 'X_test':X_test,'Y_train':y_train,'Y_test':y_test}) 108 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # Data Description 2 | The training/testing data has the shape (N x T x D) with labels of shape (N x C): 3 | - N: The total number of the training/testing samples 4 | - T: The times steps (64) 5 | - D: The dimension of the data (9), which corresponds to [X_ACC, Y_ACC, Z_ACC, ROLL, PITCH, YAW, distance ahead, NoV, speed] 6 | - C: The class of driving behaviour (1) [0-> normal, 1-> aggressive, 2-> drowsy] 7 | 8 | ## Files 9 | 10 | - .pkl files are the original preprocessed data sets of [1] 11 | - .mat files are the same data as in .pkl but in Matlab file format 12 | 13 | [1] K. Saleh, M. Hossny, and S. Nahavandi, “Driving behavior classification based on sensor data fusion using LSTM recurrent neural networks,” in International Conference on Intelligent Transportation Systems (ITSC), 2017. 14 | -------------------------------------------------------------------------------- /data/motorway_dataset_window_64_proc_veh_DtA.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUC-ProAut/HDC_driving_style_classification/21f957c484684641891a63c967fd99e5922c1e72/data/motorway_dataset_window_64_proc_veh_DtA.pkl -------------------------------------------------------------------------------- /data/secondary_dataset_window_64_proc_veh_DtA.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUC-ProAut/HDC_driving_style_classification/21f957c484684641891a63c967fd99e5922c1e72/data/secondary_dataset_window_64_proc_veh_DtA.pkl -------------------------------------------------------------------------------- /data/uah_dataset.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUC-ProAut/HDC_driving_style_classification/21f957c484684641891a63c967fd99e5922c1e72/data/uah_dataset.mat -------------------------------------------------------------------------------- /eval_baseline_models.m: -------------------------------------------------------------------------------- 1 | %% classify all datasets with all baseline models 2 | % scken, 2021 3 | % Copyright (C) 2021 Chair of Automation Technology / TU Chemnitz 4 | 5 | clear all 6 | close all 7 | 8 | dim = 576; 9 | frac_scale = 6; 10 | 11 | 12 | %% secondary 13 | if 1 14 | dataset = 'secondary'; 15 | Baseline_Models 16 | end 17 | 18 | %% motorway 19 | if 1 20 | dataset = 'motorway'; 21 | Baseline_Models 22 | end 23 | 24 | %% full 25 | if 1 26 | dataset = 'full'; 27 | Baseline_Models 28 | end 29 | 30 | %% full dataset cross validation 31 | if 1 32 | dataset = 'full_crossval'; 33 | Baseline_Models 34 | end 35 | 36 | -------------------------------------------------------------------------------- /getF1Score.m: -------------------------------------------------------------------------------- 1 | function f1 = getF1Score(y_gt, y_pred) 2 | % get the weighted F1 score 3 | 4 | CM = confusionmat(double(y_gt),double(y_pred)); 5 | 6 | num_classes = size(CM,1); 7 | [TP,TN,FP,FN,f1] = deal(zeros(num_classes,1)); 8 | for c = 1:num_classes 9 | TP(c) = CM(c,c); 10 | tempMat = CM; 11 | tempMat(:,c) = []; % remove column 12 | tempMat(c,:) = []; % remove row 13 | TN(c) = sum(sum(tempMat)); 14 | FP(c) = sum(CM(:,c))-TP(c); 15 | FN(c) = sum(CM(c,:))-TP(c); 16 | end 17 | for c = 1:num_classes 18 | f1(c) = 2*TP(c)/(2*TP(c) + FP(c) + FN(c)); 19 | end 20 | 21 | h=hist(double(y_gt),0:num_classes-1)'; 22 | f1 = sum(f1.*(h/sum(h))); 23 | 24 | end -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from argparse import ArgumentParser 6 | from datetime import datetime 7 | import logging 8 | 9 | from utils import * 10 | from HDC_ANN import main_HDC 11 | from HDC_SNN import main_SNN 12 | from LSTM import main_LSTM 13 | from Concat_ANN import main_Concat_ANN 14 | 15 | 16 | # config logger 17 | logging.basicConfig(level=logging.INFO, format='%(message)s') 18 | logger = logging.getLogger('log') 19 | if not os.path.exists("./logs"): 20 | os.makedirs("./logs") 21 | logger.addHandler(logging.FileHandler('./logs/main_log.log', 'a')) 22 | stdout_handler = logging.StreamHandler() 23 | logger.addHandler(stdout_handler) 24 | 25 | 26 | if __name__ == '__main__': 27 | parser = ArgumentParser(description=__doc__) 28 | parser.add_argument('--dataset', '-d', 29 | help='Which split of the dataset to train/test the model on?' \ 30 | '(i.e. full, motorway, secondary or full_crossval)', 31 | default='full') 32 | parser.add_argument('--save_dir', '-s', 33 | help='Directory of (to be)-saved model', 34 | default='saves') 35 | parser.add_argument('--hyperparams_experiment', '-f', 36 | help='Run complete experiments (hyper-parameter analysis of HDC approach) if set to true', 37 | type=bool, 38 | default=False) 39 | parser.add_argument('--data_efficiency', 40 | help='Run data efficiency experiments if set to true', 41 | type=bool, 42 | default=False) 43 | parser.add_argument('--runtime_measurement', 44 | help='Run inference time measurement with 10 repetitions.', 45 | type=bool, 46 | default=False) 47 | parser.add_argument('--test', 48 | help='Start testing the saved model in $save_dir$ ' \ 49 | 'othewrwise, it will start the training', 50 | type=bool, 51 | default=False) 52 | parser.add_argument('--input_dim', 53 | help='Defines the input dimension of the HDC model' \ 54 | '(possible values are 512, 1024 or 2048)', 55 | type=int, 56 | default=2048) 57 | parser.add_argument('--scale', 58 | help='scaling of the scalar encoding with fractional binding ' \ 59 | '(possible values are 2, 4, 6, 8 and 10)', 60 | type=int, 61 | default=6) 62 | parser.add_argument('--encoding_dim', 63 | help='dimension of the first hidden layer (named encoding dimension)' \ 64 | ' possible values are 20, 40, 60, 80, 100)', 65 | type=int, 66 | default=40) 67 | parser.add_argument('--HDC_ANN', 68 | help='Boolean value to train the HDC network (if true, the network ' \ 69 | 'will be trained)', 70 | type=bool, 71 | default=False) 72 | parser.add_argument('--Concat_ANN', 73 | help='Boolean value to train the concatenated network (if true, the network ' \ 74 | 'will be trained)', 75 | type=bool, 76 | default=False) 77 | parser.add_argument('--LSTM', 78 | help='Boolean value to train the LSTM network (if true, the network ' \ 79 | 'will be trained)', 80 | type=bool, 81 | default=False) 82 | parser.add_argument('--HDC_SNN', 83 | help='Boolean value to train the HDC network with SNN (if true, the network ' \ 84 | 'will be trained)', 85 | type=bool, 86 | default=False) 87 | parser.add_argument('--training_volume', 88 | help='To train with only a fraction of the training data. ' \ 89 | 'Value is in range [0 1] (0 to 100 percentage).', 90 | type=float, 91 | default=1) 92 | parser.add_argument('--stat_iterations', 93 | help="Number of repetitions", 94 | default=1, 95 | type=int) 96 | args = parser.parse_args() 97 | 98 | # check if result folder exists 99 | if not os.path.exists('results/full'): 100 | os.makedirs('results/full') 101 | if not os.path.exists('results/motorway'): 102 | os.makedirs('results/motorway') 103 | if not os.path.exists('results/secondary'): 104 | os.makedirs('results/secondary') 105 | if not os.path.exists('results/full_crossval'): 106 | os.makedirs('results/full_crossval') 107 | 108 | # experiments to HDC classification 109 | n_dim = [512, 1024, 2048] 110 | scale = [2, 4, 6, 8, 10] 111 | encoding_dim = [20, 40, 60, 80, 100] 112 | 113 | training_volume = 1.0 114 | training_volume_range = [0.2, 0.4, 0.6, 0.8, 1.0] 115 | 116 | logger.info('_________________________' + str(datetime.now())) 117 | 118 | # HDC network 119 | if args.HDC_ANN: 120 | logger.info("---HDC Model---") 121 | logger.info("- Dataset: " + args.dataset) 122 | # multiple experiments based on the HDC approach 123 | if args.hyperparams_experiment: 124 | logger.info("##### Full experiment (hyper-parameter analysis)") 125 | for d in range(len(n_dim)): 126 | for s in range(len(scale)): 127 | for e in range(len(encoding_dim)): 128 | args.input_dim = n_dim[d] 129 | args.scale = scale[s] 130 | args.encoding_dim = encoding_dim[e] 131 | args.training_volume = training_volume 132 | 133 | logger.info("Training with " + str(n_dim[d]) + " " + str(scale[s]) + " " + str( 134 | encoding_dim[e]) + " training volume=" + str(training_volume)) 135 | main_HDC(args) 136 | elif args.data_efficiency: 137 | logger.info("#### Training efficiency:") 138 | logger.info("Config: input_dim = " + str(args.input_dim) + " scale = " + str(args.scale) + 139 | " encoding_dim = " + str(args.encoding_dim)) 140 | for t in range(len(training_volume_range)): 141 | args.training_volume = training_volume_range[t] 142 | logger.info("Training with training volume=" + str(training_volume_range[t])) 143 | main_HDC(args) 144 | else: 145 | logger.info("#### normal Training on " + args.dataset + ": ") 146 | logger.info("Config: input_dim = " + str(args.input_dim) + " scale = " + str(args.scale) + 147 | " encoding_dim = " + str(args.encoding_dim) + " training_volume = " + str(args.training_volume)) 148 | main_HDC(args) 149 | 150 | # concatenate intput network 151 | if args.Concat_ANN: 152 | logger.info("---Concat Model---") 153 | logger.info("- Dataset: " + args.dataset) 154 | logger.info("#### normal Training on" + args.dataset + ": ") 155 | main_Concat_ANN(args) 156 | 157 | # original LSTM network 158 | if args.LSTM: 159 | logger.info("---original LSTM Model---") 160 | logger.info("- Dataset: " + args.dataset) 161 | if args.data_efficiency: 162 | logger.info("#### Training efficiency:") 163 | for t in range(len(training_volume_range)): 164 | args.training_volume = training_volume_range[t] 165 | logger.info("Training with training volume=" + str(training_volume_range[t])) 166 | main_LSTM(args) 167 | else: 168 | logger.info("#### normal Training on " + args.dataset + ": ") 169 | main_LSTM(args) 170 | 171 | # SNN network 172 | if args.HDC_SNN: 173 | logger.info("---SNN Model---") 174 | logger.info("- Dataset: " + args.dataset) 175 | # multiple experiments based on the HDC approach 176 | if args.data_efficiency: 177 | logger.info("#### Training efficiency:") 178 | logger.info("Config: input_dim = " + str(args.input_dim) + " scale = " + str(args.scale) + 179 | " encoding_dim = " + str(args.encoding_dim) + " training_volume = " + str(args.training_volume)) 180 | for t in range(len(training_volume_range)): 181 | args.training_volume = training_volume_range[t] 182 | logger.info("Training with training volume=" + str(training_volume_range[t])) 183 | main_SNN(args) 184 | else: 185 | logger.info("#### normal Training on " + args.dataset + ": ") 186 | logger.info("Config: input_dim = " + str(args.input_dim) + " scale = " + str(args.scale) + 187 | " encoding_dim = " + str(args.encoding_dim) + " training_volume = " + str(args.training_volume)) 188 | main_SNN(args) 189 | -------------------------------------------------------------------------------- /model.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | import keras 4 | from keras import layers 5 | from keras.layers import * 6 | import keras.backend as K 7 | from utils import * 8 | 9 | 10 | def HDC_ANN(config): 11 | """HDC feed-forward model 12 | """ 13 | tf.config.optimizer.set_jit(True) 14 | encoding_dim = config.encoding_dim 15 | 16 | input = keras.Input(shape=(config.input_dim,)) 17 | dropout = layers.Dropout(config.dropout)(input) 18 | encoded = layers.Dense(encoding_dim, activation='relu')(dropout) 19 | output = layers.Dense(config.n_classes, activation='softmax')(encoded) 20 | 21 | model = keras.Model(input, output) 22 | 23 | return model 24 | 25 | def HDC_ANN_tf(input, config, init_vecs, W, biases): 26 | """ Tensorflow model of the HDC ANN """ 27 | tf.config.optimizer.set_jit(True) 28 | 29 | # preprocessing 30 | preproc = HDC_tf_preproc(input, init_vecs) 31 | 32 | # normalize data 33 | norm_data = tf.divide(preproc - config.m,config.s) 34 | 35 | # neural network for classification 36 | #hidden = tf.nn.dropout(preproc, rate=config.dropout) 37 | hidden = tf.matmul(norm_data,W['hidden']) + biases['hidden'] 38 | hidden = tf.nn.relu(hidden) 39 | 40 | output = tf.matmul(hidden,W['output']) + biases['output'] 41 | output = tf.nn.softmax(output) 42 | 43 | print("total # of trainable parameters:" + str( 44 | np.sum([np.prod(v.get_shape().as_list()) for v in tf.compat.v1.trainable_variables()]))) 45 | 46 | return output 47 | 48 | def LSTM_Network(feature_mat, config): 49 | """model a LSTM Network, (borrowed from https://github.com/KhaledSaleh/driving_behaviour_classification) 50 | it stacks 2 LSTM layers, each layer has n_hidden=32 cells 51 | and 1 output layer, it is a full connet layer 52 | argument: 53 | feature_mat: ndarray fature matrix, shape=[batch_size,time_steps,n_inputs] 54 | config: class containing config of network 55 | return: 56 | : matrix output shape [batch_size,n_classes] 57 | """ 58 | W = { 59 | 'hidden': tf.Variable(tf.random.normal([config.n_inputs, config.n_hidden]), name="W_hidden"), 60 | 'output': tf.Variable(tf.random.normal([config.n_hidden, config.n_classes]), name="W_output") 61 | } 62 | biases = { 63 | 'hidden': tf.Variable(tf.random.normal([config.n_hidden], mean=1.0), name="b_hidden"), 64 | 'output': tf.Variable(tf.random.normal([config.n_classes]), name="b_output") 65 | } 66 | 67 | feature_mat = tf.transpose(feature_mat, [1, 0, 2]) 68 | feature_mat = tf.reshape(feature_mat, [-1, config.n_inputs], name="features_reshape") 69 | hidden = tf.nn.relu(tf.matmul( 70 | feature_mat, W['hidden'] 71 | ) + biases['hidden']) 72 | hidden = tf.split(hidden, config.n_steps, 0, name="input_hidden") 73 | lstm_cell = tf.compat.v1.nn.rnn_cell.BasicLSTMCell(config.n_hidden, forget_bias=1.0) 74 | lsmt_layers = tf.compat.v1.nn.rnn_cell.MultiRNNCell([lstm_cell] * 2) 75 | outputs, _ = tf.compat.v1.nn.static_rnn(lsmt_layers, hidden, dtype=tf.float32) 76 | lstm_last_output = outputs[-1] 77 | # Linear activation 78 | final_out = tf.add(tf.matmul(lstm_last_output, W['output']), biases['output'], name="logits") 79 | 80 | print("total # of trainable parameters:" + str( 81 | np.sum([np.prod(v.get_shape().as_list()) for v in tf.compat.v1.trainable_variables()]))) 82 | 83 | return final_out 84 | 85 | def HDC_tf_preproc(inputs, init_vecs): 86 | ''' 87 | preprocessing function to create HDC vectors with tensorflow on GPU 88 | @param inputs: input tensor (#samples , #variables, #timesteps) 89 | @param init_vecs: initial hypervectors 90 | @return: context vectors 91 | ''' 92 | init_vec = init_vecs['init_vec'] 93 | sensor_ids = init_vecs['sensor_ids'] 94 | timestamps = init_vecs['timestamps'] 95 | scale = init_vecs['scale'] 96 | 97 | tf.config.optimizer.set_jit(True) 98 | # fractional binding 99 | reshaped_input = tf.tile(tf.expand_dims(inputs, axis=3), [1,1,1,init_vec.shape[0]]) 100 | 101 | expand_init_vec = tf.expand_dims(K.expand_dims(tf.transpose(init_vec * scale), axis=0), axis=0) 102 | reshaped_init_vec = tf.tile(expand_init_vec, [1,reshaped_input.shape[1],1,1]) 103 | reshaped_init_vec = tf.tile(reshaped_init_vec, [1,1,reshaped_input.shape[2],1]) 104 | # fractional binding with scale 105 | encoded_scalars = tf.math.multiply(reshaped_input, reshaped_init_vec) 106 | 107 | # bind to sensors 108 | sensor_ids = tf.transpose(sensor_ids, (1, 0)) 109 | sensor_ids = tf.expand_dims(tf.expand_dims(sensor_ids, axis=0), axis=0) 110 | sensor_ids = tf.tile(sensor_ids, [1,encoded_scalars.shape[1],1,1]) 111 | sensor_vals = tf.add(encoded_scalars, sensor_ids) 112 | 113 | # bundle all sensor vectors 114 | vals_cos = tf.cos(sensor_vals) 115 | vals_sin = tf.sin(sensor_vals) 116 | sensor_bundle_cos = (tf.reduce_sum(vals_cos, axis=2)) 117 | sensor_bundle_sin = (tf.reduce_sum(vals_sin, axis=2)) 118 | sensor_bundle = tf.math.atan2(sensor_bundle_sin, sensor_bundle_cos) 119 | 120 | # encode temporal context 121 | timestamps = tf.transpose(timestamps, (1, 0)) 122 | timestamps = tf.expand_dims(timestamps, axis=0) 123 | context_vecs = tf.add(sensor_bundle, timestamps) 124 | 125 | # bundle temporal context 126 | complex_context_cos = tf.cos(context_vecs) 127 | complex_context_sin = tf.sin(context_vecs) 128 | context_bundle_cos = (tf.reduce_sum(complex_context_cos, axis=1)) 129 | context_bundle_sin = (tf.reduce_sum(complex_context_sin, axis=1)) 130 | context_bundle = tf.math.atan2(context_bundle_sin, context_bundle_cos) 131 | 132 | return context_bundle -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | scipy==1.4.1 2 | nengo==3.1.0 3 | Keras==2.4.3 4 | tensorflow>=2.4.2 5 | nengo_dl==3.4.0 6 | matplotlib==3.1.2 7 | numpy==1.17.4 8 | scikit_learn==0.24.2 9 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pickle 3 | from sklearn.model_selection import train_test_split 4 | from sklearn.model_selection import KFold 5 | import os 6 | import tensorflow as tf 7 | from tensorflow.python.client import timeline 8 | import io 9 | from scipy.io import savemat, loadmat 10 | from tensorflow.keras.callbacks import Callback 11 | import time 12 | from tensorflow.keras.layers import Input, Dense, LSTM, multiply, concatenate, Activation, Masking, Reshape 13 | from tensorflow.keras.layers import Conv1D, BatchNormalization, GlobalAveragePooling1D, Permute, Dropout 14 | from tensorflow.python.eager.context import context, EAGER_MODE, GRAPH_MODE, eager_mode, graph_mode 15 | 16 | from model import HDC_tf_preproc 17 | 18 | def extract_batch_size(_train, step, batch_size): 19 | # Function to fetch a "batch_size" amount of data from "(X|y)_train" data. 20 | 21 | shape = list(_train.shape) 22 | shape[0] = batch_size 23 | batch_s = np.empty(shape) 24 | 25 | for i in range(batch_size): 26 | # Loop index 27 | index = ((step-1)*batch_size + i) % len(_train) 28 | batch_s[i] = _train[index] 29 | 30 | return batch_s 31 | 32 | def one_hot(y_,n_classes=-1): 33 | # Function to encode output labels from number indexes 34 | # e.g.: [[5], [0], [3]] --> [[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]] 35 | 36 | y_ = y_.reshape(len(y_)) 37 | if n_classes==-1: 38 | n_values = np.max(y_) + 1 39 | else: 40 | n_values = n_classes 41 | return np.eye(n_values)[np.array(y_, dtype=np.int32)] # Returns FLOATS 42 | 43 | def load_motorway_dataset(data_path='data'): 44 | # Function to load the motorway dataset only 45 | 46 | with open(os.path.join(data_path, 'motorway_dataset_window_64_proc_veh_DtA.pkl'), 'rb') as f: 47 | save = pickle.load(f, encoding='latin1') 48 | motorway_dataset = save['dataset'] 49 | motorway_labels = save['labels'] 50 | del save 51 | print('Motorway set', motorway_dataset.shape, motorway_labels.shape) 52 | 53 | X_train, X_test, y_train, y_test = train_test_split(motorway_dataset, motorway_labels, test_size=0.33, random_state=42) 54 | 55 | return X_train, X_test, y_train, y_test 56 | 57 | def load_secondary_dataset(data_path='data'): 58 | # Function to load the secondary dataset only 59 | 60 | with open(os.path.join(data_path,'secondary_dataset_window_64_proc_veh_DtA.pkl'), 'rb') as f: 61 | save = pickle.load(f, encoding='latin1') 62 | secondary_dataset = save['dataset'] 63 | secondary_labels = save['labels'] 64 | del save 65 | print('Secondary set', secondary_dataset.shape, secondary_labels.shape) 66 | 67 | X_train, X_test, y_train, y_test = train_test_split(secondary_dataset, secondary_labels, test_size=0.33, random_state=42) 68 | 69 | return X_train, X_test, y_train, y_test 70 | 71 | def load_full_dataset(data_path='data'): 72 | # Function to load the full dataset (motorway+secondary roads) 73 | 74 | with open(os.path.join(data_path, 'motorway_dataset_window_64_proc_veh_DtA.pkl'), 'rb') as f: 75 | save = pickle.load(f, encoding='latin1') 76 | motorway_dataset = save['dataset'] 77 | motorway_labels = save['labels'] 78 | del save 79 | print('Motorway set', motorway_dataset.shape, motorway_labels.shape) 80 | 81 | with open(os.path.join(data_path,'secondary_dataset_window_64_proc_veh_DtA.pkl'), 'rb') as f: 82 | save = pickle.load(f, encoding='latin1') 83 | secondary_dataset = save['dataset'] 84 | secondary_labels = save['labels'] 85 | del save 86 | print('Secondary set', secondary_dataset.shape, secondary_labels.shape) 87 | 88 | dataset = np.concatenate((motorway_dataset,secondary_dataset), axis=0) 89 | labels = np.concatenate((motorway_labels,secondary_labels), axis=0) 90 | 91 | X_train, X_test, y_train, y_test = train_test_split(dataset, labels, test_size=0.33, random_state=42) 92 | 93 | return X_train, X_test, y_train, y_test 94 | 95 | def get_model_summary(model): 96 | stream = io.StringIO() 97 | model.summary(print_fn=lambda x: stream.write(x + '\n')) 98 | summary_string = stream.getvalue() 99 | stream.close() 100 | return summary_string 101 | 102 | class TimingCallback(Callback): 103 | def __init__(self): 104 | self.logs=[] 105 | def on_epoch_begin(self,epoch, logs={}): 106 | self.starttime=time.time() 107 | def on_epoch_end(self, epoch, logs={}): 108 | self.logs.append(time.time()-self.starttime) 109 | 110 | 111 | def load_dataset(dataset,config): 112 | """ 113 | load the specific data set (from the data/ folder) 114 | @param dataset: specifies the data set [string] 115 | @param config: configure struct with necessary parameters [struct] 116 | @param hdc_encoded: if the data set to be loaded is to be HDC-coded [Bool] 117 | @return: set of training and test data [list] 118 | """ 119 | # load preprocessed data 120 | if dataset == "full": 121 | X_train, X_test, y_train, y_test = load_full_dataset() 122 | elif dataset == "motorway": 123 | X_train, X_test, y_train, y_test = load_motorway_dataset() 124 | elif dataset == "secondary": 125 | X_train, X_test, y_train, y_test = load_secondary_dataset() 126 | elif dataset == "full_crossval": 127 | data = loadmat('data/uah_dataset.mat') 128 | mot_data = data['motorway_dataset'] 129 | sec_data = data['secondary_dataset'] 130 | mot_label = data['motorway_labels'] 131 | sec_label = data['secondary_labels'] 132 | 133 | # creat cross-validation split (3-fold cross-validation) on the full dataset 134 | k = 3 135 | X_train = [] 136 | X_test = [] 137 | y_train = [] 138 | y_test = [] 139 | #info = data['data_info'] 140 | 141 | kf = KFold(n_splits=k) 142 | #motorway 143 | for train_idx, test_idx in kf.split(mot_data): 144 | X_train.append(mot_data[train_idx]) 145 | y_train.append(mot_label[train_idx]) 146 | X_test.append(mot_data[test_idx]) 147 | y_test.append(mot_label[test_idx]) 148 | k_idx = 0 149 | # secondary 150 | for train_idx, test_idx in kf.split(sec_data): 151 | X_train[k_idx]=np.concatenate((X_train[k_idx],sec_data[train_idx]),0) 152 | y_train[k_idx]=np.concatenate((y_train[k_idx],sec_label[train_idx]), 0) 153 | X_test[k_idx]=np.concatenate((X_test[k_idx],sec_data[test_idx]),0) 154 | y_test[k_idx]=np.concatenate((y_test[k_idx],sec_label[test_idx]), 0) 155 | k_idx+=1 156 | else: 157 | print("No valid dataset argument was set!") 158 | 159 | return X_train, X_test, y_train, y_test, config 160 | 161 | 162 | def create_HDC_vectors(config, input): 163 | """ 164 | create the HDC vectors from given input 165 | @param config: config struct 166 | @param input: inputs tensor with size m x t x v (m... number of samples, t... number of timesteps, v... number of variables) 167 | """ 168 | with graph_mode(): 169 | tf.config.optimizer.set_jit(True) 170 | # pre initialize vectors 171 | init_vec = tf.random.uniform(shape=(config.input_dim, 1), minval=-np.pi, maxval=np.pi, seed=1, 172 | dtype="float32") 173 | sensor_ids = tf.random.uniform(shape=(config.input_dim, config.n_inputs), minval=-np.pi, maxval=np.pi, 174 | seed=2, 175 | dtype="float32") 176 | timestamps = tf.random.uniform(shape=(config.input_dim, config.n_steps), minval=-np.pi, maxval=np.pi, 177 | seed=3, 178 | dtype="float32") 179 | init_vecs = {'init_vec': init_vec, 'sensor_ids': sensor_ids, 'timestamps': timestamps, 'scale':config.scale} 180 | 181 | X = tf.compat.v1.placeholder(tf.float32, [None, config.n_steps, config.n_inputs], name="X") 182 | preproc = HDC_tf_preproc(X, init_vecs) 183 | t_proc = [] 184 | traces = [] 185 | 186 | for i in range(config.n_time_measures): 187 | sess = tf.compat.v1.Session() 188 | options = tf.compat.v1.RunOptions(trace_level=tf.compat.v1.RunOptions.FULL_TRACE) 189 | run_metadata = tf.compat.v1.RunMetadata() 190 | t = time.perf_counter() 191 | output = sess.run(preproc, feed_dict={X: input}, options=options, run_metadata=run_metadata) 192 | t_proc.append((time.perf_counter() - t)) 193 | fetched_timeline = timeline.Timeline(run_metadata.step_stats) 194 | chrome_trace = fetched_timeline.generate_chrome_trace_format() 195 | traces.append(chrome_trace) 196 | preprocessing_time = np.median(t_proc) 197 | 198 | return preprocessing_time, output, traces, init_vecs --------------------------------------------------------------------------------