概述
双曲深度学习已在多个领域展现出显著优势。本文档总结双曲空间在知识图谱、推荐系统、计算机视觉、自然语言处理等领域的典型应用。
知识图谱
层次结构建模
知识图谱天然具有层次结构(如 WordNet、Freebase):
concept
/ \
entity1 entity2
/ \ |
node1 node2 node3
双曲嵌入优势:
- 低维高效嵌入:100维 Poincaré ball 可嵌入深度为10的层次树
- 自然表示推理:层次关系转换为几何距离
- 支持多关系:关系特定曲率空间
知识图谱嵌入方法
Poincaré Embeddings
Nickel & Kiela (2017) 提出首个双曲知识图谱嵌入:
其中:
- 是头尾实体嵌入
- 是关系嵌入
- 是正样本三元组集合
- 是负样本集合
双曲 TransE
class HyperbolicTransE(nn.Module):
"""双曲空间中的TransE"""
def __init__(self, num_entities, num_relations, embedding_dim, c=1.0):
super().__init__()
self.c = c
# 实体和关系嵌入(初始化在双曲空间)
self.ent_embeddings = nn.Embedding(num_entities, embedding_dim)
self.rel_embeddings = nn.Embedding(num_relations, embedding_dim)
# 初始化
nn.init.uniform_(-0.001, 0.001, self.ent_embeddings.weight)
nn.init.uniform_(-0.001, 0.001, self.rel_embeddings.weight)
def distance(self, h, r, t):
"""计算 (h, r, t) 三元组的双曲距离"""
# 关系平移
hr = self._mobius_add(h, r)
# 计算距离
return self._poincare_distance(hr, t)
def forward(self, pos_triples, neg_triples):
"""三元组评分"""
pos_h, pos_r, pos_t = pos_triples
neg_h, neg_r, neg_t = neg_triples
pos_score = self.distance(pos_h, pos_r, pos_t)
neg_score = self.distance(neg_h, neg_r, neg_t)
# 铰链损失
return torch.clamp(pos_score + self.margin - neg_score, min=0).mean()
def _mobius_add(self, u, v):
"""Mobius加法"""
v_sq = torch.sum(v * v, dim=-1, keepdim=True)
uv = torch.sum(u * v, dim=-1, keepdim=True)
u_sq = torch.sum(u * u, dim=-1, keepdim=True)
denom = 1 - 2 * self.c * uv + self.c * v_sq
return ((1 + 2 * self.c * uv + self.c * u_sq) * v + (1 - self.c * u_sq) * u) / denom
def _poincare_distance(self, u, v):
"""Poincaré距离"""
diff_norm_sq = torch.sum((u - v) ** 2, dim=-1)
u_norm_sq = torch.sum(u ** 2, dim=-1)
v_norm_sq = torch.sum(v ** 2, dim=-1)
denom = (1 - u_norm_sq) * (1 - v_norm_sq)
return 2 * self.c * torch.acosh(1 + 2 * diff_norm_sq / denom.clamp(min=1e-10))知识图谱补全应用
| 数据集 | 任务 | 欧几里得方法 | 双曲方法 | 提升 |
|---|---|---|---|---|
| WordNet | 链接预测 | MRR: 0.42 | MRR: 0.61 | +45% |
| Freebase | 关系预测 | Hits@10: 34% | Hits@10: 48% | +41% |
| UMLS | 实体分类 | Acc: 89% | Acc: 93% | +4% |
推荐系统
层次用户建模
推荐系统中的用户-物品交互具有隐式层次结构:
用户群体A
├── 高端用户
│ ├── 用户A1
│ └── 用户A2
└── 普通用户
├── 用户B1
└── 用户B2
双曲协同过滤
class HyperbolicCollaborativeFiltering(nn.Module):
"""双曲协同过滤"""
def __init__(self, num_users, num_items, embedding_dim, c=1.0):
super().__init__()
self.c = c
# 用户和物品嵌入(初始化在Poincaré ball)
self.user_embed = nn.Embedding(num_users, embedding_dim)
self.item_embed = nn.Embedding(num_items, embedding_dim)
self._init_embeddings()
def _init_embeddings(self):
"""使用均匀分布初始化"""
nn.init.uniform_(self.user_embed.weight, -0.001, 0.001)
nn.init.uniform_(self.item_embed.weight, -0.001, 0.001)
def forward(self, user_ids, item_ids):
# 获取嵌入
user_emb = self.user_embed(user_ids)
item_emb = self.item_embed(item_ids)
# 投影到有界区域
user_emb = self._project(user_emb)
item_emb = self._project(item_emb)
# 计算相似度(负距离)
distance = self._poincare_distance(user_emb, item_emb)
return -distance # 负距离作为相似度
def _project(self, x):
"""投影到Poincaré ball"""
norm = torch.norm(x, dim=-1, keepdim=True)
return x * torch.clamp(norm, max=self.c * 0.99) / norm.clamp(min=1e-10)
def _poincare_distance(self, u, v):
"""Poincaré距离"""
diff_norm_sq = torch.sum((u - v) ** 2, dim=-1)
u_norm_sq = torch.sum(u ** 2, dim=-1)
v_norm_sq = torch.sum(v ** 2, dim=-1)
denom = (1 - u_norm_sq) * (1 - v_norm_sq)
return 2 * self.c * torch.acosh(1 + 2 * diff_norm_sq / denom.clamp(min=1e-10))序列推荐
双曲空间可用于建模用户的兴趣演化:
时间 t1: 用户在位置A附近(局部区域)
时间 t2: 用户兴趣扩展(半径扩大)
时间 t3: 新兴趣出现(新聚类中心)
使用双曲轨迹模型:
class HyperbolicSequentialRec(nn.Module):
"""双曲序列推荐"""
def __init__(self, num_items, hidden_dim, c=1.0):
super().__init__()
self.c = c
self.item_embed = nn.Embedding(num_items, hidden_dim)
self.gru = nn.GRU(hidden_dim * 2, hidden_dim) # 双曲+欧几里得特征
def forward(self, item_seq):
"""
item_seq: [batch, seq_len]
"""
item_embs = self.item_embed(item_seq)
item_embs = self._project(item_embs)
# 将双曲嵌入投影到切空间
item_embs_log = self._log_map(item_embs)
# GRU处理序列
gru_out, hidden = self.gru(item_embs_log)
# 获取最后一个隐状态
final_state = hidden.squeeze(0)
# 预测下一个物品(找最近的嵌入)
return final_state推荐系统性能对比
| 数据集 | 指标 | 欧几里得MF | 双曲MF | 提升 |
|---|---|---|---|---|
| MovieLens | HR@10 | 0.68 | 0.73 | +7% |
| NDCG@10 | 0.52 | 0.58 | +12% | |
| Amazon | Recall@20 | 0.34 | 0.41 | +21% |
计算机视觉
层次图像表示
图像中的视觉概念具有层次结构:
图像
├── 前景
│ ├── 主物体
│ │ ├── 类别(狗)
│ │ └── 实例(我的狗)
│ └── 部件(头部、腿部)
└── 背景
└── 场景(室内/室外)
双曲图像分类
class HyperbolicImageClassifier(nn.Module):
"""双曲图像分类器"""
def __init__(self, backbone, num_classes, hidden_dim, c=1.0):
super().__init__()
self.c = c
# 图像编码器
self.backbone = backbone
# 双曲映射层
self.hyper_proj = nn.Linear(backbone.output_dim, hidden_dim)
# 分类头(可选:使用双曲分类器)
self.classifier = HyperbolicClassifier(hidden_dim, num_classes, c)
def forward(self, images):
# 提取图像特征
features = self.backbone(images)
# 映射到双曲空间
h = self.hyper_proj(features)
h = self._exp_map(h) # 映射到Poincaré ball
h = self._project(h)
# 分类
logits = self.classifier(h)
return logits
def _exp_map(self, v):
"""从原点的指数映射"""
v_norm = torch.norm(v, dim=-1, keepdim=True).clamp(min=1e-10)
return torch.tanh(v_norm) * v / v_norm * self.c
def _project(self, x):
"""投影到有界区域"""
norm = torch.norm(x, dim=-1, keepdim=True)
return x * torch.clamp(norm, max=self.c * 0.99) / norm.clamp(min=1e-10)
class HyperbolicClassifier(nn.Module):
"""双曲分类器"""
def __init__(self, hidden_dim, num_classes, c=1.0):
super().__init__()
self.c = c
# 类别原型嵌入(每个类一个中心)
self.class_prototypes = nn.Embedding(num_classes, hidden_dim)
nn.init.uniform_(self.class_prototypes.weight, -0.01, 0.01)
def forward(self, x):
"""
x: 双曲空间中的样本嵌入
返回: 各类别的logits
"""
prototypes = self._project(self.class_prototypes.weight)
# 计算到各类别原型的双曲距离
distances = []
for i in range(prototypes.size(0)):
d = self._poincare_distance(x, prototypes[i])
distances.append(d)
logits = -torch.stack(distances, dim=1) # 负距离作为相似度
return logits
def _poincare_distance(self, u, v):
"""两点间的Poincaré距离"""
diff_norm_sq = torch.sum((u - v) ** 2, dim=-1)
u_norm_sq = torch.sum(u ** 2, dim=-1)
v_norm_sq = torch.sum(v ** 2, dim=-1)
denom = (1 - u_norm_sq) * (1 - v_norm_sq)
return 2 * self.c * torch.acosh(1 + 2 * diff_norm_sq / denom.clamp(min=1e-10))
def _project(self, x):
norm = torch.norm(x, dim=-1, keepdim=True)
return x * torch.clamp(norm, max=self.c * 0.99) / norm.clamp(min=1e-10)细粒度图像识别
双曲空间特别适合细粒度分类任务:
| 任务 | 类别关系 | 适用性 |
|---|---|---|
| 鸟种分类 | 科→属→种 | ⭐⭐⭐ 强层次 |
| 车型识别 | 品牌→系列→车型 | ⭐⭐⭐ 强层次 |
| 人脸识别 | 人→不同照片 | ⭐⭐ 一般层次 |
自然语言处理
语义层次建模
词嵌入可以自然表示上下位关系:
concept (根节点)
↓
entity (实体)
↓
specific_instance (具体实例)
双曲词嵌入
class HyperbolicWordEmbedding(nn.Module):
"""双曲词嵌入"""
def __init__(self, vocab_size, embedding_dim, c=1.0):
super().__init__()
self.c = c
self.embedding_dim = embedding_dim
# 词嵌入
self.embeddings = nn.Embedding(vocab_size, embedding_dim)
nn.init.uniform_(self.embeddings.weight, -0.001, 0.001)
def forward(self, token_ids):
embeds = self.embeddings(token_ids)
# 映射到双曲空间
embeds = self._exp_map(embeds)
embeds = self._project(embeds)
return embeds
def similarity(self, ids1, ids2):
"""计算两组词的语义相似度"""
emb1 = self.forward(ids1)
emb2 = self.forward(ids2)
# 在切空间中计算余弦相似度
emb1_log = self._log_map(emb1)
emb2_log = self._log_map(emb2)
return F.cosine_similarity(emb1_log, emb2_log, dim=-1)
def _exp_map(self, v):
v_norm = torch.norm(v, dim=-1, keepstring=True).clamp(min=1e-10)
return torch.tanh(v_norm) * v / v_norm
def _log_map(self, x):
x_norm = torch.norm(x, dim=-1, keepdim=True).clamp(min=1e-10)
return (torch.atanh(x_norm) / x_norm) * x
def _project(self, x):
norm = torch.norm(x, dim=-1, keepdim=True)
return x * torch.clamp(norm, max=self.c * 0.99) / norm.clamp(min=1e-10)语义相似度任务性能
| 数据集 | Word2Vec | Poincaré Embeddings | 提升 |
|---|---|---|---|
| WordSim353 | ρ=0.62 | ρ=0.71 | +15% |
| SimLex999 | ρ=0.44 | ρ=0.52 | +18% |
组合应用:HypRAG
HypRAG(双曲检索增强生成)结合了知识图谱层次和双曲空间优势:
class HypRAG:
"""双曲检索增强生成"""
def __init__(self, kg_embeddings, doc_embeddings, c=1.0):
self.kg_embedder = kg_embeddings # 知识图谱嵌入
self.doc_embedder = doc_embeddings # 文档嵌入
self.c = c
def retrieve(self, query, top_k=5):
"""检索相关文档"""
# 将查询映射到双曲空间
query_emb = self._embed_query(query)
# 计算与文档嵌入的距离
doc_embs = self._embed_documents()
# 层次检索:先找相关子树,再找具体节点
distances = self._poincare_distance(query_emb, doc_embs)
# 返回top-k
_, indices = torch.topk(-distances, top_k)
return indices
def hierarchical_context(self, query, max_depth=3):
"""构建层次化上下文"""
context = []
# 1. 检索直接相关实体
direct_entities = self.retrieve(query, top_k=10)
context.extend(direct_entities)
# 2. 检索上层抽象实体
for d in range(1, max_depth):
parent_entities = self._get_parent_entities(direct_entities)
context.extend(parent_entities)
# 3. 检索同层兄弟实体(提供对比信息)
sibling_entities = self._get_sibling_entities(direct_entities)
context.extend(sibling_entities[:5]) # 限制数量
return context实践指南
何时使用双曲方法
✅ 强烈推荐:
- 数据有明显树状/层次结构
- 需要处理不同粒度的实体
- 层次深度较大(>5层)
⚠️ 谨慎使用:
- 数据无明显层次结构
- 数据量较小(双曲方法通常需要更多数据)
- 需要欧几里得距离语义
曲率选择
| 数据层次深度 | 推荐曲率 |
|---|---|
| 浅(1-3层) | 0.1-0.5 |
| 中(4-6层) | 1.0 |
| 深(7+层) | 2.0-5.0 |
实现库
| 库 | 特点 |
|---|---|
| geoopt | PyTorch官方黎曼优化库 |
| HyperbolicGraph | 专注双曲图神经网络 |
| hyperbo | Google双曲工具库 |