概述

双曲深度学习已在多个领域展现出显著优势。本文档总结双曲空间在知识图谱推荐系统计算机视觉自然语言处理等领域的典型应用。


知识图谱

层次结构建模

知识图谱天然具有层次结构(如 WordNet、Freebase):

                    concept
                     /    \
               entity1    entity2
                /    \      |
            node1   node2  node3

双曲嵌入优势

  1. 低维高效嵌入:100维 Poincaré ball 可嵌入深度为10的层次树
  2. 自然表示推理:层次关系转换为几何距离
  3. 支持多关系:关系特定曲率空间

知识图谱嵌入方法

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.42MRR: 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提升
MovieLensHR@100.680.73+7%
PinterestNDCG@100.520.58+12%
AmazonRecall@200.340.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)

语义相似度任务性能

数据集Word2VecPoincaré 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

实现库

特点
geooptPyTorch官方黎曼优化库
HyperbolicGraph专注双曲图神经网络
hyperboGoogle双曲工具库

参考