diff --git a/config_files/config_test.yml b/config_files/config_test.yml index 979bdc8ae13c1dde5227746138952b4ec840f666..2edb012bd2f148ad0d93ba94fd62ba9e606209cd 100644 --- a/config_files/config_test.yml +++ b/config_files/config_test.yml @@ -24,7 +24,7 @@ Classification: classes: type: ["multiview"] algos_monoview: ["all"] - algos_multiview: ["mvml", "lp_norm_mkl",] + algos_multiview: ["lp_norm_mkl",] stats_iter: 2 metrics: ["accuracy_score", "f1_score"] metric_princ: "f1_score" @@ -210,10 +210,10 @@ easy_mkl: lp_norm_mkl: lmbda: [0.1] - max_rounds: [50] - max_diff: [0.0001] - kernel_types: ["rbf_kernel"] - kernel_configs: + n_loops: [50] + precision: [0.0001] + kernel: ["rbf"] + kernel_params: gamma: [0.1] mvml: diff --git a/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/additions/kernel_learning.py b/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/additions/kernel_learning.py index c2a18ed33c066a15229c60867fee3145b6ec081b..beb24789784e532ee45f1a37e42b562b3172496f 100644 --- a/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/additions/kernel_learning.py +++ b/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/additions/kernel_learning.py @@ -7,11 +7,8 @@ from ...utils.transformations import sign_labels, unsign_labels class KernelClassifier(BaseMultiviewClassifier): - def __init__(self, random_state=None, - kernel_types=None, kernel_configs=None): + def __init__(self, random_state=None,): super().__init__(random_state) - self.kernel_configs=kernel_configs - self.kernel_types=kernel_types def _compute_kernels(self, X, example_indices, view_indices, ): new_X = {} @@ -22,42 +19,37 @@ class KernelClassifier(BaseMultiviewClassifier): **kernel_config) return new_X - def _init_fit(self, X, y, train_indices, view_indices): - train_indices, view_indices = get_examples_views_indices(X, - train_indices, - view_indices) - self.init_kernels(nb_view=len(view_indices), ) - new_X = self._compute_kernels(X, - train_indices, view_indices) - new_y = sign_labels(y[train_indices]) - return new_X, new_y + def format_X(self, X, example_indices, view_indices): + example_indices, view_indices = get_examples_views_indices(X, + example_indices, + view_indices) + formatted_X = dict((index, X.get_v(view_index, example_indices=example_indices)) + for index, view_index in enumerate(view_indices)) + + return formatted_X, example_indices def extract_labels(self, predicted_labels): signed_labels = np.sign(predicted_labels) return unsign_labels(signed_labels) - - - def init_kernels(self, nb_view=2, ): - if isinstance(self.kernel_types, KernelDistribution): - self.kernel_functions = self.kernel_types.draw(nb_view) - elif isinstance(self.kernel_types, str): - self.kernel_functions = [getattr(pairwise, self.kernel_types) + if isinstance(self.kernel, KernelDistribution): + self.kernel = self.kernel.draw(nb_view) + elif isinstance(self.kernel, str): + self.kernel = [self.kernel for _ in range(nb_view)] - elif isinstance(self.kernel_types, list): - self.kernel_functions = [getattr(pairwise, kernel_type) - for kernel_type in self.kernel_types] - - if isinstance(self.kernel_configs, KernelConfigDistribution): - self.kernel_configs = self.kernel_configs.draw(nb_view) - self.kernel_configs = [kernel_config[kernel_function.__name__] - for kernel_config, kernel_function - in zip(self.kernel_configs, - self.kernel_functions)] - - elif isinstance(self.kernel_configs, dict): - self.kernel_configs = [self.kernel_configs for _ in range(nb_view)] + elif isinstance(self.kernel, list): + pass + + if isinstance(self.kernel_params, KernelConfigDistribution): + self.kernel_params = self.kernel_params.draw(nb_view) + self.kernel_params = [kernel_config[kernel_name] + for kernel_config, kernel_name + in zip(self.kernel_params, + self.kernel)] + + elif isinstance(self.kernel_params, dict): + self.kernel_params = [self.kernel_params for _ in range(nb_view)] else: pass @@ -76,13 +68,8 @@ class KernelConfigDistribution: def __init__(self, seed=42): self.random_state=np.random.RandomState(seed) self.possible_config = { - # "polynomial_kernel":{"degree": CustomRandint(low=1, high=7), - # "gamma": CustomUniform(), - # "coef0": CustomUniform() - # - # }, - "chi2_kernel": {"gamma": CustomUniform()}, - "rbf_kernel": {"gamma": CustomUniform()}, + "additive_chi2": {"gamma": CustomUniform()}, + "rbf": {"gamma": CustomUniform()}, } def draw(self, nb_view): @@ -108,8 +95,7 @@ class KernelDistribution: def __init__(self, seed=42): self.random_state=np.random.RandomState(seed) - self.available_kernels = [pairwise.chi2_kernel, - pairwise.rbf_kernel,] + self.available_kernels = ["rbf",] def draw(self, nb_view): - return self.random_state.choice(self.available_kernels, nb_view) + return list(self.random_state.choice(self.available_kernels, nb_view)) diff --git a/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/lp_norm_mkl.py b/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/lp_norm_mkl.py index f93f632c0982f7203f02feb727095dea6a1b966b..72722d01addbfacfde09a8e7b3349cb2a37814d0 100644 --- a/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/lp_norm_mkl.py +++ b/multiview_platform/mono_multi_view_classifiers/multiview_classifiers/lp_norm_mkl.py @@ -1,231 +1,38 @@ -from sklearn.metrics import pairwise +from metriclearning.lpMKL import MKL from ..multiview.multiview_utils import BaseMultiviewClassifier, get_examples_views_indices from .additions.kernel_learning import KernelClassifier, KernelConfigGenerator, KernelGenerator from ..utils.hyper_parameter_search import CustomUniform, CustomRandint -classifier_class_name = "LPNormMKL" - -### The following code is a welcome contribution by Riikka Huusari -# (riikka.huusari@lis-lab.fr) that we adapted te create the classifier - - -import numpy as np -from sklearn.base import BaseEstimator -from sklearn.base import ClassifierMixin -from sklearn.utils.multiclass import unique_labels -from sklearn.utils.validation import check_X_y -from .additions.data_sample import Metriclearn_array - - -class MKL(BaseEstimator, ClassifierMixin): - def __init__(self, lmbda, m_param=1.0, use_approx=True, max_rounds=50, - max_diff=0.0001, p=2): - print(lmbda) - # calculate nyström approximation (if used) - self.lmbda = lmbda - self.use_approx = use_approx - self.m_param = m_param - - # Non-optimizable Hyper-params - self.max_rounds = max_rounds - self.max_diff = max_diff - self.p = p - - def fit(self, X, y= None, views_ind=None): - if isinstance(X, Metriclearn_array): - self.X_ = X - elif isinstance(X, dict): - self.X_ = Metriclearn_array(X) - elif isinstance(X, np.ndarray) : - self.X_ = Metriclearn_array(X, views_ind) - self.classes_ = unique_labels(y) - check_X_y(self.X_, y) - self.y_ = y - n = self.X_.shape[0] - self._calc_nystrom(self.X_, n) - C, weights = self.learn_lpMKL() - self.C = C - self.weights = weights - - def learn_lpMKL(self): - - views = self.X_.n_views - X = self.X_ - # p = 2 - n = self.X_.shape[0] - weights = np.ones(views) / (views) - - prevalpha = False - max_diff = 1 - - kernels = np.zeros((views, n, n)) - for v in range(0, views): - kernels[v, :, :] = np.dot(self.U_dict[v], np.transpose(self.U_dict[v])) - - rounds = 0 - stuck = False - while max_diff > self.max_diff and rounds < self.max_rounds and not stuck: - - # gammas are fixed upon arrival to the loop - # -> solve for alpha! - - if self.m_param < 1 and self.use_approx: - combined_kernel = np.zeros((n, n)) - for v in range(0, views): - combined_kernel = combined_kernel + weights[v] * kernels[v] - else: - combined_kernel = np.zeros((n, n)) - for v in range(0, views): - combined_kernel = combined_kernel + weights[v]*X.get_view(v) - # combined kernel includes the weights - - # alpha = (K-lambda*I)^-1 y - C = np.linalg.solve((combined_kernel + self.lmbda * np.eye(n)), self.y_) - - # alpha fixed -> calculate gammas - weights_old = weights.copy() - - # first the ||f_t||^2 todo wtf is the formula used here???? - ft2 = np.zeros(views) - for v in range(0, views): - if self.m_param < 1 and self.use_approx: - # ft2[v,vv] = weights_old[v,vv] * np.dot(np.transpose(C), np.dot(np.dot(np.dot(data.U_dict[v], - # np.transpose(data.U_dict[v])), - # np.dot(data.U_dict[vv], - # np.transpose(data.U_dict[vv]))), C)) - ft2[v] = np.linalg.norm(weights_old[v] * np.dot(kernels[v], C))**2 - else: - ft2[v] = np.linalg.norm(weights_old[v] * np.dot(X.get_view(v), C))**2 - # ft2[v] = weights_old[v] * np.dot(np.transpose(C), np.dot(data.kernel_dict[v], C)) - - # calculate the sum for downstairs - - # print(weights_old) - # print(ft2) - # print(ft2 ** (p / (p + 1.0))) - - downstairs = np.sum(ft2 ** (self.p / (self.p + 1.0))) ** (1.0 / self.p) - # and then the gammas - weights = (ft2 ** (1 / (self.p + 1))) / downstairs - - # convergence - if prevalpha == False: # first time in loop we don't have a previous alpha value - prevalpha = True - diff_alpha = 1 - else: - diff_alpha = np.linalg.norm(C_old - C) / np.linalg.norm(C_old) - max_diff_gamma_prev = max_diff_gamma - - max_diff_gamma = np.max(np.max(np.abs(weights - weights_old))) - - # try to see if convergence is as good as it gets: if it is stuck - if max_diff_gamma < 1e-3 and max_diff_gamma_prev < max_diff_gamma: - # if the gamma difference starts to grow we are most definitely stuck! - # (this condition determined empirically by running algo and observing the convergence) - stuck = True - if rounds > 1 and max_diff_gamma - max_diff_gamma_prev > 1e-2: - # If suddenly the difference starts to grow much - stuck = True - - max_diff = np.max([max_diff_gamma, diff_alpha]) - # print([max_diff_gamma, diff_alpha]) # print if convergence is interesting - C_old = C.copy() - rounds = rounds + 1 - - # print("\nlearned the weights:") - # np.set_printoptions(precision=3, suppress=True) - # print(weights) - # print("") - - # print if resulting convergence is of interest - # print("convergence of ", max_diff, " at step ", rounds, "/500") - - if stuck: - return C_old, weights_old - else: - return C, weights - - def predict(self, X, views_ind=None): - if isinstance(X, Metriclearn_array): - # self.X_ = X - pass - elif isinstance(X, dict): - X = Metriclearn_array(X) - elif isinstance(X, np.ndarray): - X = Metriclearn_array(X, views_ind) - C = self.C - weights = self.weights - return self.lpMKL_predict(X , C, weights) - - def lpMKL_predict(self, X, C, weights, views_ind=None): - if isinstance(X, Metriclearn_array): - # self.X_ = X - pass - elif isinstance(X, dict): - X = Metriclearn_array(X) - elif isinstance(X, np.ndarray): - X = Metriclearn_array(X, views_ind) - views = X.n_views - tt = X.shape[0] - m = self.X_.shape[0] # self.m_param * n - - # NO TEST KERNEL APPROXIMATION - # kernel = weights[0] * self.data.test_kernel_dict[0] - # for v in range(1, views): - # kernel = kernel + weights[v] * self.data.test_kernel_dict[v] - # TEST KERNEL APPROXIMATION - kernel = np.zeros((tt, self.X_.shape[0])) - for v in range(0, views): - if self.m_param < 1: - kernel = kernel + weights[v] * np.dot(np.dot(X.get_view(v)[:, 0:m], self.W_sqrootinv_dict[v]), - np.transpose(self.U_dict[v])) - else: - kernel = kernel + weights[v] * X.get_view(v) - - return np.dot(kernel, C) - - def _calc_nystrom(self, kernels, n_approx): - # calculates the nyström approximation for all the kernels in the given dictionary - self.W_sqrootinv_dict = {} - self.U_dict = {} - for v in range(kernels.n_views): - kernel = kernels.get_view(v) - E = kernel[:, 0:n_approx] - W = E[0:n_approx, :] - Ue, Va, _ = np.linalg.svd(W) - vak = Va[0:n_approx] - inVa = np.diag(vak ** (-0.5)) - U_v = np.dot(E, np.dot(Ue[:, 0:n_approx], inVa)) - self.U_dict[v] = U_v - self.W_sqrootinv_dict[v] = np.dot(Ue[:, 0:n_approx], inVa) +classifier_class_name = "LPNormMKL" class LPNormMKL(KernelClassifier, MKL): - def __init__(self, random_state=None, lmbda=0.1, m_param=1, max_rounds=50, - max_diff=0.0001, use_approx=True, kernel_types="rbf_kernel", - kernel_configs=None, p=2, prev_alpha=False): - super().__init__(random_state, kernel_configs=kernel_configs, - kernel_types=kernel_types) - super(BaseMultiviewClassifier, self).__init__(lmbda, m_param, - use_approx, max_rounds, - max_diff, p) - self.param_names = ["lmbda", "kernel_types", "kernel_configs"] + def __init__(self, random_state=None, lmbda=0.1, m_param=1, n_loops=50, + precision=0.0001, use_approx=True, kernel="rbf", + kernel_params=None): + super().__init__(random_state) + super(BaseMultiviewClassifier, self).__init__(lmbda, m_param=m_param, + kernel=kernel, + n_loops=n_loops, + precision=precision, + use_approx=use_approx, + kernel_params=kernel_params) + self.param_names = ["lmbda", "kernel", "kernel_params"] self.distribs = [CustomUniform(), KernelGenerator(), KernelConfigGenerator()] - self.prev_alpha = prev_alpha def fit(self, X, y, train_indices=None, view_indices=None): - new_X, new_y = self._init_fit(X, y, train_indices, view_indices) - return super(LPNormMKL, self).fit(new_X, new_y) + formatted_X, train_indices = self.format_X(X, train_indices, view_indices) + self.init_kernels(nb_view=len(formatted_X)) + + return super(LPNormMKL, self).fit(formatted_X, y[train_indices]) def predict(self, X, example_indices=None, view_indices=None): - example_indices, view_indices = get_examples_views_indices(X, - example_indices, - view_indices) - new_X = self._compute_kernels(X, example_indices, view_indices) + print(self.C.shape) + new_X, _ = self.format_X(X, example_indices, view_indices) return self.extract_labels(super(LPNormMKL, self).predict(new_X))