def update_mean(manifold, solver, data, land, k, param): N, D = data.shape Logmaps = land['Logmaps'][k, :, :] # N x D mu = land['means'][k, :].reshape(-1, 1) # D x 1 Sigma = land['Sigmas'][k, :, :] # D x D Resp = land['Resp'][:, k].reshape(-1, 1) # N x 1 Const = land['Consts'][k] V_samples = land['V_samples'][ k, :, :] # S x D, Use the samples from the last Const estimation volM_samples = land['volM_samples'][:, k].reshape(-1, 1) # S x 1 Z_eucl = land['Z_eucl'][k] for iteration in range(param['max_iter']): print('[Updating mean: {}] [Iteration: {}/{}]'.format( k + 1, iteration + 1, param['max_iter'])) # Use the non-failed only inds = np.isnan(Logmaps[:, 0]) # The failed logmaps grad_term1 = Logmaps[~inds, :].T @ Resp[~inds, :] / Resp[ ~inds, :].sum() grad_term2 = V_samples.T @ volM_samples.reshape( -1, 1) * Z_eucl / (Const * param['S']) # We do not multiply in front the invSigma because we use the steepest direction grad = (grad_term1 - grad_term2) curve, failed = geodesics.expmap(manifold, mu, grad * param['step_size']) mu_new = curve(1)[0] # Compute the logmaps for the new mean for n in range(N): _, logmap, curve_length, failed, sol \ = geodesics.compute_geodesic(solver, manifold, mu_new, data[n, :].reshape(-1, 1)) if failed: Logmaps[n, :] = np.nan # We do NOT use the straight geodesic else: Logmaps[n, :] = logmap.flatten() # Compute the constant for the new mean Const, V_samples, volM_samples, Z_eucl, _ = \ estimate_norm_constant(manifold, mu_new, Sigma, param['S']) cond = np.sum((mu - mu_new)**2) # The condition to stop mu = mu_new.copy() if cond < param['tol']: # Stop if lower than tolerance the change break return mu.flatten(), Logmaps, Const, V_samples, volM_samples.flatten( ), Z_eucl
def land_predict(manifold, solver, land, data): N, D = data.shape K = land['means'].shape[0] responsibilities = np.zeros((N, K)) Logmaps = np.zeros((K, N, D)) for k in range(K): mu = land['means'][k, :].reshape(-1, 1) for n in range(N): print('[Center: {}/{}] [Point: {}/{}]'.format(k + 1, K, n + 1, N)) _, logmap, _, failed, _ \ = geodesics.compute_geodesic(solver, manifold, mu, data[n, :].reshape(-1, 1)) Logmaps[k, n, :] = logmap.flatten( ) # Use the geodesic even if it is the failed line for k in range(K): responsibilities[:, k] = land['Weights'][k] \ * evaluate_pdf_land(Logmaps[k, :, :], land['Sigmas'][k, :, :], land['Consts'][k]).flatten() responsibilities = responsibilities / responsibilities.sum(axis=1, keepdims=True) return responsibilities
# Construct the manifold manifold_latent = manifolds.MlpMeanInvRbfVar(G_model_parameters) # Rescale the metric such that the maximum measure on the data to be 1 beta_rbf = 1 / (np.sqrt( np.linalg.det(manifold_latent.metric_tensor(MU_Z_data.T)).max())) G_model_parameters['beta'] = beta_rbf**(2 / d) # Rescale the pull-back metric manifold_latent = manifolds.MlpMeanInvRbfVar(G_model_parameters) z1min, z2min = MU_Z_data.min(0) - 0.5 z1max, z2max = MU_Z_data.max(0) + 0.5 plt.figure() utils.plot_measure(manifold_latent, np.linspace(z1min, z1max, 100), np.linspace(z2min, z2max, 100)) utils.my_plot(MU_Z_data, c='k', s=5, alpha=0.3) # Compute some random curves using a heuristic graph solver GRAPH_DATA = KMeans(n_clusters=100, n_init=30, max_iter=1000).fit(MU_Z_data).cluster_centers_ solver_graph = geodesics.SolverGraph(manifold_latent, data=GRAPH_DATA, kNN_num=5, tol=1e-2) for _ in range(20): ind_0, ind_1 = np.random.choice(N_train, 2, replace=False) c0 = MU_Z_data[ind_0, :].reshape(-1, 1) c1 = MU_Z_data[ind_1, :].reshape(-1, 1) curve_graph, logmap_graph, curve_length_graph, failed_graph, solution_graph \ = geodesics.compute_geodesic(solver_graph, manifold_latent, c0, c1) geodesics.plot_curve(curve_graph, c='r', linewidth=2)
def land_mixture_model(manifold, solver, data, param): K = param['K'] N, D = data.shape # Initializations solutions = {} # Initialize the land land = {} land['Resp'] = np.zeros((N, K)) # 1 failed, 0 not failed land['Logmaps'] = np.zeros((K, N, D)) land['Logmaps'][:] = np.nan land['Consts'] = np.zeros((K, 1)) land['means'] = param['means'].copy() land['Sigmas'] = np.zeros((K, D, D)) land['Weights'] = np.zeros((K, 1)) # The mixing components land['Failed'] = np.zeros((N, K)) # 1 failed, 0 not failed land['V_samples'] = np.zeros((K, param['S'], D)) land['volM_samples'] = np.zeros((param['S'], K)) land['Z_eucl'] = np.zeros((K, 1)) # Initialize components for k in range(K): for n in range(N): print('[Initialize: {}/{}] [Process point: {}/{}]'.format( k + 1, K, n + 1, N)) key = 'k_' + str(k) + '_n_' + str(n) _, logmap, curve_length, failed, sol \ = geodesics.compute_geodesic(solver, manifold, land['means'][k, :].reshape(-1, 1), data[n, :].reshape(-1, 1)) if failed: land['Failed'][n, k] = True land['Logmaps'][ k, n, :] = logmap.flatten() # The straight line geodesic land['Resp'][ n, k] = 1 / curve_length # If points are far lower responsibility solutions[key] = None else: land['Failed'][n, k] = False land['Logmaps'][k, n, :] = logmap.flatten() land['Resp'][n, k] = 1 / curve_length solutions[key] = sol land['Resp'] = land['Resp'] / land['Resp'].sum( axis=1, keepdims=True) # Compute the responsibilities land['Weights'] = np.sum(land['Resp'], axis=0).reshape(-1, 1) / N # Use the closest points to estimate the Sigmas and the normalization constants for k in range(K): inds_k = (land['Resp'].argmax(axis=1) == k ) # The closest points to the k-th center land['Sigmas'][k, :, :] = np.cov(land['Logmaps'][k, inds_k, :].T) * ( 1 - param['mixing_param']) + np.eye(D) * param['mixing_param'] land['Consts'][k], land['V_samples'][k, :, :], land['volM_samples'][:, k], land['Z_eucl'][k], _ \ = estimate_norm_constant(manifold, land['means'][k, :].reshape(-1, 1), land['Sigmas'][k, :, :], param['S']) negLogLikelihood = compute_negLogLikelihood(land) negLogLikelihoods = [negLogLikelihood] for iteration in range(param['max_iter']): print('[Iteration: {}/{}] [Negative log-likelihood: {}]'.format( iteration + 1, param['max_iter'], negLogLikelihood)) # ----- E-step ----- # for k in range(K): land['Resp'][:, k] = land['Weights'][k] \ * evaluate_pdf_land(land['Logmaps'][k, :, :], land['Sigmas'][k, :, :], land['Consts'][k]).flatten() land['Resp'] = land['Resp'] / land['Resp'].sum(axis=1, keepdims=True) # ----- M-step ----- # # Update the means for k in range(K): land['means'][k, :], land['Logmaps'][k, :, :], land['Consts'][k], land['V_samples'][k, :, :], land['volM_samples'][:, k], land['Z_eucl'][k] = \ update_mean(manifold, solver, data, land, k, param) # Update the covariances for k in range(K): land['Sigmas'][k, :, :], land['Consts'][k], land['V_samples'][k, :, :], land['volM_samples'][:, k], land['Z_eucl'][k] = \ update_Sigma(manifold, solver, data, land, k, param) # Update the constants for k in range(K): land['Consts'][k], land['V_samples'][k, :, :], land['volM_samples'][:, k], land['Z_eucl'][k], _ \ = estimate_norm_constant(manifold, land['means'][k, :].reshape(-1, 1), land['Sigmas'][k, :, :], param['S']) # Update the mixing components land['Weights'] = np.sum(land['Resp'], axis=0).reshape(-1, 1) / N # Compute the new likelihood and store it newNegLogLikelihood = compute_negLogLikelihood(land) negLogLikelihoods = np.concatenate( (negLogLikelihoods, [newNegLogLikelihood]), axis=0) # Check the difference in log-likelihood between updates if (newNegLogLikelihood - negLogLikelihood)**2 < param['tol']: break else: negLogLikelihood = newNegLogLikelihood land['negLogLikelihoods'] = negLogLikelihoods return land
data=GRAPH_DATA, kNN_num=int(np.log(N_nodes)), tol=1e-2) Dists_euclidean = np.zeros((N_points, N_points)) Dists_pca = np.zeros((N_points, N_points)) Dists_lda = np.zeros((N_points, N_points)) Dists_cost = np.zeros((N_points, N_points)) for n1 in range(N_points): for n2 in range(n1 + 1, N_points): c0 = POINTS[n1, :].reshape(-1, 1) c1 = POINTS[n2, :].reshape(-1, 1) _, _, curve_length_euclidean, _, _ \ = geodesics.compute_geodesic(solver_graph_latent_euclidean, manifold_latent_euclidean, c0, c1) _, _, curve_length_pca, _, _ \ = geodesics.compute_geodesic(solver_graph_latent_pca, manifold_latent_pca, c0, c1) _, _, curve_length_lda, _, _ \ = geodesics.compute_geodesic(solver_graph_latent_lda, manifold_latent_lda, c0, c1) _, _, curve_length_cost, _, _ \ = geodesics.compute_geodesic(solver_graph_latent_cost, manifold_latent_cost, c0, c1) Dists_euclidean[n1, n2] = curve_length_euclidean Dists_pca[n1, n2] = curve_length_pca Dists_lda[n1, n2] = curve_length_lda Dists_cost[n1, n2] = curve_length_cost Res_euclidean = Dists_euclidean[np.triu_indices(N_points, k=1)] Res_pca = Dists_pca[np.triu_indices(N_points, k=1)] Res_lda = Dists_lda[np.triu_indices(N_points, k=1)]
# Initialize the geodesic solvers solver_bvp = geodesics.SolverBVP(NMax=1000, tol=1e-1) solver_fp = geodesics.SolverFP(D=2, N=10, tol=1e-1) N_nodes = 30 solver_graph = geodesics.SolverGraph(manifold, data=KMeans(n_clusters=N_nodes, n_init=30, max_iter=1000).fit(data).cluster_centers_, kNN_num=int(np.log(N_nodes)), tol=1e-2) solver_comb = geodesics.SolverComb(solver_1=solver_bvp, solver_2=solver_graph) # Compute the shortest path between two points c0 = utils.my_vector([-1, 0]) c1 = utils.my_vector([1, 0]) curve_bvp, logmap_bvp, curve_length_bvp, failed_bvp, solution_bvp \ = geodesics.compute_geodesic(solver_bvp, manifold, c0, c1) geodesics.plot_curve(curve_bvp, c='r', linewidth=2, label='bvp') curve_fp, logmap_fp, curve_length_fp, failed_fp, solution_fp \ = geodesics.compute_geodesic(solver_fp, manifold, c0, c1) geodesics.plot_curve(curve_fp, c='g', linewidth=2, label='fp') curve_graph, logmap_graph, curve_length_graph, failed_graph, solution_graph \ = geodesics.compute_geodesic(solver_graph, manifold, c0, c1) geodesics.plot_curve(curve_graph, c='m', linewidth=2, label='graph') curve_comb, logmap_comb, curve_length_comb, failed_comb, solution_comb \ = geodesics.compute_geodesic(solver_comb, manifold, c0, c1) geodesics.plot_curve(curve_comb, c='y', linestyle='--', linewidth=2, label='comb (graph -> bvp)') plt.legend()
solver=solver, data=LAND_DATA, param=params) # Plot the density z1min, z2min = data.min(0) - 0.5 z1max, z2max = data.max(0) + 0.5 N_Z_grid = 20 Z_grid = utils.my_meshgrid(z1min, z1max, z2min, z2max, N=N_Z_grid) Logmaps = np.zeros( (2, Z_grid.shape[0], data.shape[1])) # The logmaps for each center for k in range(2): for n in range(Z_grid.shape[0]): # If the solver fails then the logmap is overestimated (straight line) curve_bvp, logmap_bvp, curve_length_bvp, failed_bvp, solution_bvp \ = geodesics.compute_geodesic(solver, manifold, land_res_prior['means'][k, :].reshape(-1, 1), Z_grid[n, :].reshape(-1, 1)) Logmaps[k, n, :] = logmap_bvp.ravel() pdf_vals = np.zeros((Z_grid.shape[0], 1)) for k in range(2): pdf_vals += land_res_prior['Weights'][k, 0] * ( np.exp(-0.5 * np.diag(Logmaps[k, :, :] @ np.linalg.inv( land_res_prior['Sigmas'][k, :, :]) @ Logmaps[k, :, :].T)) / land_res_prior['Consts'][k, 0]).reshape(-1, 1) plt.imshow(pdf_vals.reshape(N_Z_grid, N_Z_grid), interpolation='bicubic', extent=(z1min, z1max, z2min, z2max), origin='lower')