You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

model_selection_precomputed.py 21 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. def model_selection_for_precomputed_kernel(datafile, estimator,
  2. param_grid_precomputed, param_grid,
  3. model_type, NUM_TRIALS=30,
  4. datafile_y=None,
  5. extra_params=None,
  6. ds_name='ds-unknown'):
  7. """Perform model selection, fitting and testing for precomputed kernels using nested cv. Print out neccessary data during the process then finally the results.
  8. Parameters
  9. ----------
  10. datafile : string
  11. Path of dataset file.
  12. estimator : function
  13. kernel function used to estimate. This function needs to return a gram matrix.
  14. param_grid_precomputed : dictionary
  15. Dictionary with names (string) of parameters used to calculate gram matrices as keys and lists of parameter settings to try as values. This enables searching over any sequence of parameter settings. Params with length 1 will be omitted.
  16. param_grid : dictionary
  17. Dictionary with names (string) of parameters used as penelties as keys and lists of parameter settings to try as values. This enables searching over any sequence of parameter settings. Params with length 1 will be omitted.
  18. model_type : string
  19. Typr of the problem, can be regression or classification.
  20. NUM_TRIALS : integer
  21. Number of random trials of outer cv loop. The default is 30.
  22. datafile_y : string
  23. Path of file storing y data. This parameter is optional depending on the given dataset file.
  24. Examples
  25. --------
  26. >>> import numpy as np
  27. >>> import sys
  28. >>> sys.path.insert(0, "../")
  29. >>> from pygraph.utils.model_selection_precomputed import model_selection_for_precomputed_kernel
  30. >>> from pygraph.kernels.weisfeilerLehmanKernel import weisfeilerlehmankernel
  31. >>>
  32. >>> datafile = '../../../../datasets/acyclic/Acyclic/dataset_bps.ds'
  33. >>> estimator = weisfeilerlehmankernel
  34. >>> param_grid_precomputed = {'height': [0,1,2,3,4,5,6,7,8,9,10], 'base_kernel': ['subtree']}
  35. >>> param_grid = {"alpha": np.logspace(-2, 2, num = 10, base = 10)}
  36. >>>
  37. >>> model_selection_for_precomputed_kernel(datafile, estimator, param_grid_precomputed, param_grid, 'regression')
  38. """
  39. import numpy as np
  40. from matplotlib import pyplot as plt
  41. from sklearn.kernel_ridge import KernelRidge
  42. from sklearn.svm import SVC
  43. from sklearn.metrics import accuracy_score, mean_squared_error
  44. from sklearn.model_selection import KFold, train_test_split, ParameterGrid
  45. import sys
  46. sys.path.insert(0, "../")
  47. import os
  48. from os.path import basename, splitext
  49. from pygraph.utils.graphfiles import loadDataset
  50. from tqdm import tqdm
  51. tqdm.monitor_interval = 0
  52. results_dir = '../notebooks/results/' + estimator.__name__
  53. if not os.path.exists(results_dir):
  54. os.makedirs(results_dir)
  55. # open file to save all results for this dataset.
  56. with open(results_dir + '/' + ds_name + '.txt', 'w') as fresults:
  57. fresults.write('# This file contains results of ' + estimator.__name__ + ' on dataset ' + ds_name + ',\n# including gram matrices, serial numbers for gram matrix figures and performance.\n\n')
  58. # setup the model type
  59. model_type = model_type.lower()
  60. if model_type != 'regression' and model_type != 'classification':
  61. raise Exception(
  62. 'The model type is incorrect! Please choose from regression or classification.')
  63. print()
  64. print('--- This is a %s problem ---' % model_type)
  65. fresults.write('This is a %s problem.\n\n' % model_type)
  66. # Load the dataset
  67. print()
  68. print('\nI. Loading dataset from file...')
  69. dataset, y = loadDataset(datafile, filename_y=datafile_y, extra_params=extra_params)
  70. # import matplotlib.pyplot as plt
  71. # import networkx as nx
  72. # nx.draw_networkx(dataset[30])
  73. # plt.show()
  74. # Grid of parameters with a discrete number of values for each.
  75. param_list_precomputed = list(ParameterGrid(param_grid_precomputed))
  76. param_list = list(ParameterGrid(param_grid))
  77. # np.savetxt(results_name_pre + 'param_grid_precomputed.dt',
  78. # [[key, value] for key, value in sorted(param_grid_precomputed)])
  79. # np.savetxt(results_name_pre + 'param_grid.dt',
  80. # [[key, value] for key, value in sorted(param_grid)])
  81. gram_matrices = [] # a list to store gram matrices for all param_grid_precomputed
  82. gram_matrix_time = [] # a list to store time to calculate gram matrices
  83. param_list_pre_revised = [] # list to store param grids precomputed ignoring the useless ones
  84. # calculate all gram matrices
  85. print()
  86. print('2. Calculating gram matrices. This could take a while...')
  87. fresults.write('\nI. Gram matrices.\n\n')
  88. nb_gm_ignore = 0 # the number of gram matrices those should not be considered, as they may contain elements that are not numbers (NaN)
  89. for idx, params_out in enumerate(param_list_precomputed):
  90. rtn_data = estimator(dataset, **params_out)
  91. Kmatrix = rtn_data[0]
  92. current_run_time = rtn_data[1]
  93. if len(rtn_data) == 3:
  94. idx_trim = rtn_data[2] # the index of trimmed graph list
  95. y = [y[idx] for idx in idx_trim]
  96. Kmatrix_diag = Kmatrix.diagonal().copy()
  97. for i in range(len(Kmatrix)):
  98. for j in range(i, len(Kmatrix)):
  99. # if Kmatrix_diag[i] != 0 and Kmatrix_diag[j] != 0:
  100. Kmatrix[i][j] /= np.sqrt(Kmatrix_diag[i] * Kmatrix_diag[j])
  101. Kmatrix[j][i] = Kmatrix[i][j]
  102. print()
  103. if params_out == {}:
  104. print('the gram matrix is: ')
  105. fresults.write('the gram matrix is:\n\n')
  106. else:
  107. print('the gram matrix with parameters', params_out, 'is: ')
  108. fresults.write('the gram matrix with parameters %s is:\n\n' % params_out)
  109. if np.isnan(Kmatrix).any(): # if the matrix contains elements that are not numbers
  110. nb_gm_ignore += 1
  111. print('ignored, as it contains elements that are not numbers.')
  112. fresults.write('ignored, as it contains elements that are not numbers.\n\n')
  113. else:
  114. print(Kmatrix)
  115. fresults.write(np.array2string(Kmatrix, separator=',', threshold=np.inf, floatmode='unique') + '\n\n')
  116. plt.matshow(Kmatrix)
  117. plt.colorbar()
  118. fig_file_name = results_dir + '/GM[ds]' + ds_name
  119. if params_out != {}:
  120. fig_file_name += '[params]' + str(idx)
  121. plt.savefig(fig_file_name + '.eps', format='eps', dpi=300)
  122. plt.show()
  123. gram_matrices.append(Kmatrix)
  124. gram_matrix_time.append(current_run_time)
  125. param_list_pre_revised.append(params_out)
  126. print()
  127. print('{} gram matrices are calculated, {} of which are ignored.'.format(len(param_list_precomputed), nb_gm_ignore))
  128. fresults.write('{} gram matrices are calculated, {} of which are ignored.\n\n'.format(len(param_list_precomputed), nb_gm_ignore))
  129. fresults.write('serial numbers of gram matrix figure and their corresponding parameters settings:\n\n')
  130. fresults.write(''.join(['{}: {}\n'.format(idx, params_out)
  131. for idx, params_out in enumerate(param_list_precomputed)]))
  132. print()
  133. print('3. Fitting and predicting using nested cross validation. This could really take a while...')
  134. # Arrays to store scores
  135. train_pref = np.zeros(
  136. (NUM_TRIALS, len(param_list_pre_revised), len(param_list)))
  137. val_pref = np.zeros(
  138. (NUM_TRIALS, len(param_list_pre_revised), len(param_list)))
  139. test_pref = np.zeros(
  140. (NUM_TRIALS, len(param_list_pre_revised), len(param_list)))
  141. # Loop for each trial
  142. pbar = tqdm(total=NUM_TRIALS * len(param_list_pre_revised) * len(param_list),
  143. desc='calculate performance', file=sys.stdout)
  144. for trial in range(NUM_TRIALS): # Test set level
  145. # loop for each outer param tuple
  146. for index_out, params_out in enumerate(param_list_pre_revised):
  147. # split gram matrix and y to app and test sets.
  148. X_app, X_test, y_app, y_test = train_test_split(
  149. gram_matrices[index_out], y, test_size=0.1)
  150. split_index_app = [y.index(y_i) for y_i in y_app if y_i in y]
  151. # split_index_test = [y.index(y_i) for y_i in y_test if y_i in y]
  152. X_app = X_app[:, split_index_app]
  153. X_test = X_test[:, split_index_app]
  154. y_app = np.array(y_app)
  155. y_test = np.array(y_test)
  156. # loop for each inner param tuple
  157. for index_in, params_in in enumerate(param_list):
  158. inner_cv = KFold(n_splits=10, shuffle=True, random_state=trial)
  159. current_train_perf = []
  160. current_valid_perf = []
  161. current_test_perf = []
  162. # For regression use the Kernel Ridge method
  163. try:
  164. if model_type == 'regression':
  165. KR = KernelRidge(kernel='precomputed', **params_in)
  166. # loop for each split on validation set level
  167. # validation set level
  168. for train_index, valid_index in inner_cv.split(X_app):
  169. KR.fit(X_app[train_index, :]
  170. [:, train_index], y_app[train_index])
  171. # predict on the train, validation and test set
  172. y_pred_train = KR.predict(
  173. X_app[train_index, :][:, train_index])
  174. y_pred_valid = KR.predict(
  175. X_app[valid_index, :][:, train_index])
  176. y_pred_test = KR.predict(X_test[:, train_index])
  177. # root mean squared errors
  178. current_train_perf.append(
  179. np.sqrt(mean_squared_error(y_app[train_index], y_pred_train)))
  180. current_valid_perf.append(
  181. np.sqrt(mean_squared_error(y_app[valid_index], y_pred_valid)))
  182. current_test_perf.append(
  183. np.sqrt(mean_squared_error(y_test, y_pred_test)))
  184. # For clcassification use SVM
  185. else:
  186. KR = SVC(kernel='precomputed', **params_in)
  187. # loop for each split on validation set level
  188. # validation set level
  189. for train_index, valid_index in inner_cv.split(X_app):
  190. KR.fit(X_app[train_index, :]
  191. [:, train_index], y_app[train_index])
  192. # predict on the train, validation and test set
  193. y_pred_train = KR.predict(
  194. X_app[train_index, :][:, train_index])
  195. y_pred_valid = KR.predict(
  196. X_app[valid_index, :][:, train_index])
  197. y_pred_test = KR.predict(
  198. X_test[:, train_index])
  199. # root mean squared errors
  200. current_train_perf.append(accuracy_score(
  201. y_app[train_index], y_pred_train))
  202. current_valid_perf.append(accuracy_score(
  203. y_app[valid_index], y_pred_valid))
  204. current_test_perf.append(
  205. accuracy_score(y_test, y_pred_test))
  206. except ValueError:
  207. print(sys.exc_info()[0])
  208. print(params_out, params_in)
  209. # average performance on inner splits
  210. train_pref[trial][index_out][index_in] = np.mean(
  211. current_train_perf)
  212. val_pref[trial][index_out][index_in] = np.mean(
  213. current_valid_perf)
  214. test_pref[trial][index_out][index_in] = np.mean(
  215. current_test_perf)
  216. pbar.update(1)
  217. pbar.clear()
  218. # np.save(results_name_pre + 'train_pref.dt', train_pref)
  219. # np.save(results_name_pre + 'val_pref.dt', val_pref)
  220. # np.save(results_name_pre + 'test_pref.dt', test_pref)
  221. print()
  222. print('4. Getting final performance...')
  223. fresults.write('\nII. Performance.\n\n')
  224. # averages and confidences of performances on outer trials for each combination of parameters
  225. average_train_scores = np.mean(train_pref, axis=0)
  226. average_val_scores = np.mean(val_pref, axis=0)
  227. average_perf_scores = np.mean(test_pref, axis=0)
  228. # sample std is used here
  229. std_train_scores = np.std(train_pref, axis=0, ddof=1)
  230. std_val_scores = np.std(val_pref, axis=0, ddof=1)
  231. std_perf_scores = np.std(test_pref, axis=0, ddof=1)
  232. if model_type == 'regression':
  233. best_val_perf = np.amin(average_val_scores)
  234. else:
  235. best_val_perf = np.amax(average_val_scores)
  236. best_params_index = np.where(average_val_scores == best_val_perf)
  237. # find smallest val std with best val perf.
  238. best_val_stds = [std_val_scores[value][best_params_index[1][idx]] for idx, value in enumerate(best_params_index[0])]
  239. min_val_std = np.amin(best_val_stds)
  240. best_params_index = np.where(std_val_scores == min_val_std)
  241. best_params_out = [param_list_pre_revised[i] for i in best_params_index[0]]
  242. best_params_in = [param_list[i] for i in best_params_index[1]]
  243. print('best_params_out: ', best_params_out)
  244. print('best_params_in: ', best_params_in)
  245. print()
  246. print('best_val_perf: ', best_val_perf)
  247. print('best_val_std: ', min_val_std)
  248. fresults.write('best settings of hyper-params to build gram matrix: %s\n' % best_params_out)
  249. fresults.write('best settings of other hyper-params: %s\n\n' % best_params_in)
  250. fresults.write('best_val_perf: %s\n' % best_val_perf)
  251. fresults.write('best_val_std: %s\n' % min_val_std)
  252. final_performance = [average_perf_scores[value][best_params_index[1][idx]] for idx, value in enumerate(best_params_index[0])]
  253. final_confidence = [std_perf_scores[value][best_params_index[1][idx]] for idx, value in enumerate(best_params_index[0])]
  254. print('final_performance: ', final_performance)
  255. print('final_confidence: ', final_confidence)
  256. fresults.write('final_performance: %s\n' % final_performance)
  257. fresults.write('final_confidence: %s\n' % final_confidence)
  258. train_performance = [average_train_scores[value][best_params_index[1][idx]] for idx, value in enumerate(best_params_index[0])]
  259. train_std = [std_train_scores[value][best_params_index[1][idx]] for idx, value in enumerate(best_params_index[0])]
  260. print('train_performance: %s' % train_performance)
  261. print('train_std: ', train_std)
  262. fresults.write('train_performance: %s\n' % train_performance)
  263. fresults.write('train_std: %s\n\n' % train_std)
  264. print()
  265. average_gram_matrix_time = np.mean(gram_matrix_time)
  266. std_gram_matrix_time = np.std(gram_matrix_time, ddof=1)
  267. best_gram_matrix_time = [gram_matrix_time[i] for i in best_params_index[0]]
  268. ave_bgmt = np.mean(best_gram_matrix_time)
  269. std_bgmt = np.std(best_gram_matrix_time, ddof=1)
  270. print('time to calculate gram matrix with different hyper-params: {:.2f}±{:.2f}s'
  271. .format(average_gram_matrix_time, std_gram_matrix_time))
  272. print('time to calculate best gram matrix: {:.2f}±{:.2f}s'.format(ave_bgmt, std_bgmt))
  273. fresults.write('time to calculate gram matrix with different hyper-params: {:.2f}±{:.2f}s\n'
  274. .format(average_gram_matrix_time, std_gram_matrix_time))
  275. fresults.write('time to calculate best gram matrix: {:.2f}±{:.2f}s\n\n'.format(ave_bgmt, std_bgmt))
  276. # # save results to file
  277. # np.savetxt(results_name_pre + 'average_train_scores.dt',
  278. # average_train_scores)
  279. # np.savetxt(results_name_pre + 'average_val_scores', average_val_scores)
  280. # np.savetxt(results_name_pre + 'average_perf_scores.dt',
  281. # average_perf_scores)
  282. # np.savetxt(results_name_pre + 'std_train_scores.dt', std_train_scores)
  283. # np.savetxt(results_name_pre + 'std_val_scores.dt', std_val_scores)
  284. # np.savetxt(results_name_pre + 'std_perf_scores.dt', std_perf_scores)
  285. # np.save(results_name_pre + 'best_params_index', best_params_index)
  286. # np.save(results_name_pre + 'best_params_pre.dt', best_params_out)
  287. # np.save(results_name_pre + 'best_params_in.dt', best_params_in)
  288. # np.save(results_name_pre + 'best_val_perf.dt', best_val_perf)
  289. # np.save(results_name_pre + 'best_val_std.dt', best_val_std)
  290. # np.save(results_name_pre + 'final_performance.dt', final_performance)
  291. # np.save(results_name_pre + 'final_confidence.dt', final_confidence)
  292. # np.save(results_name_pre + 'train_performance.dt', train_performance)
  293. # np.save(results_name_pre + 'train_std.dt', train_std)
  294. # np.save(results_name_pre + 'gram_matrix_time.dt', gram_matrix_time)
  295. # np.save(results_name_pre + 'average_gram_matrix_time.dt',
  296. # average_gram_matrix_time)
  297. # np.save(results_name_pre + 'std_gram_matrix_time.dt',
  298. # std_gram_matrix_time)
  299. # np.save(results_name_pre + 'best_gram_matrix_time.dt',
  300. # best_gram_matrix_time)
  301. # print out as table.
  302. from collections import OrderedDict
  303. from tabulate import tabulate
  304. table_dict = {}
  305. if model_type == 'regression':
  306. for param_in in param_list:
  307. param_in['alpha'] = '{:.2e}'.format(param_in['alpha'])
  308. else:
  309. for param_in in param_list:
  310. param_in['C'] = '{:.2e}'.format(param_in['C'])
  311. table_dict['params'] = [{**param_out, **param_in}
  312. for param_in in param_list for param_out in param_list_pre_revised]
  313. table_dict['gram_matrix_time'] = ['{:.2f}'.format(gram_matrix_time[index_out])
  314. for param_in in param_list for index_out, _ in enumerate(param_list_pre_revised)]
  315. table_dict['valid_perf'] = ['{:.2f}±{:.2f}'.format(average_val_scores[index_out][index_in], std_val_scores[index_out][index_in])
  316. for index_in, _ in enumerate(param_list) for index_out, _ in enumerate(param_list_pre_revised)]
  317. table_dict['test_perf'] = ['{:.2f}±{:.2f}'.format(average_perf_scores[index_out][index_in], std_perf_scores[index_out][index_in])
  318. for index_in, _ in enumerate(param_list) for index_out, _ in enumerate(param_list_pre_revised)]
  319. table_dict['train_perf'] = ['{:.2f}±{:.2f}'.format(average_train_scores[index_out][index_in], std_train_scores[index_out][index_in])
  320. for index_in, _ in enumerate(param_list) for index_out, _ in enumerate(param_list_pre_revised)]
  321. keyorder = ['params', 'train_perf', 'valid_perf',
  322. 'test_perf', 'gram_matrix_time']
  323. print()
  324. tb_print = tabulate(OrderedDict(sorted(table_dict.items(),
  325. key=lambda i: keyorder.index(i[0]))), headers='keys')
  326. print(tb_print)
  327. fresults.write('table of performance v.s. hyper-params:\n\n%s\n\n' % tb_print)
  328. fresults.close()

A Python package for graph kernels, graph edit distances and graph pre-image problem.