ProCreations's picture
Reproduction logbook (paper-vaApZm6MKM)
1b7a999 verified
Raw
History Blame Contribute Delete
20.2 kB
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from scipy.stats import special_ortho_group
from scipy.spatial.transform import Rotation as R
from scipy.linalg import cholesky
from scipy.linalg import logm, expm
from utils import KMeans
from utils import seed_everything
def interpolate_rotations(tab_matrix, weights):
"""
Interpole entre plusieurs matrices de rotation orthogonales \( k \times k \).
Arguments :
- tab_matrix : liste ou tableau de np.ndarray de taille (n, k, k),
où chaque matrice est une matrice de rotation orthogonale.
- weights : liste ou tableau de taille (n,), poids associés à chaque matrice.
Retourne :
- Matrice de rotation interpolée (np.ndarray de taille (k, k)).
"""
# Vérifications de la taille
tab_matrix = np.array(tab_matrix)
weights = np.array(weights)
assert tab_matrix.shape[1] == tab_matrix.shape[2], "Les matrices doivent être carrées."
assert len(weights) == tab_matrix.shape[0], "Le nombre de poids doit correspondre au nombre de matrices."
# Normaliser les poids
weights = weights / np.sum(weights)
# Calcul du logarithme pondéré
log_sum = np.zeros_like(tab_matrix[0])
for mat, w in zip(tab_matrix, weights):
log_sum += w * logm(mat) # Ajouter le logarithme pondéré
# Retourner à l'espace des rotations avec l'exponentielle
interpolated_matrix = expm(log_sum)
return interpolated_matrix
class NonLinearFunction:
def __init__(self, d, k, beta=None):
if beta is None:
beta = np.random.randn(d, k)
self.beta = beta
def get(self, x):
nonlinear_term = np.sin(np.dot(x, self.beta)) + 0.5 * np.tanh(np.dot(x**2, self.beta))
return nonlinear_term
class NonLinearFunctionUselessFeatures:
def __init__(self, d, k, number_usefull_features=5, beta=None):
if beta is None:
beta = np.random.randn(number_usefull_features, k)
self.beta = beta
self.number_usefull_features = number_usefull_features
def get(self, x):
x_usefull = x[:, :self.number_usefull_features]
nonlinear_term = np.sin(np.dot(x_usefull, self.beta)) + 0.5 * np.tanh(np.dot(x_usefull**2, self.beta))
return nonlinear_term
class RadiusTransformation:
def __init__(self, d, beta=None):
if beta is None:
beta = np.random.randn(d)
self.beta = beta
def get(self, x):
return ( np.linalg.norm(x) + 10.0 ) / 10
class IdTransformation:
def __init__(self, d, beta=None):
if beta is None:
beta = np.random.randn(d)
self.beta = beta
def get(self, x):
return 1.0
class LocalPerturbation:
def __init__(self, d, k, n_anchors=4, radius_transformation=None, seed=42):
seed_everything(seed)
self.k = k
if radius_transformation is not None:
self.radius_transformation = radius_transformation
else : self.radius_transformation = IdTransformation(d)
self.k = k
if self.k == 1: return
self.anchors_mu = []
self.anchors_cov = []
random_points = np.random.randn(1000, d)
kmeans = KMeans(n_clusters=n_anchors, random_state=seed)
kmeans.fit(random_points)
centers = kmeans.centroids
for i in range(n_anchors):
self.anchors_mu.append(centers[i])
covariance_matrix = special_ortho_group.rvs(dim=k)
self.anchors_cov.append(covariance_matrix)
def inverse_distance(self, point1, point2):
return (1 / (np.linalg.norm(point1 - point2) + 1e-6)) ** 4
def get_one_x(self, x):
if self.k == 1:
return np.array(self.radius_transformation.get(x)).reshape(1, 1)
sum_inv_dist = np.sum([self.inverse_distance(x, mu) for mu in self.anchors_mu])
weights = [self.inverse_distance(x, mu) / sum_inv_dist for mu in self.anchors_mu]
cov = interpolate_rotations(self.anchors_cov, weights)
cov = cov * self.radius_transformation.get(x)
return cov
def get(self, x):
return np.array([ self.get_one_x(x_i) for x_i in x])
class LocalPerturbationPSD:
def __init__(self, d, k, n_anchors=4, radius_transformation=None):
self.k = k
if radius_transformation is not None:
self.radius_transformation = radius_transformation
else : self.radius_transformation = IdTransformation(d)
self.k = k
if self.k == 1: return
self.anchors_mu = []
self.anchors_cov = []
random_points = np.random.randn(1000, d)
kmeans = KMeans(n_clusters=n_anchors)
kmeans.fit(random_points)
centers = kmeans.centroids
for i in range(n_anchors):
self.anchors_mu.append(centers[i])
mat_A = np.random.randn(k, k)
covariance_matrix = np.dot(mat_A.T, mat_A)
self.anchors_cov.append(covariance_matrix)
if n_anchors == 2:
self.anchors_mu.append(centers[i])
self.anchors_cov = [np.array([1, -0.9, -0.9, 1]).reshape(2, 2), np.array([1, 0.9, 0.9, 1]).reshape(2, 2)]
def inverse_distance(self, point1, point2):
return (1 / (np.linalg.norm(point1 - point2) + 1e-6)) ** 4
def get_one_x(self, x):
if self.k == 1:
return self.radius_transformation.get(x)
sum_inv_dist = np.sum([self.inverse_distance(x, mu) for mu in self.anchors_mu])
cov = np.sum([self.inverse_distance(x, mu) / sum_inv_dist * cov for mu, cov in zip(self.anchors_mu, self.anchors_cov)], axis=0)
return cholesky(cov)
def get(self, x):
return np.array([ self.get_one_x(x_i) for x_i in x])
class LocalPerturbationBin:
def __init__(self, d, k, n_anchors=4):
self.k = k
self.anchors_mu = []
self.anchors_cov = []
random_points = np.random.randn(1000, d)
kmeans = KMeans(n_clusters=n_anchors)
kmeans.fit(random_points)
centers = kmeans.centroids
for i in range(n_anchors):
self.anchors_mu.append(centers[i])
mat_A = np.random.randn(k, k)
covariance_matrix = np.dot(mat_A.T, mat_A)
self.anchors_cov.append(covariance_matrix)
if n_anchors == 2:
self.anchors_mu.append(centers[i])
self.anchors_cov = [np.array([1, -0.9, -0.9, 1]).reshape(2, 2), np.array([1, 0.9, 0.9, 1]).reshape(2, 2)]
def inverse_distance(self, point1, point2):
return (1 / (np.linalg.norm(point1 - point2) + 1e-6)) ** 4
def get_one_x(self, x):
idx = np.argmax([self.inverse_distance(x, mu) for mu in self.anchors_mu])
cov = self.anchors_cov[idx]
return cholesky(cov)
def get(self, x):
return np.array([self.get_one_x(x_i) for x_i in x])
class LinearFunction:
def __init__(self, d, k, beta=None):
if beta is None:
beta = np.random.randn(d, k)
self.beta = beta
def get(self, x):
return np.dot(x, self.beta)
class Perturbation:
def __init__(self, k, noise_type, scale_noise= 1.0, covariance_matrix=None):
self.scale_noise = scale_noise
self.noise_type = noise_type
self.k = k
if covariance_matrix is not None:
self.covariance_matrix = covariance_matrix
else:
A = np.random.randn(k, k)
self.covariance_matrix = A @ A.T
def get(self, x):
n = x.shape[0]
data_perturbation = self.scale_noise * get_perturbation(self.noise_type, n, self.k, covariance_matrix=self.covariance_matrix)
return data_perturbation
class DataGenerator:
def __init__(self, input_dim, output_dim, noise_type, f_star=None, scale_noise = 1.0, local_perturbation = None, law_x = "gaussian", covariance_matrix = None, bias=True, seed = 42):
seed_everything(seed)
self.perturbation = Perturbation(output_dim, noise_type, scale_noise=scale_noise, covariance_matrix=covariance_matrix)
self.local_perturbation = local_perturbation
self.law_x = law_x
if f_star is None:
self.f_star = LinearFunction(input_dim, output_dim)
else:
self.f_star = f_star
self.d = input_dim
self.k = output_dim
self.bias = bias
self.noise_type = noise_type
self.covariance_matrix = covariance_matrix
def process(self, x_train, y_train, x_test, y_test, x_cal, y_cal):
# Calcul des statistiques à partir de l'ensemble d'entraînement
self.x_mean = x_train.mean(axis=0)
self.x_range = x_train.max(axis=0) - x_train.min(axis=0)
self.y_mean = y_train.mean(axis=0)
self.y_range = y_train.max(axis=0) - y_train.min(axis=0)
# Normalisation des ensembles
x_train_processed = (x_train - self.x_mean) / self.x_range
x_test_processed = (x_test - self.x_mean) / self.x_range
x_cal_processed = (x_cal - self.x_mean) / self.x_range
y_train_processed = (y_train - self.y_mean) / self.y_range
y_test_processed = (y_test - self.y_mean) / self.y_range
y_cal_processed = (y_cal - self.y_mean) / self.y_range
return x_train_processed, y_train_processed, x_test_processed, y_test_processed, x_cal_processed, y_cal_processed
def process_from_train(self, x_specific, y_specific):
# Normalisation des ensembles
x_train_processed = (x_specific - self.x_mean) / self.x_range
y_train_processed = (y_specific - self.y_mean) / self.y_range
return x_train_processed, y_train_processed
def generate_specific_x(self, n):
if self.bias:
x_specific = get_perturbation(self.law_x, 1, self.d-1, covariance_matrix=np.eye(self.d-1))
# x_specific = np.random.randn(1, self.d-1)
x_specific = np.repeat(x_specific, n, axis=0)
x_specific = np.hstack([np.ones((n, 1)), x_specific])
else:
x_specific = get_perturbation(self.law_x, 1, self.d, covariance_matrix=np.eye(self.d))
# x_specific = np.random.randn(1, self.d)
x_specific = np.repeat(x_specific, n, axis=0)
f_star = self.f_star.get(x_specific)
data_perturbation_specific = self.perturbation.get(x_specific)
if self.local_perturbation is not None:
rotation = self.local_perturbation.get(x_specific)
data_perturbation_specific = np.einsum('njk,nk->nj', rotation, data_perturbation_specific)
y_specific = f_star + data_perturbation_specific
return x_specific, y_specific
def generate_specific_y_given_x(self, x_specific, n=1):
if n != 1:
x_specifics = np.tile(x_specific[:, np.newaxis], (1, n))
else:
x_specifics = x_specific
f_star = self.f_star.get(x_specifics)
data_perturbation_specific = self.perturbation.get(x_specifics)
if self.local_perturbation is not None:
rotation = self.local_perturbation.get(x_specifics)
data_perturbation_specific = np.einsum('njk,nk->nj', rotation, data_perturbation_specific)
y_specific = f_star + data_perturbation_specific
return x_specifics, y_specific
def generate_y_given_x(self, x_samples):
f_star = self.f_star.get(x_samples)
data_perturbation_specific = self.perturbation.get(x_samples)
if self.local_perturbation is not None:
rotation = self.local_perturbation.get(x_samples)
data_perturbation_specific = np.einsum('njk,nk->nj', rotation, data_perturbation_specific)
y_samples = f_star + data_perturbation_specific
return y_samples
def generate(self, n):
if self.bias:
x_train = get_perturbation(self.law_x, n, self.d-1, covariance_matrix=np.eye(self.d-1))
# x_train = np.random.randn(n, self.d-1)
x_train = np.hstack([np.ones((n, 1)), x_train])
else:
# x_train = np.random.randn(n, self.d)
x_train = get_perturbation(self.law_x, n, self.d, covariance_matrix=np.eye(self.d))
f_star = self.f_star.get(x_train)
data_perturbation_train = self.perturbation.get(x_train)
if self.local_perturbation is not None:
rotation = self.local_perturbation.get(x_train)
data_perturbation_train = np.einsum('njk,nk->nj', rotation, data_perturbation_train)
y_train = f_star + data_perturbation_train
return x_train, y_train
def generate_train_calibration_test(self, n_train, n_calibration, n_test):
x_train, y_train = self.generate(n_train)
x_calibration, y_calibration = self.generate(n_calibration)
x_test, y_test = self.generate(n_test)
return x_train, y_train, x_calibration, y_calibration, x_test, y_test
def generate_perturbation(self, n):
data_perturbation_train = get_perturbation(self.noise_type, n, self.k, covariance_matrix=self.covariance_matrix)
return data_perturbation_train
def generate_perturbation_train_calibration_test(self, n_train, n_calibration, n_test):
data_perturbation_train = self.generate_perturbation(n_train)
data_perturbation_calibration = self.generate_perturbation(n_calibration)
data_perturbation_test = self.generate_perturbation(n_test)
return data_perturbation_train, data_perturbation_calibration, data_perturbation_test
def get_perturbation(perturbation, n_train, k, covariance_matrix=None):
mean = np.zeros(k)
if perturbation == "laplace":
data_perturbation_train = np.random.laplace(0, 1, (n_train, k))
elif perturbation == "gaussian":
if covariance_matrix is None:
raise ValueError("Covariance matrix must be provided for gaussian perturbation")
data_perturbation_train = np.random.multivariate_normal(mean, covariance_matrix, n_train)
elif perturbation == "uniform":
data_perturbation_train = np.random.uniform(-1, 1, (n_train, k))
elif perturbation == "pareto":
data_perturbation_train = np.random.pareto(a=2.0, size=(n_train, k)) # a est le paramètre de forme
elif perturbation == "lognormal":
data_perturbation_train = np.random.lognormal(mean=0.0, sigma=1.0, size=(n_train, k))
elif perturbation == "exponential":
data_perturbation_train = np.random.exponential(scale=1.0, size=(n_train, k))
elif perturbation == "chisquare":
data_perturbation_train = np.random.chisquare(df=2, size=(n_train, k)) # df est le nombre de degrés de liberté
elif perturbation == "poisson":
data_perturbation_train = np.random.poisson(lam=3.0, size=(n_train, k))
elif perturbation == "gamma":
data_perturbation_train = np.random.gamma(shape=2.0, scale=1.0, size=(n_train, k))
elif perturbation == "beta":
data_perturbation_train = np.random.standard_t(df=3, size=(n_train, k)) # df est le nombre de degrés de liberté
elif perturbation == "cauchy":
data_perturbation_train = np.random.standard_cauchy(size=(n_train, k))
else:
data_perturbation_train = np.zeros((n_train, k))
return data_perturbation_train
# def inverse_distance(point1, point2):
# """
# Calculates the inverse-distance between two points
# :param point1: First point's coordinates
# :param point2: Second point's coordinates
# :return: Inverse-distance value
# """
# return (1 / (np.linalg.norm(point1 - point2) + 1e-6)) ** 4
# def point_cov(point):
# """
# Generates a covariance matrix for a point influenced by the 4 $\mu_i$ points used to simulate data
# :param point: The point's coordinates
# :return: Local covariance matrix for the point
# """
# # first part.
# mu_1 = np.array([-5, 5])
# cov_1 = np.array([[0.1, -0.09], [-0.09, 0.1]])
# # second part.
# mu_2 = np.array([5, 5])
# cov_2 = np.array([[0.1, 0.09], [0.09, 0.1]])
# # first part.
# mu_3 = np.array([-5, -5])
# cov_3 = np.array([[0.1, 0.09], [0.09, 0.1]])
# # second part.
# mu_4 = np.array([5, -5])
# cov_4 = np.array([[0.1, -0.09], [-0.09, 0.1]])
# sum_inv_dist = (
# inverse_distance(point, mu_1)
# + inverse_distance(point, mu_2)
# + inverse_distance(point, mu_3)
# + inverse_distance(point, mu_4)
# )
# cov = (
# (inverse_distance(point, mu_1) / sum_inv_dist) * cov_1
# + (inverse_distance(point, mu_2) / sum_inv_dist) * cov_2
# + (inverse_distance(point, mu_3) / sum_inv_dist) * cov_3
# + (inverse_distance(point, mu_4) / sum_inv_dist) * cov_4
# )
# return cov
# def gen_noise_from_x(x, noise_type):
# """
# Generates noise from the point according to the covariance matrix generated above
# :param x: The point's coordinates
# :return: The point's noisy coordinates
# """
# cov = point_cov(x)
# if noise_type == "gaussian":
# b = np.random.normal(0, 1, size=(1, 2))
# elif noise_type == "cauchy":
# b = np.random.standard_cauchy(size=(1, 2))*0.01
# elif noise_type == "exponential":
# b = np.random.exponential(1, size=(1, 2))*0.1
# else:
# raise ValueError("Noise type not supported")
# c = b @ cholesky(cov)
# return c.squeeze()
# def generate_synthetic_data(n=10_000, noise_type='gaussian'):
# """
# Generates a synthetic data set
# :param n: Desired number of instances
# :return: x_train, y_train, x_calibration, y_calibration
# """
# df = pd.DataFrame(columns=["x1", "x2", "y1", "y2"], index=range(n))
# df["x1"] = np.random.uniform(-5, 5, size=n)
# df["x2"] = np.random.uniform(-5, 5, size=n)
# df["y1"] = 0.7 * df["x1"] + 0.3 * df["x2"]
# df["y2"] = 0.2 * df["x1"] + 0.8 * df["x2"]
# df["noise_np"] = df.apply(
# lambda x: gen_noise_from_x(np.array([x["x1"], x["x2"]]), noise_type), axis=1
# )
# df["epsilon_1"] = df.noise_np.apply(lambda x: x[0])
# df["epsilon_2"] = df.noise_np.apply(lambda x: x[1])
# df["y1"] = df["y1"] + df["epsilon_1"]
# df["y2"] = df["y2"] + df["epsilon_2"]
# train, test = train_test_split(df, test_size=0.2)
# train, cal = train_test_split(train, test_size=0.2)
# x_train = train[["x1", "x2"]].to_numpy()
# y_train = train[["y1", "y2"]].to_numpy()
# x_test = test[["x1", "x2"]].to_numpy()
# y_test = test[["y1", "y2"]].to_numpy()
# x_calibration = cal[["x1", "x2"]].to_numpy()
# y_calibration = cal[["y1", "y2"]].to_numpy()
# return x_train, y_train, x_test, y_test, x_calibration, y_calibration
# def generate_synthetic_data_one_x(n=10_000, noise_type='gaussian'):
# """
# Generates a synthetic data set
# :param n: Desired number of instances
# :return: x_train, y_train, x_calibration, y_calibration
# """
# df = pd.DataFrame(columns=["x1", "x2", "y1", "y2"], index=range(n))
# val1 = np.random.uniform(-5, 5, size=1)
# val2 = np.random.uniform(-5, 5, size=1)
# df["x1"] = [val1[0]] * n
# df["x2"] = [val2[0]] * n
# df["y1"] = 0.7 * df["x1"] + 0.3 * df["x2"]
# df["y2"] = 0.2 * df["x1"] + 0.8 * df["x2"]
# df["noise_np"] = df.apply(
# lambda x: gen_noise_from_x(np.array([x["x1"], x["x2"]]), noise_type), axis=1
# )
# df["epsilon_1"] = df.noise_np.apply(lambda x: x[0])
# df["epsilon_2"] = df.noise_np.apply(lambda x: x[1])
# df["y1"] = df["y1"] + df["epsilon_1"]
# df["y2"] = df["y2"] + df["epsilon_2"]
# x_train = df[["x1", "x2"]].to_numpy()
# y_train = df[["y1", "y2"]].to_numpy()
# return x_train, y_train
# if __name__=="__main__":
# import matplotlib.pyplot as plt
# x_train, y_train, x_calibration, y_calibration = generate_synthetic_data(n=100)
# print(y_train.shape)
# # plt.plot(y_train)
# plt.scatter(y_train[:,0], y_train[:,1])
# plt.show()