别再只用欧式空间了!用Python+PyTorch在庞加莱球里做知识图谱嵌入实战
用Python实现庞加莱球模型的知识图谱嵌入实战知识图谱嵌入技术正在经历一场从欧式空间到非欧空间的范式转移。想象一下当你试图在二维平面上绘制一棵枝繁叶茂的大树时要么需要压缩树枝长度要么不得不扩大画布尺寸——这正是传统欧式空间处理层次化数据时面临的困境。而双曲空间的神奇之处在于它能够像折纸艺术般自然地折叠数据空间让靠近边缘的区域获得更大的表示容量。1. 环境配置与基础工具链1.1 安装必要的Python库工欲善其事必先利其器。我们需要配置以下核心工具包pip install torch1.10.0cu113 pip install geoopt0.2.0 pip install networkx matplotlib特别推荐geoopt这个专门为黎曼几何优化的PyTorch扩展库它封装了包括庞加莱球在内的多种流形操作import torch import geoopt from geoopt.manifolds import PoincareBall # 初始化曲率为-1的3维庞加莱球 manifold PoincareBall(c1.0, dim3)1.2 双曲空间基础运算实现庞加莱球中的距离计算与欧式空间截然不同。以下是核心运算的实现def poincare_distance(u, v, c1.0): 计算庞加莱球中两点间的双曲距离 sqrt_c c**0.5 u_norm u.norm(dim-1) v_norm v.norm(dim-1) delta (u - v).norm(dim-1) t1 1 2 * c * delta**2 / ((1 - c*u_norm**2) * (1 - c*v_norm**2)) return (1/sqrt_c) * torch.acosh(t1)注意庞加莱球中所有点的模长必须小于1/√c否则会超出流形边界2. 知识图谱数据建模2.1 数据集准备与处理我们以经典的WordNet名词层次结构为例import networkx as nx # 构建示例层次结构 G nx.DiGraph() G.add_edges_from([ (entity, physical_entity), (physical_entity, object), (object, artifact), (artifact, instrumentality) ]) # 实体和关系编码 entities list(G.nodes()) relations [hypernym] * (len(G.edges()))2.2 双曲嵌入模型架构class HyperbolicKG(torch.nn.Module): def __init__(self, num_entities, num_rels, dim32): super().__init__() self.manifold PoincareBall(c1.0, dimdim) self.entity_emb geoopt.ManifoldParameter( torch.randn(num_entities, dim) * 1e-3, manifoldself.manifold ) self.rel_emb torch.nn.Embedding(num_rels, dim) def forward(self, h_idx, r_idx, t_idx): h self.manifold.expmap0(self.entity_emb[h_idx]) r self.rel_emb(r_idx) t self.manifold.expmap0(self.entity_emb[t_idx]) # 关系转换在切空间进行 transformed self.manifold.expmap(h, r) return -poincare_distance(transformed, t)3. 黎曼优化实战3.1 自定义RSGD优化器class RiemannianSGD(geoopt.optim.RiemannianOptimizer): def __init__(self, params, lr1e-3): super().__init__(params, lrlr, stabilizationclip) def step(self, closureNone): loss None if closure is not None: loss closure() for group in self.param_groups: for p in group[params]: if p.grad is None: continue d_p p.grad.data if hasattr(p, manifold): d_p p.manifold.egrad2rgrad(p.data, d_p) p.data p.manifold.retr(p.data, -group[lr] * d_p) return loss3.2 训练循环实现def train(model, data_loader, epochs100): opt RiemannianSGD(model.parameters(), lr0.1) for epoch in range(epochs): total_loss 0 for h, r, t in data_loader: opt.zero_grad() pos_score model(h, r, t) neg_t torch.randint(0, len(entities), h.shape) neg_score model(h, r, neg_t) loss -torch.log(torch.sigmoid(pos_score - neg_score)).mean() loss.backward() opt.step() total_loss loss.item() print(fEpoch {epoch}: Loss {total_loss/len(data_loader):.4f})4. 效果评估与可视化4.1 链接预测评估def evaluate(model, test_triples): ranks [] for h, r, t in test_triples: scores [] # 计算头实体预测 all_heads model(None, r.expand(len(entities)), t) # 计算尾实体预测 all_tails model(h.expand(len(entities)), r, None) ranks.append((all_heads.argsort() h).nonzero().item() 1) ranks.append((all_tails.argsort() t).nonzero().item() 1) return { MR: torch.tensor(ranks).float().mean(), MRR: (1. / torch.tensor(ranks).float()).mean(), Hits10: (torch.tensor(ranks) 10).float().mean() }4.2 嵌入空间可视化import matplotlib.pyplot as plt from matplotlib.patches import Circle def plot_embeddings(embeddings, labels): fig, ax plt.subplots(figsize(10,10)) circle Circle((0,0), 1.0, fillFalse, linestyle--) ax.add_patch(circle) for (x,y), label in zip(embeddings, labels): ax.scatter(x, y) ax.annotate(label, (x,y)) ax.set_xlim(-1.1,1.1) ax.set_ylim(-1.1,1.1) ax.set_aspect(equal) plt.show()5. 进阶技巧与优化策略5.1 曲率自适应学习庞加莱球的曲率参数c对模型性能有显著影响。我们可以将其设为可学习参数class CurvatureAdapter(torch.nn.Module): def __init__(self, init_val1.0): super().__init__() self.log_c torch.nn.Parameter(torch.log(torch.tensor(init_val))) property def c(self): return torch.exp(self.log_c)5.2 混合空间架构结合欧式空间和双曲空间的优势class HybridEmbedding(torch.nn.Module): def __init__(self, num_emb, euclid_dim, hyp_dim): super().__init__() self.euclid_emb torch.nn.Embedding(num_emb, euclid_dim) self.hyp_emb geoopt.ManifoldParameter( torch.randn(num_emb, hyp_dim) * 1e-3, manifoldPoincareBall() ) def forward(self, idx): return torch.cat([ self.euclid_emb(idx), self.manifold.expmap0(self.hyp_emb[idx]) ], dim-1)在实际项目中我发现双曲嵌入特别适合处理具有明确层次结构的关系数据。当处理像哺乳动物-犬科-家犬这样的分类链时庞加莱球能够自然地保持每个层级间的语义距离。相比之下欧式嵌入要么过度压缩高层级概念要么使底层概念过于分散。