如何基于多维向量矩阵在hyperbolic space中构建distance matrix?
双曲空间中构建距离矩阵的实现指南
没问题!把欧氏空间里构建距离矩阵的操作迁移到双曲空间,核心是先搞清楚双曲空间的距离度量规则,再把你的多维向量适配到双曲空间中,最后批量计算点对距离。下面我一步步给你讲具体实现方法:
一、先选好双曲空间模型
最适合从欧氏空间迁移的是Poincaré球模型(n维),这个模型里所有点都落在n维单位球内部(向量的欧氏范数严格小于1),计算规则直观,容易和你的输入向量适配。
二、把输入向量投影到双曲空间
你的输入是多维向量矩阵,但这些向量大概率不在Poincaré球内部,所以第一步要做投影缩放:
- 对每个向量,计算它的欧氏范数,然后将其缩放到单位球内,确保范数小于1(加个极小的
ε避免刚好落在球面上,因为球面是双曲空间的无穷远点)。
用Python + NumPy实现的投影函数:
import numpy as np def project_to_poincare_ball(X, eps=1e-8): # 计算每个向量的欧氏范数 vec_norms = np.linalg.norm(X, axis=1, keepdims=True) # 处理零向量,避免除以0 vec_norms[vec_norms == 0] = eps # 缩放向量到单位球内 return X / (vec_norms + eps)
三、实现双曲空间的距离计算
Poincaré球模型中,两个点u和v的双曲距离公式是:
d(u, v) = arccosh(1 + 2 * ||u - v||² / [(1 - ||u||²)(1 - ||v||²)])
注意事项:
- 当两个点非常接近时,公式里的
arg可能因为数值精度略小于1,这时候要把它截断到1.0,否则arccosh会返回NaN。 - 批量计算距离矩阵时,用广播代替双重循环能大幅提升效率。
距离矩阵的实现(两种方式)
方式1:基础循环版(适合小数据量,易理解)
def hyperbolic_distance(u, v): # 计算两点欧氏距离的平方 delta_sq = np.linalg.norm(u - v, axis=-1)**2 # 计算每个点的范数平方 u_norm_sq = np.linalg.norm(u, axis=-1)**2 v_norm_sq = np.linalg.norm(v, axis=-1)**2 # 计算分母项 denominator = (1 - u_norm_sq) * (1 - v_norm_sq) # 计算arccosh的输入值 arg = 1 + 2 * delta_sq / denominator # 修正数值精度问题,确保arg >= 1 arg = np.clip(arg, 1.0, None) return np.arccosh(arg) def build_hyperbolic_dist_matrix(X): # 先把输入向量投影到Poincaré球 X_hyper = project_to_poincare_ball(X) n_samples = X_hyper.shape[0] dist_matrix = np.zeros((n_samples, n_samples)) # 计算上三角矩阵,再对称填充 for i in range(n_samples): for j in range(i+1, n_samples): dist_matrix[i, j] = hyperbolic_distance(X_hyper[i], X_hyper[j]) dist_matrix[j, i] = dist_matrix[i, j] return dist_matrix
方式2:广播优化版(适合大数据量,速度快)
def build_hyperbolic_dist_matrix_fast(X): X_hyper = project_to_poincare_ball(X) n_samples = X_hyper.shape[0] # 用广播一次性计算所有点对的欧氏距离平方 delta_sq = np.sum((X_hyper[:, None, :] - X_hyper[None, :, :])**2, axis=-1) # 计算所有点的范数平方,转成适合广播的形状 u_norm_sq = np.sum(X_hyper**2, axis=1)[:, None] v_norm_sq = np.sum(X_hyper**2, axis=1)[None, :] denominator = (1 - u_norm_sq) * (1 - v_norm_sq) arg = 1 + 2 * delta_sq / denominator arg = np.clip(arg, 1.0, None) dist_matrix = np.arccosh(arg) # 手动把对角线设为0(避免数值误差导致的微小非零值) np.fill_diagonal(dist_matrix, 0.0) return dist_matrix
四、额外注意点
- 数值稳定性:如果你的输入向量范数很大,投影时一定要确保缩放后范数严格小于1,
ε的取值建议在1e-8~1e-6之间。 - 模型替代:如果你需要用其他双曲空间模型(比如Lorentz模型),距离公式会不同,但Poincaré球模型是最适合从欧氏空间迁移的场景。
- 库支持:如果不想自己实现,可以用专门的几何学习库(比如
geomstats),但自己实现核心逻辑能更清楚地理解双曲距离的计算过程。
内容的提问来源于stack exchange,提问作者user40267
相关产品推荐
相关产品推荐

