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.

kernel_knn_cv.py 18 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Tue May 12 12:52:15 2020
  5. @author: ljia
  6. """
  7. import numpy as np
  8. import csv
  9. import os
  10. import os.path
  11. from gklearn.utils import Dataset
  12. from sklearn.model_selection import ShuffleSplit
  13. from gklearn.preimage import MedianPreimageGenerator
  14. from gklearn.utils import normalize_gram_matrix, compute_distance_matrix
  15. from gklearn.preimage.utils import get_same_item_indices
  16. from gklearn.utils.knn import knn_classification
  17. from gklearn.preimage.utils import compute_k_dis
  18. def kernel_knn_cv(ds_name, train_examples, knn_options, mpg_options, kernel_options, ged_options, mge_options, save_results=True, load_gm='auto', dir_save='', irrelevant_labels=None, edge_required=False, cut_range=None):
  19. # 1. get dataset.
  20. print('1. getting dataset...')
  21. dataset_all = Dataset()
  22. dataset_all.load_predefined_dataset(ds_name)
  23. dataset_all.trim_dataset(edge_required=edge_required)
  24. if irrelevant_labels is not None:
  25. dataset_all.remove_labels(**irrelevant_labels)
  26. if cut_range is not None:
  27. dataset_all.cut_graphs(cut_range)
  28. if save_results:
  29. # create result files.
  30. print('creating output files...')
  31. fn_output_detail, fn_output_summary = _init_output_file_knn(ds_name, kernel_options['name'], mpg_options['fit_method'], dir_save)
  32. else:
  33. fn_output_detail, fn_output_summary = None, None
  34. # 2. compute/load Gram matrix a priori.
  35. print('2. computing/loading Gram matrix...')
  36. gram_matrix_unnorm, time_precompute_gm = _get_gram_matrix(load_gm, dir_save, ds_name, kernel_options, dataset_all)
  37. # 3. perform k-nn CV.
  38. print('3. performing k-nn CV...')
  39. if train_examples == 'k-graphs' or train_examples == 'expert' or train_examples == 'random':
  40. _kernel_knn_cv_median(dataset_all, ds_name, knn_options, mpg_options, kernel_options, mge_options, ged_options, gram_matrix_unnorm, time_precompute_gm, train_examples, save_results, dir_save, fn_output_detail, fn_output_summary)
  41. elif train_examples == 'best-dataset':
  42. _kernel_knn_cv_best_ds(dataset_all, ds_name, knn_options, kernel_options, gram_matrix_unnorm, time_precompute_gm, train_examples, save_results, dir_save, fn_output_detail, fn_output_summary)
  43. elif train_examples == 'trainset':
  44. _kernel_knn_cv_trainset(dataset_all, ds_name, knn_options, kernel_options, gram_matrix_unnorm, time_precompute_gm, train_examples, save_results, dir_save, fn_output_detail, fn_output_summary)
  45. print('\ncomplete.\n')
  46. def _kernel_knn_cv_median(dataset_all, ds_name, knn_options, mpg_options, kernel_options, mge_options, ged_options, gram_matrix_unnorm, time_precompute_gm, train_examples, save_results, dir_save, fn_output_detail, fn_output_summary):
  47. Gn = dataset_all.graphs
  48. y_all = dataset_all.targets
  49. n_neighbors, n_splits, test_size = knn_options['n_neighbors'], knn_options['n_splits'], knn_options['test_size']
  50. # get shuffles.
  51. train_indices, test_indices, train_nums, y_app = _get_shuffles(y_all, n_splits, test_size)
  52. accuracies = [[], [], []]
  53. for trial in range(len(train_indices)):
  54. print('\ntrial =', trial)
  55. train_index = train_indices[trial]
  56. test_index = test_indices[trial]
  57. G_app = [Gn[i] for i in train_index]
  58. G_test = [Gn[i] for i in test_index]
  59. y_test = [y_all[i] for i in test_index]
  60. gm_unnorm_trial = gram_matrix_unnorm[train_index,:][:,train_index].copy()
  61. # compute pre-images for each class.
  62. medians = [[], [], []]
  63. train_nums_tmp = [0] + train_nums
  64. print('\ncomputing pre-image for each class...\n')
  65. for i_class in range(len(train_nums_tmp) - 1):
  66. print(i_class + 1, 'of', len(train_nums_tmp) - 1, 'classes:')
  67. i_start = int(np.sum(train_nums_tmp[0:i_class + 1]))
  68. i_end = i_start + train_nums_tmp[i_class + 1]
  69. median_set = G_app[i_start:i_end]
  70. dataset = dataset_all.copy()
  71. dataset.load_graphs([g.copy() for g in median_set], targets=None)
  72. mge_options['update_order'] = True
  73. mpg_options['gram_matrix_unnorm'] = gm_unnorm_trial[i_start:i_end,i_start:i_end].copy()
  74. mpg_options['runtime_precompute_gm'] = 0
  75. set_median, gen_median_uo = _generate_median_preimages(dataset, mpg_options, kernel_options, ged_options, mge_options)
  76. mge_options['update_order'] = False
  77. mpg_options['gram_matrix_unnorm'] = gm_unnorm_trial[i_start:i_end,i_start:i_end].copy()
  78. mpg_options['runtime_precompute_gm'] = 0
  79. _, gen_median = _generate_median_preimages(dataset, mpg_options, kernel_options, ged_options, mge_options)
  80. medians[0].append(set_median)
  81. medians[1].append(gen_median)
  82. medians[2].append(gen_median_uo)
  83. # for each set of medians.
  84. print('\nperforming k-nn...')
  85. for i_app, G_app in enumerate(medians):
  86. # compute dis_mat between medians.
  87. dataset = dataset_all.copy()
  88. dataset.load_graphs([g.copy() for g in G_app], targets=None)
  89. gm_app_unnorm, _ = _compute_gram_matrix_unnorm(dataset, kernel_options.copy())
  90. # compute the entire Gram matrix.
  91. graph_kernel = _get_graph_kernel(dataset.copy(), kernel_options.copy())
  92. kernels_to_medians = []
  93. for g in G_app:
  94. kernels_to_median, _ = graph_kernel.compute(g, G_test, **kernel_options.copy())
  95. kernels_to_medians.append(kernels_to_median)
  96. kernels_to_medians = np.array(kernels_to_medians)
  97. gm_all = np.concatenate((gm_app_unnorm, kernels_to_medians), axis=1)
  98. gm_all = np.concatenate((gm_all, np.concatenate((kernels_to_medians.T, gram_matrix_unnorm[test_index,:][:,test_index].copy()), axis=1)), axis=0)
  99. gm_all = normalize_gram_matrix(gm_all.copy())
  100. dis_mat, _, _, _ = compute_distance_matrix(gm_all)
  101. N = len(G_app)
  102. d_app = dis_mat[range(N),:][:,range(N)].copy()
  103. d_test = np.zeros((N, len(test_index)))
  104. for i in range(N):
  105. for j in range(len(test_index)):
  106. d_test[i, j] = dis_mat[i, j]
  107. accuracies[i_app].append(knn_classification(d_app, d_test, y_app, y_test, n_neighbors, verbose=True, text=train_examples))
  108. # write result detail.
  109. if save_results:
  110. f_detail = open(dir_save + fn_output_detail, 'a')
  111. print('writing results to files...')
  112. for i, median_type in enumerate(['set-median', 'gen median', 'gen median uo']):
  113. csv.writer(f_detail).writerow([ds_name, kernel_options['name'],
  114. train_examples + ': ' + median_type, trial,
  115. knn_options['n_neighbors'],
  116. len(gm_all), knn_options['test_size'],
  117. accuracies[i][-1][0], accuracies[i][-1][1]])
  118. f_detail.close()
  119. results = {}
  120. results['ave_perf_train'] = [np.mean([i[0] for i in j], axis=0) for j in accuracies]
  121. results['std_perf_train'] = [np.std([i[0] for i in j], axis=0, ddof=1) for j in accuracies]
  122. results['ave_perf_test'] = [np.mean([i[1] for i in j], axis=0) for j in accuracies]
  123. results['std_perf_test'] = [np.std([i[1] for i in j], axis=0, ddof=1) for j in accuracies]
  124. # write result summary for each letter.
  125. if save_results:
  126. f_summary = open(dir_save + fn_output_summary, 'a')
  127. for i, median_type in enumerate(['set-median', 'gen median', 'gen median uo']):
  128. csv.writer(f_summary).writerow([ds_name, kernel_options['name'],
  129. train_examples + ': ' + median_type,
  130. knn_options['n_neighbors'],
  131. knn_options['test_size'], results['ave_perf_train'][i],
  132. results['ave_perf_test'][i], results['std_perf_train'][i],
  133. results['std_perf_test'][i], time_precompute_gm])
  134. f_summary.close()
  135. def _kernel_knn_cv_best_ds(dataset_all, ds_name, knn_options, kernel_options, gram_matrix_unnorm, time_precompute_gm, train_examples, save_results, dir_save, fn_output_detail, fn_output_summary):
  136. Gn = dataset_all.graphs
  137. y_all = dataset_all.targets
  138. n_neighbors, n_splits, test_size = knn_options['n_neighbors'], knn_options['n_splits'], knn_options['test_size']
  139. # get shuffles.
  140. train_indices, test_indices, train_nums, y_app = _get_shuffles(y_all, n_splits, test_size)
  141. accuracies = []
  142. for trial in range(len(train_indices)):
  143. print('\ntrial =', trial)
  144. train_index = train_indices[trial]
  145. test_index = test_indices[trial]
  146. G_app = [Gn[i] for i in train_index]
  147. G_test = [Gn[i] for i in test_index]
  148. y_test = [y_all[i] for i in test_index]
  149. gm_unnorm_trial = gram_matrix_unnorm[train_index,:][:,train_index].copy()
  150. # get best graph from trainset according to distance in kernel space for each class.
  151. best_graphs = []
  152. train_nums_tmp = [0] + train_nums
  153. print('\ngetting best graph from trainset for each class...')
  154. for i_class in range(len(train_nums_tmp) - 1):
  155. print(i_class + 1, 'of', len(train_nums_tmp) - 1, 'classes.')
  156. i_start = int(np.sum(train_nums_tmp[0:i_class + 1]))
  157. i_end = i_start + train_nums_tmp[i_class + 1]
  158. G_class = G_app[i_start:i_end]
  159. gm_unnorm_class = gm_unnorm_trial[i_start:i_end,i_start:i_end]
  160. gm_class = normalize_gram_matrix(gm_unnorm_class.copy())
  161. k_dis_list = []
  162. for idx in range(len(G_class)):
  163. k_dis_list.append(compute_k_dis(idx, range(0, len(G_class)), [1 / len(G_class)] * len(G_class), gm_class, withterm3=False))
  164. idx_k_dis_min = np.argmin(k_dis_list)
  165. best_graphs.append(G_class[idx_k_dis_min].copy())
  166. # perform k-nn.
  167. print('\nperforming k-nn...')
  168. # compute dis_mat between medians.
  169. dataset = dataset_all.copy()
  170. dataset.load_graphs([g.copy() for g in best_graphs], targets=None)
  171. gm_app_unnorm, _ = _compute_gram_matrix_unnorm(dataset, kernel_options.copy())
  172. # compute the entire Gram matrix.
  173. graph_kernel = _get_graph_kernel(dataset.copy(), kernel_options.copy())
  174. kernels_to_best_graphs = []
  175. for g in best_graphs:
  176. kernels_to_best_graph, _ = graph_kernel.compute(g, G_test, **kernel_options.copy())
  177. kernels_to_best_graphs.append(kernels_to_best_graph)
  178. kernels_to_best_graphs = np.array(kernels_to_best_graphs)
  179. gm_all = np.concatenate((gm_app_unnorm, kernels_to_best_graphs), axis=1)
  180. gm_all = np.concatenate((gm_all, np.concatenate((kernels_to_best_graphs.T, gram_matrix_unnorm[test_index,:][:,test_index].copy()), axis=1)), axis=0)
  181. gm_all = normalize_gram_matrix(gm_all.copy())
  182. dis_mat, _, _, _ = compute_distance_matrix(gm_all)
  183. N = len(best_graphs)
  184. d_app = dis_mat[range(N),:][:,range(N)].copy()
  185. d_test = np.zeros((N, len(test_index)))
  186. for i in range(N):
  187. for j in range(len(test_index)):
  188. d_test[i, j] = dis_mat[i, j]
  189. accuracies.append(knn_classification(d_app, d_test, y_app, y_test, n_neighbors, verbose=True, text=train_examples))
  190. # write result detail.
  191. if save_results:
  192. f_detail = open(dir_save + fn_output_detail, 'a')
  193. print('writing results to files...')
  194. csv.writer(f_detail).writerow([ds_name, kernel_options['name'],
  195. train_examples, trial,
  196. knn_options['n_neighbors'],
  197. len(gm_all), knn_options['test_size'],
  198. accuracies[-1][0], accuracies[-1][1]])
  199. f_detail.close()
  200. results = {}
  201. results['ave_perf_train'] = np.mean([i[0] for i in accuracies], axis=0)
  202. results['std_perf_train'] = np.std([i[0] for i in accuracies], axis=0, ddof=1)
  203. results['ave_perf_test'] = np.mean([i[1] for i in accuracies], axis=0)
  204. results['std_perf_test'] = np.std([i[1] for i in accuracies], axis=0, ddof=1)
  205. # write result summary for each letter.
  206. if save_results:
  207. f_summary = open(dir_save + fn_output_summary, 'a')
  208. csv.writer(f_summary).writerow([ds_name, kernel_options['name'],
  209. train_examples,
  210. knn_options['n_neighbors'],
  211. knn_options['test_size'], results['ave_perf_train'],
  212. results['ave_perf_test'], results['std_perf_train'],
  213. results['std_perf_test'], time_precompute_gm])
  214. f_summary.close()
  215. def _kernel_knn_cv_trainset(dataset_all, ds_name, knn_options, kernel_options, gram_matrix_unnorm, time_precompute_gm, train_examples, save_results, dir_save, fn_output_detail, fn_output_summary):
  216. y_all = dataset_all.targets
  217. n_neighbors, n_splits, test_size = knn_options['n_neighbors'], knn_options['n_splits'], knn_options['test_size']
  218. # compute distance matrix.
  219. gram_matrix = normalize_gram_matrix(gram_matrix_unnorm.copy())
  220. dis_mat, _, _, _ = compute_distance_matrix(gram_matrix)
  221. # get shuffles.
  222. train_indices, test_indices, _, _ = _get_shuffles(y_all, n_splits, test_size)
  223. accuracies = []
  224. for trial in range(len(train_indices)):
  225. print('\ntrial =', trial)
  226. train_index = train_indices[trial]
  227. test_index = test_indices[trial]
  228. y_app = [y_all[i] for i in train_index]
  229. y_test = [y_all[i] for i in test_index]
  230. N = len(train_index)
  231. d_app = dis_mat[train_index,:][:,train_index].copy()
  232. d_test = np.zeros((N, len(test_index)))
  233. for i in range(N):
  234. for j in range(len(test_index)):
  235. d_test[i, j] = dis_mat[train_index[i], test_index[j]]
  236. accuracies.append(knn_classification(d_app, d_test, y_app, y_test, n_neighbors, verbose=True, text=train_examples))
  237. # write result detail.
  238. if save_results:
  239. print('writing results to files...')
  240. f_detail = open(dir_save + fn_output_detail, 'a')
  241. csv.writer(f_detail).writerow([ds_name, kernel_options['name'],
  242. train_examples, trial, knn_options['n_neighbors'],
  243. len(gram_matrix), knn_options['test_size'],
  244. accuracies[-1][0], accuracies[-1][1]])
  245. f_detail.close()
  246. results = {}
  247. results['ave_perf_train'] = np.mean([i[0] for i in accuracies], axis=0)
  248. results['std_perf_train'] = np.std([i[0] for i in accuracies], axis=0, ddof=1)
  249. results['ave_perf_test'] = np.mean([i[1] for i in accuracies], axis=0)
  250. results['std_perf_test'] = np.std([i[1] for i in accuracies], axis=0, ddof=1)
  251. # write result summary for each letter.
  252. if save_results:
  253. f_summary = open(dir_save + fn_output_summary, 'a')
  254. csv.writer(f_summary).writerow([ds_name, kernel_options['name'],
  255. train_examples, knn_options['n_neighbors'],
  256. knn_options['test_size'], results['ave_perf_train'],
  257. results['ave_perf_test'], results['std_perf_train'],
  258. results['std_perf_test'], time_precompute_gm])
  259. f_summary.close()
  260. def _get_shuffles(y_all, n_splits, test_size):
  261. rs = ShuffleSplit(n_splits=n_splits, test_size=test_size, random_state=0)
  262. train_indices = [[] for _ in range(n_splits)]
  263. test_indices = [[] for _ in range(n_splits)]
  264. idx_targets = get_same_item_indices(y_all)
  265. train_nums = []
  266. keys = []
  267. for key, item in idx_targets.items():
  268. i = 0
  269. for train_i, test_i in rs.split(item): # @todo: careful when parallel.
  270. train_indices[i] += [item[idx] for idx in train_i]
  271. test_indices[i] += [item[idx] for idx in test_i]
  272. i += 1
  273. train_nums.append(len(train_i))
  274. keys.append(key)
  275. return train_indices, test_indices, train_nums, keys
  276. def _generate_median_preimages(dataset, mpg_options, kernel_options, ged_options, mge_options):
  277. mpg = MedianPreimageGenerator()
  278. mpg.dataset = dataset.copy()
  279. mpg.set_options(**mpg_options.copy())
  280. mpg.kernel_options = kernel_options.copy()
  281. mpg.ged_options = ged_options.copy()
  282. mpg.mge_options = mge_options.copy()
  283. mpg.run()
  284. return mpg.set_median, mpg.gen_median
  285. def _get_gram_matrix(load_gm, dir_save, ds_name, kernel_options, dataset_all):
  286. if load_gm == 'auto':
  287. gm_fname = dir_save + 'gram_matrix_unnorm.' + ds_name + '.' + kernel_options['name'] + '.gm.npz'
  288. gmfile_exist = os.path.isfile(os.path.abspath(gm_fname))
  289. if gmfile_exist:
  290. gmfile = np.load(gm_fname, allow_pickle=True) # @todo: may not be safe.
  291. gram_matrix_unnorm = gmfile['gram_matrix_unnorm']
  292. time_precompute_gm = float(gmfile['run_time'])
  293. else:
  294. gram_matrix_unnorm, time_precompute_gm = _compute_gram_matrix_unnorm(dataset_all, kernel_options)
  295. np.savez(dir_save + 'gram_matrix_unnorm.' + ds_name + '.' + kernel_options['name'] + '.gm', gram_matrix_unnorm=gram_matrix_unnorm, run_time=time_precompute_gm)
  296. elif not load_gm:
  297. gram_matrix_unnorm, time_precompute_gm = _compute_gram_matrix_unnorm(dataset_all, kernel_options)
  298. np.savez(dir_save + 'gram_matrix_unnorm.' + ds_name + '.' + kernel_options['name'] + '.gm', gram_matrix_unnorm=gram_matrix_unnorm, run_time=time_precompute_gm)
  299. else:
  300. gm_fname = dir_save + 'gram_matrix_unnorm.' + ds_name + '.' + kernel_options['name'] + '.gm.npz'
  301. gmfile = np.load(gm_fname, allow_pickle=True)
  302. gram_matrix_unnorm = gmfile['gram_matrix_unnorm']
  303. time_precompute_gm = float(gmfile['run_time'])
  304. return gram_matrix_unnorm, time_precompute_gm
  305. def _get_graph_kernel(dataset, kernel_options):
  306. from gklearn.utils.utils import get_graph_kernel_by_name
  307. graph_kernel = get_graph_kernel_by_name(kernel_options['name'],
  308. node_labels=dataset.node_labels,
  309. edge_labels=dataset.edge_labels,
  310. node_attrs=dataset.node_attrs,
  311. edge_attrs=dataset.edge_attrs,
  312. ds_infos=dataset.get_dataset_infos(keys=['directed']),
  313. kernel_options=kernel_options)
  314. return graph_kernel
  315. def _compute_gram_matrix_unnorm(dataset, kernel_options):
  316. from gklearn.utils.utils import get_graph_kernel_by_name
  317. graph_kernel = get_graph_kernel_by_name(kernel_options['name'],
  318. node_labels=dataset.node_labels,
  319. edge_labels=dataset.edge_labels,
  320. node_attrs=dataset.node_attrs,
  321. edge_attrs=dataset.edge_attrs,
  322. ds_infos=dataset.get_dataset_infos(keys=['directed']),
  323. kernel_options=kernel_options)
  324. gram_matrix, run_time = graph_kernel.compute(dataset.graphs, **kernel_options)
  325. gram_matrix_unnorm = graph_kernel.gram_matrix_unnorm
  326. return gram_matrix_unnorm, run_time
  327. def _init_output_file_knn(ds_name, gkernel, fit_method, dir_output):
  328. if not os.path.exists(dir_output):
  329. os.makedirs(dir_output)
  330. fn_output_detail = 'results_detail_knn.' + ds_name + '.' + gkernel + '.csv'
  331. f_detail = open(dir_output + fn_output_detail, 'a')
  332. csv.writer(f_detail).writerow(['dataset', 'graph kernel',
  333. 'train examples', 'trial', 'num neighbors', 'num graphs', 'test size',
  334. 'perf train', 'perf test'])
  335. f_detail.close()
  336. fn_output_summary = 'results_summary_knn.' + ds_name + '.' + gkernel + '.csv'
  337. f_summary = open(dir_output + fn_output_summary, 'a')
  338. csv.writer(f_summary).writerow(['dataset', 'graph kernel',
  339. 'train examples', 'num neighbors', 'test size',
  340. 'ave perf train', 'ave perf test',
  341. 'std perf train', 'std perf test', 'time precompute gm'])
  342. f_summary.close()
  343. return fn_output_detail, fn_output_summary

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