@@ -0,0 +1,11 @@ | |||||
# Fit Distances | |||||
# Run xp: | |||||
``` | |||||
python3 -m pip install graphkit-learn | |||||
python3 run_xp.py | |||||
``` | |||||
# Run xp (deprecated). | |||||
export PYTHONPATH="/path/to/gedlibpy:/path/to/py-graph" | |||||
python optim_costs.py dataset output_file |
@@ -0,0 +1,43 @@ | |||||
import numpy as np | |||||
def sum_squares(a, b): | |||||
""" | |||||
Return the sum of squares of the difference between a and b, aka MSE | |||||
""" | |||||
return np.sum([(a[i] - b[i])**2 for i in range(len(a))]) | |||||
def euclid_d(x, y): | |||||
""" | |||||
1D euclidean distance | |||||
""" | |||||
return np.sqrt((x-y)**2) | |||||
def man_d(x, y): | |||||
""" | |||||
1D manhattan distance | |||||
""" | |||||
return np.abs((x-y)) | |||||
def classif_d(x, y): | |||||
""" | |||||
Function adapted to classification problems | |||||
""" | |||||
return np.array(0 if x == y else 1) | |||||
def rmse(pred, ground_truth): | |||||
import numpy as np | |||||
return np.sqrt(sum_squares(pred, ground_truth)/len(ground_truth)) | |||||
def accuracy(pred, ground_truth): | |||||
import numpy as np | |||||
return np.mean([a == b for a, b in zip(pred, ground_truth)]) | |||||
def rbf_k(D, sigma=1): | |||||
return np.exp(-(D**2)/sigma) |
@@ -0,0 +1,85 @@ | |||||
from distances import euclid_d | |||||
from gklearn.ged.util import pairwise_ged, get_nb_edit_operations | |||||
from gklearn.utils import get_iters | |||||
import sys | |||||
def compute_ged(Gi, Gj, edit_cost, method='BIPARTITE', **kwargs): | |||||
""" | |||||
Compute GED between two graph according to edit_cost | |||||
""" | |||||
ged_options = {'edit_cost': 'CONSTANT', | |||||
'method': method, | |||||
'edit_cost_constants': edit_cost} | |||||
node_labels = kwargs.get('node_labels', []) | |||||
edge_labels = kwargs.get('edge_labels', []) | |||||
dis, pi_forward, pi_backward = pairwise_ged(Gi, Gj, ged_options, repeats=10) | |||||
n_eo_tmp = get_nb_edit_operations(Gi, Gj, pi_forward, pi_backward, edit_cost='CONSTANT', node_labels=node_labels, edge_labels=edge_labels) | |||||
return dis, n_eo_tmp | |||||
def compute_ged_all_dataset(Gn, edit_cost, ed_method, **kwargs): | |||||
N = len(Gn) | |||||
G_pairs = [] | |||||
for i in range(N): | |||||
for j in range(i, N): | |||||
G_pairs.append([i, j]) | |||||
return compute_geds(G_pairs, Gn, edit_cost, ed_method, **kwargs) | |||||
def compute_geds(G_pairs, Gn, edit_cost, ed_method, **kwargs): | |||||
""" | |||||
Compute GED between all indexes in G_pairs given edit_cost | |||||
:return: ged_vec : the list of computed distances, n_edit_operations : the list of edit operations | |||||
""" | |||||
ged_vec = [] | |||||
n_edit_operations = [] | |||||
for k in get_iters(range(len(G_pairs)), desc='Computing GED', file=sys.stdout, length=len(G_pairs)): | |||||
[i, j] = G_pairs[k] | |||||
dis, n_eo_tmp = compute_ged( | |||||
Gn[i], Gn[j], edit_cost = edit_cost, method=ed_method, **kwargs) | |||||
ged_vec.append(dis) | |||||
n_edit_operations.append(n_eo_tmp) | |||||
return ged_vec, n_edit_operations | |||||
def compute_D(G_app, edit_cost, G_test=None, ed_method='BIPARTITE', **kwargs): | |||||
import numpy as np | |||||
N = len(G_app) | |||||
D_app = np.zeros((N, N)) | |||||
for i, G1 in get_iters(enumerate(G_app), desc='Computing D - app', file=sys.stdout, length=N): | |||||
for j, G2 in enumerate(G_app[i+1:], i+1): | |||||
D_app[i, j], _ = compute_ged(G1, G2, edit_cost, method=ed_method, **kwargs) | |||||
D_app[j, i] = D_app[i, j] | |||||
if (G_test is None): | |||||
return D_app, edit_cost | |||||
else: | |||||
D_test = np.zeros((len(G_test), N)) | |||||
for i, G1 in get_iters(enumerate(G_test), desc='Computing D - test', file=sys.stdout, length=len(G_test)): | |||||
for j, G2 in enumerate(G_app): | |||||
D_test[i, j], _ = compute_ged(G1, G2, edit_cost, method=ed_method, **kwargs) | |||||
return D_app, D_test, edit_cost | |||||
def compute_D_random(G_app, G_test=None, ed_method='BIPARTITE', **kwargs): | |||||
import numpy as np | |||||
edit_costs = np.random.rand(6) | |||||
return compute_D(G_app, edit_costs, G_test, ed_method=ed_method, **kwargs) | |||||
def compute_D_expert(G_app, G_test=None, ed_method='BIPARTITE', **kwargs): | |||||
edit_cost = [3, 3, 1, 3, 3, 1] | |||||
return compute_D(G_app, edit_cost, G_test, ed_method=ed_method, **kwargs) | |||||
def compute_D_fitted(G_app, y_app, G_test=None, y_distance=euclid_d, | |||||
mode='reg', unlabeled=False, ed_method='BIPARTITE', **kwargs): | |||||
from optim_costs import compute_optimal_costs | |||||
costs_optim = compute_optimal_costs( | |||||
G_app, y_app, y_distance=y_distance, | |||||
mode=mode, unlabeled=unlabeled, ed_method=ed_method, **kwargs) | |||||
return compute_D(G_app, costs_optim, G_test, ed_method=ed_method, **kwargs) |
@@ -0,0 +1,391 @@ | |||||
#!/usr/bin/env python3 | |||||
# -*- coding: utf-8 -*- | |||||
""" | |||||
Created on Thu Dec 31 10:42:55 2020 | |||||
@author: ljia | |||||
""" | |||||
import os | |||||
import numpy as np | |||||
import scipy.stats | |||||
import matplotlib.pyplot as plt | |||||
import matplotlib.gridspec as gridspec | |||||
def rounder(x, decimals): | |||||
x_strs = str(x).split('.') | |||||
if len(x_strs) == 2: | |||||
before = x_strs[0] | |||||
after = x_strs[1] | |||||
if len(after) > decimals: | |||||
if int(after[decimals]) >= 5: | |||||
after0s = '' | |||||
for c in after: | |||||
if c == '0': | |||||
after0s += '0' | |||||
elif c != '0': | |||||
break | |||||
after = after0s + str(int(after[0:decimals]) + 1)[-decimals:] | |||||
else: | |||||
after = after[0:decimals] | |||||
elif len(after) < decimals: | |||||
after += '0' * (decimals - len(after)) | |||||
return before + '.' + after | |||||
elif len(x_strs) == 1: | |||||
return x_strs[0] | |||||
def df_to_latex_table(df, replace_header=True, end_mid_line=7): | |||||
ltx = df.to_latex(index=True, escape=False, multirow=True) | |||||
# modify middle lines. | |||||
end_mid_line = str(end_mid_line) | |||||
ltx = ltx.replace('\\cline{1-' + end_mid_line + '}\n\\cline{2-' + end_mid_line + '}', '\\toprule') | |||||
ltx = ltx.replace('\\cline{2-' + end_mid_line + '}', '\\cmidrule(l){2-' + end_mid_line + '}') | |||||
# Reset dataset name. | |||||
ltx = ltx.replace('Alkane_unlabeled', 'Alkane') | |||||
ltx = ltx.replace('Vitamin_D', 'Vitamin\_D') | |||||
# modify header. | |||||
if replace_header: | |||||
i_start = ltx.find('\\begin{tabular}') | |||||
i_end = ltx.find('\\\\\n\\midrule\n') | |||||
replace = r"""\begin{tabular}{lll@{~~}c@{~~}c@{~~}c@{~~}c} | |||||
\toprule | |||||
\multirow{2}[2]{*}{\textbf{Dataset}} & \multirow{2}[2]{*}{\textbf{Distance}} & \multirow{2}[2]{*}{\textbf{Method}} & \multicolumn{2}{c}{\textbf{BIPARTITE}} & \multicolumn{2}{c}{\textbf{IPFP}} \\ | |||||
\cmidrule(lr){4-5}\cmidrule(lr){6-7} | |||||
& & & \textbf{Train errors} & \textbf{Test errors} & \textbf{Train errors} & \textbf{Test errors} \\ | |||||
\midrule | |||||
""" | |||||
ltx = ltx.replace(ltx[i_start:i_end+12], replace, 1) | |||||
# | |||||
# # add row numbers. | |||||
# ltx = ltx.replace('lllllllll', 'lllllllll|@{\\makebox[2em][r]{\\textit{\\rownumber\\space}}}', 1) | |||||
# ltx = replace_nth(ltx, '\\\\\n', '\\gdef\\rownumber{\\stepcounter{magicrownumbers}\\arabic{magicrownumbers}} \\\\\n', 1) | |||||
return ltx | |||||
def beautify_df(df): | |||||
# df = df.sort_values(by=['Datasets', 'Graph Kernels']) | |||||
# df = df.set_index(['Datasets', 'Graph Kernels', 'Algorithms']) | |||||
# # index = pd.MultiIndex.from_frame(df[['Datasets', 'Graph Kernels', 'Algorithms']]) | |||||
# bold the best results. | |||||
for ds in df.index.get_level_values('Dataset').unique(): | |||||
for gk in df.loc[ds].index.get_level_values('Distance').unique(): | |||||
for label, col in df.loc[(ds, gk)].items(): | |||||
min_val = np.inf | |||||
min_indices = [] | |||||
min_labels = [] | |||||
for index, row in col.items(): | |||||
value = row | |||||
if value != '-': | |||||
mean, interval = value.split('$\\pm$') | |||||
mean = float(mean.strip('/same')) | |||||
if mean < min_val: | |||||
min_val = mean | |||||
min_indices = [index] | |||||
min_labels = [label] | |||||
elif mean == min_val: | |||||
min_indices.append(index) | |||||
min_labels.append(label) | |||||
for idx, index in enumerate(min_indices): | |||||
df.loc[(ds, gk, index), min_labels[idx]] = '\\textbf{' + df.loc[(ds, gk, index), min_labels[idx]] + '}' | |||||
return df | |||||
def params_to_latex_table(results): | |||||
import pandas as pd | |||||
# Create df table. | |||||
row_indices = pd.MultiIndex.from_product([Dataset_list, Edit_Cost_List, Dis_List], names=['Dataset', 'Edit cost', 'Distance']) | |||||
df = pd.DataFrame(columns=['$c_{ni}$', '$c_{nr}$', '$c_{ns}$', '$c_{ei}$', '$c_{er}$', '$c_{es}$'], index=row_indices) | |||||
# Set data. | |||||
for idx_r, row in df.iterrows(): | |||||
for idx, (idx_c, col) in enumerate(row.items()): | |||||
key = (idx_r[0], idx_r[2], idx_r[1]) | |||||
if key in results and results[key] is not None: | |||||
# if results[key][idx] != 0: | |||||
df.loc[idx_r, idx_c] = results[key][idx] | |||||
# else: | |||||
# df.loc[idx_r, idx_c] = '-' | |||||
else: | |||||
df.loc[idx_r, idx_c] = '-' | |||||
# df = beautify_df(df) | |||||
ltx = df_to_latex_table(df, replace_header=False, end_mid_line=9) | |||||
return ltx | |||||
def results_to_latex_table(results): | |||||
import pandas as pd | |||||
# Create df table. | |||||
col_indices = pd.MultiIndex.from_product([Edit_Cost_List, ['Train errors', 'Test errors']]) | |||||
row_indices = pd.MultiIndex.from_product([Dataset_list, Dis_List, ['random', 'expert', 'fitted']], names=['Dataset', 'Distance', 'Method']) | |||||
df = pd.DataFrame(columns=col_indices, index=row_indices) | |||||
# Set data. | |||||
for idx_r, row in df.iterrows(): | |||||
for idx_c, col in row.items(): | |||||
key = (idx_r[0], idx_r[1], idx_c[0]) | |||||
if key in results and results[key] is not None: | |||||
mean = results[key][idx_r[2]]['mean'] | |||||
mean = mean[0] if idx_c[1] == 'Train errors' else mean[1] | |||||
interval = results[key][idx_r[2]]['interval'] | |||||
interval = interval[0] if idx_c[1] == 'Train errors' else interval[1] | |||||
df.loc[idx_r, idx_c] = rounder(mean, 2) + '$\pm$' + rounder(interval, 2) | |||||
else: | |||||
df.loc[idx_r, idx_c] = '-' | |||||
df = beautify_df(df) | |||||
ltx = df_to_latex_table(df) | |||||
return ltx | |||||
def get_params(results): | |||||
edit_costs = [[] for i in range(6)] | |||||
for result in results['results']: | |||||
ed = result['fitted']['edit_costs'] | |||||
for i, e in enumerate(ed): | |||||
edit_costs[i].append(e) | |||||
for i, ed in enumerate(edit_costs): | |||||
mean, interval = mean_confidence_interval(ed) | |||||
if mean == 0: | |||||
edit_costs[i] = '-' | |||||
else: | |||||
edit_costs[i] = rounder(mean, 2) + '$\pm$' + rounder(interval, 2) | |||||
return edit_costs | |||||
def print_bars(ax, p, title, y_label='RMSE', export_filename=None): | |||||
palette = plt.get_cmap('Set1') # ['red', 'blue', 'green'] | |||||
# width of the bars | |||||
barWidth = 0.1 | |||||
gap = 0.2 | |||||
# The x position of bars | |||||
# nb_xp = len(p.keys()) | |||||
# r = np.arange(2) | |||||
r = [0, gap + barWidth * 3] | |||||
# r = [0 - barWidth, nb_xp * barWidth + gap * 0.5 - barWidth] | |||||
#print(r) | |||||
for i, xp in enumerate(p.keys()): | |||||
bars = p[xp]['mean'] | |||||
y_err = p[xp]['interval'] | |||||
# Create blue bars | |||||
r_cur = [x + barWidth * (i - 1) * 1.03 for x in r] | |||||
plt.bar(r_cur, | |||||
bars, width=barWidth, color=palette(i), | |||||
edgecolor='black', linewidth=0.2, | |||||
yerr=y_err, error_kw=dict(lw=0.5, capsize=3, capthick=0.5), | |||||
label=xp) | |||||
# general layout | |||||
ax.set_xticks(r) | |||||
ax.set_xticklabels(['train', 'test'] ) # ['train errors', 'test errors']) | |||||
ax.xaxis.set_ticks_position('none') | |||||
ax.set_ylabel(y_label) | |||||
# ax.legend() | |||||
ax.set_title(title) | |||||
if (export_filename is not None): | |||||
print(export_filename) | |||||
plt.savefig(export_filename) | |||||
def print_table_results(results_by_xp): | |||||
from tabulate import tabulate | |||||
tab = [] | |||||
tab.append(["Method", "App","Test"]) | |||||
#setups = ["random","expert","fitted"] | |||||
for i,setup in enumerate(results_by_xp.keys()): | |||||
current_line = [setup] | |||||
p = results_by_xp[setup] | |||||
current_line.append(f"{p['mean'][0]:.2f} +- {p['interval'][0]:.2f}") | |||||
current_line.append(f"{p['mean'][1]:.2f} +- {p['interval'][1]:.2f}") | |||||
tab.append(current_line) | |||||
print(tabulate(tab, headers="firstrow")) | |||||
def mean_confidence_interval(data, confidence=0.95): | |||||
a = 1.0 * np.array(data) | |||||
n = len(a) | |||||
m, se = np.mean(a), scipy.stats.sem(a) | |||||
h = se * scipy.stats.t.ppf((1 + confidence) / 2., n - 1) | |||||
return m, h | |||||
def compute_perf(results, app_or_test): | |||||
return mean_confidence_interval(results[app_or_test]) | |||||
def compute_displayable_results(results_by_xp): | |||||
p = {} | |||||
for xp in results_by_xp.keys(): | |||||
p[xp] = {} | |||||
p[xp]["mean"] = [0] * 2 | |||||
p[xp]["interval"] = [0] * 2 | |||||
p[xp]["mean"][0], p[xp]["interval"][0] = compute_perf(results_by_xp[xp], 'app') | |||||
p[xp]["mean"][1], p[xp]["interval"][1] = compute_perf(results_by_xp[xp], 'test') | |||||
return p | |||||
def organize_results_by_cost_settings(results, xps): | |||||
all_results = results["results"] | |||||
results_by_xp = {} | |||||
for xp in xps: | |||||
results_xp = { | |||||
'app' :[], | |||||
'test' : [] | |||||
} | |||||
for i, split_res in enumerate(all_results): | |||||
results_xp['app'].append(split_res[xp]['perf_app']) | |||||
results_xp['test'].append(split_res[xp]['perf_test']) | |||||
results_by_xp[xp] = results_xp | |||||
return results_by_xp | |||||
def plot_a_task(ax, ds_name, edit_cost, distance, title, y_label): | |||||
# Load data. | |||||
root_dir = '/media/ljia/DATA/research-repo/codes/Linlin/graphkit-learn/gklearn/experiments/thesis/ged/fit_distances/outputs/' | |||||
fn = root_dir + 'results.' + '.'.join([ds_name, edit_cost, distance]) + '.pkl' | |||||
if os.path.isfile(fn): | |||||
with open(fn, 'rb') as file: | |||||
results = pickle.load(file) | |||||
else: | |||||
return None, None | |||||
# print(results.keys()) | |||||
# print(results['y_distance']) | |||||
# print(results['dataset']) | |||||
# print(results['params']) | |||||
# #print(results['mode']) | |||||
# print(len(results['results'])) | |||||
# len(results['results'][0]) | |||||
# print(results['results'][0].keys()) | |||||
# ### Schema Xp | |||||
# # acyclic_results['results'] est une liste qui contient les resultats de test et train/valid sur 10 split randoms. | |||||
# # Pour chaque split, results['results'][i] est un dict qui contient chaque xp avec le split i | |||||
# print(results["results"][0]['random'].keys()) | |||||
# xp = results["results"][4]['fitted'] | |||||
# for k in xp.keys(): | |||||
# print(f"{k} : {xp[k]}") | |||||
# i=4 | |||||
# print(results["results"][i]['random']['perf_test']) | |||||
# print(results["results"][i]['expert']['perf_test']) | |||||
# print(results["results"][i]['fitted']['perf_test']) | |||||
# #print(xp['clf'].cv_results_) | |||||
# Compute data. | |||||
xps = ["random", "expert", "fitted"] | |||||
results_by_xp = organize_results_by_cost_settings(results, xps) | |||||
p = compute_displayable_results(results_by_xp) | |||||
# print_bars(p,'KNN with CV and y_distance = {0}'.format(results['y_distance']),export_filename=export_filename) | |||||
print_bars(ax, p, title, y_label=y_label, export_filename=None) | |||||
c = get_params(results) | |||||
return p, c | |||||
def set_figure(nb_rows): | |||||
#plt.rc('font', size=SMALL_SIZE) # controls default text sizes | |||||
# plt.rc('axes', titlesize=15) # fontsize of the axes title | |||||
# plt.rc('axes', labelsize=15) # fontsize of the x and y labels | |||||
# plt.rc('xtick', labelsize=15) # fontsize of the tick labels | |||||
# plt.rc('ytick', labelsize=15) # fontsize of the tick labels | |||||
# plt.rc('legend', fontsize=15) # legend fontsize | |||||
# plt.rc('figure', titlesize=15) # fontsize of the figure title | |||||
#fig, _ = plt.subplots(2, 2, figsize=(13, 12)) | |||||
#ax1 = plt.subplot(221) | |||||
#ax2 = plt.subplot(222) | |||||
#ax3 = plt.subplot(223) | |||||
#ax4 = plt.subplot(224) | |||||
fig = plt.figure(figsize=(11, 2.12 * nb_rows + 0.56)) | |||||
ax = fig.add_subplot(111) # The big subplot for common labels | |||||
# Turn off axis lines and ticks of the big subplot | |||||
ax.spines['top'].set_color('none') | |||||
ax.spines['bottom'].set_color('none') | |||||
ax.spines['left'].set_color('none') | |||||
ax.spines['right'].set_color('none') | |||||
ax.tick_params(labelcolor='w', top='off', bottom='off', left='off', right='off') | |||||
ax.xaxis.set_ticks_position('none') | |||||
ax.yaxis.set_ticks_position('none') | |||||
# Set common labels | |||||
#ax.set_xlabel('accuracy(%)') | |||||
ax.yaxis.set_label_coords(-0.105, 0.5) | |||||
ax.set_ylabel('RMSE') | |||||
ax.yaxis.set_label_coords(-0.07, 0.5) | |||||
return fig | |||||
if __name__ == '__main__': | |||||
from sklearn.model_selection import ParameterGrid | |||||
import pickle | |||||
# Get task grid. | |||||
Edit_Cost_List = ['BIPARTITE', 'IPFP'] | |||||
Dataset_list = ['Alkane_unlabeled', 'Acyclic', 'Chiral', 'Vitamin_D', | |||||
'Steroid'][0:2] | |||||
Dis_List = ['euclidean', 'manhattan'] | |||||
# row_grid = ParameterGrid({'edit_cost': Edit_Cost_List[0:], | |||||
# 'distance': Dis_List[0:]}) | |||||
# show by edit costs then by distances. | |||||
row_grid_list = [] | |||||
for i in Edit_Cost_List[0:]: | |||||
for j in Dis_List[0:]: | |||||
row_grid_list.append({'edit_cost': i, 'distance': j}) | |||||
# Compute and plot. | |||||
fig = set_figure(len(Dataset_list)) | |||||
gs = gridspec.GridSpec(len(Dataset_list), len(row_grid_list)) | |||||
gs.update(hspace=0.3) | |||||
results = {} | |||||
params = {} | |||||
for row, ds_name in enumerate(Dataset_list): | |||||
for col, contents in enumerate(row_grid_list): | |||||
ax = fig.add_subplot(gs[row, col]) | |||||
y_label = (ds_name[:-10] if ds_name.endswith('_unlabeled') else ds_name) if col == 0 else '' | |||||
title = contents['edit_cost'] + ', ' + contents['distance'] if row == 0 else '' | |||||
p, c = plot_a_task(ax, ds_name, contents['edit_cost'], contents['distance'], title, y_label) | |||||
results[(ds_name, contents['distance'], contents['edit_cost'])] = p | |||||
params[(ds_name, contents['distance'], contents['edit_cost'])] = c | |||||
if col == 0 and row == 0: | |||||
handles, labels = ax.get_legend_handles_labels() | |||||
# Show graphic | |||||
size = fig.get_size_inches() | |||||
fig.subplots_adjust(bottom=0.56 / size[1]) | |||||
fig.legend(handles, labels, loc='lower center', ncol=3, frameon=False) # , ncol=5, labelspacing=0.1, handletextpad=0.4, columnspacing=0.6) | |||||
plt.savefig('ged_fit_distance_results.eps', format='eps', dpi=300, transparent=True, | |||||
bbox_inches='tight') | |||||
plt.show() | |||||
# Convert results to latex table. | |||||
ltable_perf = results_to_latex_table(results) | |||||
ltable_params = params_to_latex_table(params) | |||||
print(ltable_perf) |
@@ -0,0 +1,108 @@ | |||||
from distances import euclid_d | |||||
def split_data(D, y, train_index, test_index): | |||||
D_app = [D[i] for i in train_index] | |||||
D_test = [D[i] for i in test_index] | |||||
y_app = [y[i] for i in train_index] | |||||
y_test = [y[i] for i in test_index] | |||||
return D_app, D_test, y_app, y_test | |||||
def evaluate_D(D_app, y_app, D_test, y_test, mode='reg'): | |||||
from sklearn.neighbors import KNeighborsRegressor, KNeighborsClassifier | |||||
from distances import rmse, accuracy | |||||
from sklearn.model_selection import GridSearchCV | |||||
if (mode == 'reg'): | |||||
knn = KNeighborsRegressor(metric='precomputed') | |||||
scoring = 'neg_root_mean_squared_error' | |||||
perf_eval = rmse | |||||
else: | |||||
knn = KNeighborsClassifier(metric='precomputed') | |||||
scoring = 'accuracy' | |||||
perf_eval = accuracy | |||||
grid_params = { | |||||
'n_neighbors': [3, 5, 7, 9, 11] | |||||
} | |||||
clf = GridSearchCV(knn, param_grid=grid_params, | |||||
scoring=scoring, | |||||
cv=5, return_train_score=True, refit=True) | |||||
clf.fit(D_app, y_app) | |||||
y_pred_app = clf.predict(D_app) | |||||
y_pred_test = clf.predict(D_test) | |||||
return perf_eval(y_pred_app, y_app), perf_eval(y_pred_test, y_test), clf | |||||
def xp_knn(Gn, y_all, y_distance=euclid_d, | |||||
mode='reg', unlabeled=False, ed_method='BIPARTITE', **kwargs): | |||||
''' | |||||
Perform a knn regressor on given dataset | |||||
''' | |||||
from sklearn.model_selection import ShuffleSplit, StratifiedShuffleSplit | |||||
from ged import compute_D_random, compute_D_expert | |||||
from ged import compute_D_fitted | |||||
stratified = False | |||||
if mode == 'classif': | |||||
stratified = True | |||||
if stratified: | |||||
rs = StratifiedShuffleSplit(n_splits=10, test_size=.1) | |||||
else: | |||||
rs = ShuffleSplit(n_splits=10, test_size=.1) | |||||
if stratified: | |||||
split_scheme = rs.split(Gn, y_all) | |||||
else: | |||||
split_scheme = rs.split(Gn) | |||||
results = [] | |||||
i = 1 | |||||
for train_index, test_index in split_scheme: | |||||
print() | |||||
print("Split {0}/{1}".format(i, 10)) | |||||
i = i + 1 | |||||
cur_results = {} | |||||
# Get splitted data | |||||
G_app, G_test, y_app, y_test = split_data(Gn, y_all, | |||||
train_index, test_index) | |||||
cur_results['y_app'] = y_app | |||||
cur_results['y_test'] = y_test | |||||
# Feed distances will all methods to compare | |||||
distances = {} | |||||
distances['random'] = compute_D_random(G_app, G_test, ed_method, **kwargs) | |||||
distances['expert'] = compute_D_expert(G_app, G_test, ed_method, **kwargs) | |||||
distances['fitted'] = compute_D_fitted( | |||||
G_app, y_app, G_test, | |||||
y_distance=y_distance, | |||||
mode=mode, unlabeled=unlabeled, ed_method=ed_method, | |||||
**kwargs) | |||||
for setup in distances.keys(): | |||||
print("{0} Mode".format(setup)) | |||||
setup_results = {} | |||||
D_app, D_test, edit_costs = distances[setup] | |||||
setup_results['D_app'] = D_app | |||||
setup_results['D_test'] = D_test | |||||
setup_results['edit_costs'] = edit_costs | |||||
print(edit_costs) | |||||
perf_app, perf_test, clf = evaluate_D( | |||||
D_app, y_app, D_test, y_test, mode) | |||||
setup_results['perf_app'] = perf_app | |||||
setup_results['perf_test'] = perf_test | |||||
setup_results['clf'] = clf | |||||
print( | |||||
"Learning performance with {1} costs : {0:.2f}".format( | |||||
perf_app, setup)) | |||||
print( | |||||
"Test performance with {1} costs : {0:.2f}".format( | |||||
perf_test, setup)) | |||||
cur_results[setup] = setup_results | |||||
results.append(cur_results) | |||||
return results |
@@ -0,0 +1,66 @@ | |||||
def loglik(X, y, w): | |||||
import numpy as np | |||||
return np.sum(-y*(X@w) + np.log(1+np.exp(X@w))) | |||||
def reg_log(X, y, ite_max=100, lbd=1e-12, pos_contraint=False): | |||||
""" | |||||
y \in 1,0 | |||||
""" | |||||
import numpy as np | |||||
def proj_on_pos(w): | |||||
return np.array([x if x > 0 else 0 for x in w]) | |||||
tol = 1e-4 | |||||
N, d = X.shape | |||||
y = np.array(y) | |||||
w = np.zeros(d) # see 4.4 of ESLII | |||||
weights = [w] | |||||
J = [loglik(X, y, w)] | |||||
# print(f"J[0] = {J[0]}") | |||||
old_J = J[0] + 1 | |||||
conv = False | |||||
i = 0 | |||||
while(not conv): | |||||
i = i + 1 | |||||
Xw = X @ w | |||||
p = np.exp(Xw)/(1+np.exp(Xw)) | |||||
W = np.diag(p) | |||||
regul = lbd*np.identity(d) | |||||
descent = np.linalg.solve(X.T @ W @ X + regul, X.T@(y-p)) | |||||
# print(f"descent: {descent}") | |||||
step = 1 | |||||
update = 0.1 | |||||
cur_w = w+step*descent | |||||
if pos_contraint: | |||||
cur_w = proj_on_pos(cur_w) | |||||
# print(f"cur_w : {cur_w}") | |||||
# print(f"J : {loglik(X,y,cur_w)}") | |||||
while (loglik(X, y, cur_w) > J[-1]): | |||||
step = step*update | |||||
cur_w = w + step*descent | |||||
if pos_contraint: | |||||
cur_w = proj_on_pos(cur_w) | |||||
# print(f"step : {step}") | |||||
w = cur_w | |||||
J.append(loglik(X, y, w)) | |||||
weights.append(w) | |||||
if (i > ite_max): | |||||
conv = True | |||||
if ((old_J - J[-1]) < tol): | |||||
conv = True | |||||
else: | |||||
old_J = J[-1] | |||||
return w, J, weights |
@@ -0,0 +1,136 @@ | |||||
from ged import compute_geds | |||||
from distances import sum_squares, euclid_d | |||||
import numpy as np | |||||
# from tqdm import tqdm | |||||
import sys | |||||
# sys.path.insert(0, "../") | |||||
def optimize_costs_unlabeled(nb_cost_mat, dis_k_vec): | |||||
""" | |||||
Optimize edit costs to fit dis_k_vec according to edit operations in nb_cost_mat | |||||
! take care that nb_cost_mat do not contains 0 lines | |||||
:param nb_cost_mat: \in \mathbb{N}^{N x 6} encoding the number of edit operations for each pair of graph | |||||
:param dis_k_vec: The N distances to fit | |||||
""" | |||||
import cvxpy as cp | |||||
import numpy as np | |||||
MAX_SAMPLE = 1000 | |||||
nb_cost_mat_m = np.array([[x[0], x[1], x[3], x[4]] for x in nb_cost_mat]) | |||||
dis_k_vec = np.array(dis_k_vec) | |||||
# dis_k_vec_norm = dis_k_vec/np.max(dis_k_vec) | |||||
# import pickle | |||||
# pickle.dump([nb_cost_mat, dis_k_vec], open('debug', 'wb')) | |||||
N = nb_cost_mat_m.shape[0] | |||||
sub_sample = np.random.permutation(np.arange(N)) | |||||
sub_sample = sub_sample[:MAX_SAMPLE] | |||||
x = cp.Variable(nb_cost_mat_m.shape[1]) | |||||
cost = cp.sum_squares((nb_cost_mat_m[sub_sample, :] @ x) - dis_k_vec[sub_sample]) | |||||
prob = cp.Problem(cp.Minimize(cost), [x >= 0]) | |||||
prob.solve() | |||||
edit_costs_new = [x.value[0], x.value[1], 0, x.value[2], x.value[3], 0] | |||||
edit_costs_new = [xi if xi > 0 else 0 for xi in edit_costs_new] | |||||
residual = prob.value | |||||
return edit_costs_new, residual | |||||
def optimize_costs_classif_unlabeled(nb_cost_mat, Y): | |||||
""" | |||||
Optimize edit costs to fit dis_k_vec according to edit operations in | |||||
nb_cost_mat | |||||
! take care that nb_cost_mat do not contains 0 lines | |||||
:param nb_cost_mat: \in \mathbb{N}^{N x 6} encoding the number of edit | |||||
operations for each pair of graph | |||||
:param dis_k_vec: {-1,1}^N vector of common classes | |||||
""" | |||||
# import cvxpy as cp | |||||
from ml import reg_log | |||||
# import pickle | |||||
# pickle.dump([nb_cost_mat, Y], open('debug', 'wb')) | |||||
nb_cost_mat_m = np.array([[x[0], x[1], x[3], x[4]] | |||||
for x in nb_cost_mat]) | |||||
w, J, _ = reg_log(nb_cost_mat_m, Y, pos_contraint=True) | |||||
edit_costs_new = [w[0], w[1], 0, w[2], w[3], 0] | |||||
residual = J[-1] | |||||
return edit_costs_new, residual | |||||
def optimize_costs_classif(nb_cost_mat, Y): | |||||
""" | |||||
Optimize edit costs to fit dis_k_vec according to edit operations in nb_cost_mat | |||||
! take care that nb_cost_mat do not contains 0 lines | |||||
:param nb_cost_mat: \in \mathbb{N}^{N x 6} encoding the number of edit operations for each pair of graph | |||||
:param dis_k_vec: {-1,1}^N vector of common classes | |||||
""" | |||||
#import pickle | |||||
# pickle.dump([nb_cost_mat, Y], open("test.pickle", "wb")) | |||||
from ml import reg_log | |||||
w, J, _ = reg_log(nb_cost_mat, Y, pos_contraint=True) | |||||
return w, J[-1] | |||||
def optimize_costs(nb_cost_mat, dis_k_vec): | |||||
""" | |||||
Optimize edit costs to fit dis_k_vec according to edit operations in nb_cost_mat | |||||
! take care that nb_cost_mat do not contains 0 lines | |||||
:param nb_cost_mat: \in \mathbb{N}^{N x 6} encoding the number of edit operations for each pair of graph | |||||
:param dis_k_vec: The N distances to fit | |||||
""" | |||||
import cvxpy as cp | |||||
x = cp.Variable(nb_cost_mat.shape[1]) | |||||
cost = cp.sum_squares((nb_cost_mat @ x) - dis_k_vec) | |||||
constraints = [x >= [0.01 for i in range(nb_cost_mat.shape[1])], | |||||
np.array([1.0, 1.0, -1.0, 0.0, 0.0, 0.0]).T@x >= 0.0, | |||||
np.array([0.0, 0.0, 0.0, 1.0, 1.0, -1.0]).T@x >= 0.0] | |||||
prob = cp.Problem(cp.Minimize(cost), constraints) | |||||
prob.solve() | |||||
edit_costs_new = x.value | |||||
residual = prob.value | |||||
return edit_costs_new, residual | |||||
def compute_optimal_costs(G, y, init_costs=[3, 3, 1, 3, 3, 1], | |||||
y_distance=euclid_d, | |||||
mode='reg', unlabeled=False, | |||||
ed_method='BIPARTITE', | |||||
**kwargs): | |||||
N = len(y) | |||||
G_pairs = [] | |||||
distances_vec = [] | |||||
for i in range(N): | |||||
for j in range(i+1, N): | |||||
G_pairs.append([i, j]) | |||||
distances_vec.append(y_distance(y[i], y[j])) | |||||
ged_vec_init, n_edit_operations = compute_geds(G_pairs, G, init_costs, ed_method, **kwargs) | |||||
residual_list = [sum_squares(ged_vec_init, distances_vec)] | |||||
if (mode == 'reg'): | |||||
if unlabeled: | |||||
method_optim = optimize_costs_unlabeled | |||||
else: | |||||
method_optim = optimize_costs | |||||
elif (mode == 'classif'): | |||||
if unlabeled: | |||||
method_optim = optimize_costs_classif_unlabeled | |||||
else: | |||||
method_optim = optimize_costs_classif | |||||
ite_max = 5 | |||||
for i in range(ite_max): | |||||
print('ite', i + 1, '/', ite_max, ':') | |||||
# compute GEDs and numbers of edit operations. | |||||
edit_costs_new, residual = method_optim( | |||||
np.array(n_edit_operations), distances_vec) | |||||
ged_vec, n_edit_operations = compute_geds(G_pairs, G, edit_costs_new, ed_method, **kwargs) | |||||
residual_list.append(sum_squares(ged_vec, distances_vec)) | |||||
return edit_costs_new |
@@ -0,0 +1,100 @@ | |||||
import sys | |||||
def run_xp(ds_name, output_file, unlabeled, mode, y_distance, ed_method): | |||||
from gklearn.dataset import Dataset | |||||
from gklearn.experiments import DATASET_ROOT | |||||
from learning import xp_knn | |||||
ds = Dataset(ds_name, root=DATASET_ROOT, verbose=True) | |||||
ds.remove_labels(node_attrs=ds.node_attrs, edge_attrs=ds.edge_attrs) # @todo: ged can not deal with sym and unsym labels. | |||||
Gn = ds.graphs | |||||
y_all = ds.targets | |||||
resu = {} | |||||
resu['y_distance'] = y_distance | |||||
resu['dataset'] = ds_name | |||||
unlabeled = (len(ds.node_labels) == 0 and len(ds.edge_labels) == 0) | |||||
results = xp_knn(Gn, y_all, y_distance=y_distances[y_distance], | |||||
mode=mode, | |||||
unlabeled=unlabeled, ed_method=ed_method, | |||||
node_labels=ds.node_labels, edge_labels=ds.edge_labels) | |||||
resu['results'] = results | |||||
resu['unlabeled'] = unlabeled | |||||
resu['mode'] = mode | |||||
resu['ed_method'] = ed_method | |||||
pickle.dump(resu, open(output_result, 'wb')) | |||||
return output_result | |||||
def run_from_args(): | |||||
import argparse | |||||
parser = argparse.ArgumentParser() | |||||
parser.add_argument("dataset", help="path to / name of the dataset to predict") | |||||
parser.add_argument( | |||||
"output_file", help="path to file which will contains the results") | |||||
parser.add_argument("-u", "--unlabeled", help="Specify that the dataset is unlabeled graphs", | |||||
action="store_true") | |||||
parser.add_argument("-m", "--mode", type=str, choices=['reg', 'classif'], | |||||
help="Specify if the dataset a classification or regression problem") | |||||
parser.add_argument("-y", "--y_distance", type=str, choices=['euclidean', 'manhattan', 'classif'], | |||||
default='euclid', | |||||
help="Specify the distance on y to fit the costs") | |||||
args = parser.parse_args() | |||||
dataset = args.dataset | |||||
output_result = args.output_file | |||||
unlabeled = args.unlabeled | |||||
mode = args.mode | |||||
print(args) | |||||
y_distances = { | |||||
'euclidean': euclid_d, | |||||
'manhattan': man_d, | |||||
'classif': classif_d | |||||
} | |||||
y_distance = y_distances['euclid'] | |||||
run_xp(dataset, output_result, unlabeled, mode, y_distance) | |||||
print("Fini") | |||||
if __name__ == "__main__": | |||||
import pickle | |||||
import os | |||||
from distances import euclid_d, man_d, classif_d | |||||
y_distances = { | |||||
'euclidean': euclid_d, | |||||
'manhattan': man_d, | |||||
'classif': classif_d | |||||
} | |||||
# Read arguments. | |||||
if len(sys.argv) > 1: | |||||
run_from_args() | |||||
else: | |||||
from sklearn.model_selection import ParameterGrid | |||||
# Get task grid. | |||||
Edit_Cost_List = ['BIPARTITE', 'IPFP'] | |||||
Dataset_list = ['Alkane_unlabeled', 'Acyclic', 'Chiral', 'Vitamin_D', | |||||
'Steroid'] | |||||
Dis_List = ['euclidean', 'manhattan'] | |||||
task_grid = ParameterGrid({'edit_cost': Edit_Cost_List[0:1], | |||||
'dataset': Dataset_list[1:2], | |||||
'distance': Dis_List[:]}) | |||||
unlabeled = False # @todo: Not actually used. | |||||
mode = 'reg' | |||||
# Run. | |||||
for task in list(task_grid): | |||||
print() | |||||
print(task) | |||||
output_result = 'outputs/results.' + '.'.join([task['dataset'], task['edit_cost'], task['distance']]) + '.pkl' | |||||
if not os.path.isfile(output_result): | |||||
run_xp(task['dataset'], output_result, unlabeled, mode, task['distance'], task['edit_cost']) |
@@ -0,0 +1,15 @@ | |||||
import numpy as np | |||||
def vec2sym_mat(v): | |||||
""" | |||||
Convert a vector encoding a symmetric matrix into a matrix | |||||
See Golub and Van Loan, Matrix Computations, 3rd edition, p21 | |||||
""" | |||||
n = int((-1+np.sqrt(1+8*len(v)))/2) # second order resolution | |||||
M = np.zeros((n, n)) | |||||
for i in range(n): | |||||
for j in range(i, n): | |||||
# Golub van Loan, Matrix Computations, Eq. 1.2.2, p21 | |||||
M[i, j] = M[j, i] = v[i*n - (i+1)*(i)//2 + j] | |||||
return M |