尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从影响力传播视角理解链接预测:多关系图模型原理与PyTorch实战

从影响力传播视角理解链接预测:多关系图模型原理与PyTorch实战 近年来图神经网络和知识图谱相关的技术文章越来越多但很多资料都停留在“调用现成库跑一个指标”的层面。如果你正好在调研链接预测、多关系图建模或者被推荐系统、知识图谱补全、社交网络中的链路挖掘问题困扰不妨换个视角来看这个问题——从影响力传播的角度理解链接预测。这篇文章不打算做成论文复现报告而是把“多关系图上的链接预测”和“影响力传播”这一思路结合起来从原理、公式、代码到实验设计完整讲清楚。全篇包含可运行的 PyTorch 与 PyG 示例适合有以下需求的读者想理解多关系图与链接预测的基础概念想了解 R-GCN、消息传递、PageRank 式传播之间的联系想自己实现一个可学习的传播算子完成链接预测准备阅读相关论文需要一个直观的“导航图”。接下来我们从最核心的概念讲起然后逐步落到代码和工程建议上。1. 背景与核心概念1.1 多关系图到底是什么图Graph是一种表达实体之间关系的数据结构。我们用节点表示实体用边表示实体之间的关系。比如社交网络中人看成一个节点人与人之间的“关注”“好友”“拉黑”看成边电商场景中用户、商品、店铺都是节点用户“购买”商品、商品“属于”店铺都是边知识图谱中实体是节点“出生于”“任职于”“获奖”等事实构成边。如果图中的边只表达一种关系也就是不考虑边的类型我们一般称为“单关系图”或者“同质图”。但在真实业务中边通常带有类型信息用户 A “关注” 用户 B用户 A “屏蔽” 用户 B用户 A “私信” 用户 B。这里的“关注”“屏蔽”“私信”是不同的关系类型。我们把这种节点类型可以多样、边类型也可以多样的图称为多关系图Multi-relational Graph。在多关系图中一条边可以形式化地表示为一个三元组(头实体 head, 关系 relation, 尾实体 tail)也就是(h, r, t)。例如(张伟, 就职于, 阿里) (阿里, 位于, 杭州)这样的结构化表示让机器能够参与推理和补全。1.2 链接预测要解决什么问题现实中的图数据往往是不完整的。原因有多种显式行为没有发生、数据采集有遗漏、人工标注成本过高或者图本身持续演化。因此我们希望在已知部分边的基础上推断出“可能已经存在但尚未观察到”的边。这个任务就是链接预测Link Prediction。在知识图谱领域链接预测通常称为知识图谱补全目标是预测三元组(h, r, t)是否成立。在推荐系统领域它可能表现为预测用户对商品是否会产生购买行为在社交网络中则表现为预测两个用户之间是否会建立好友关系。链接预测的难点在于边的形成不是随机的它受到图中其他结构、实体属性、关系语义等多重因素影响。如何把这些因素建模出来是该任务的核心挑战。1.3 影响力传播视角的引入我们可以观察到一个普遍现象如果两个人有很多共同好友那么他们之间更容易产生联系如果一个用户在社交平台上频繁互动那么他可能会影响周围用户的活跃行为。这里面隐藏着一条规律——链接的形成往往伴随着影响力的传播。把这条规律放到图数据中可以这样理解影响力从源节点出发沿着已有边向外扩散扩散过程中路径的长短、中间节点的数量、经过的关系类型都会影响影响力的大小当两个节点之间的传播路径足够多、传播阻力足够小时它们之间形成直接边的概率就增大。这正是从“影响力传播”Influence Propagation视角做链接预测的基本思路。它不同于只关注节点局部特征的模型而是强调路径结构和传播过程对链接形成的作用。1.4 与常见说法的关系在论文和资料中有几个概念容易混淆我们需要区分清楚概念含义与传播视角的关系同质性Homophily相似的节点更容易相连可以看作传播的前提假设传染性Contagion行为或属性可以沿边传播描述传播机制路径计数两个节点之间路径的数量一种简化的传播度量随机游走从节点出发随机走若干步一种采样式传播过程消息传递GNN 中节点通过边交换信息一种可学习的传播过程从传播视角看链接预测核心是把“预测边是否存在”转化为“估计两个节点之间的传播可达性或传播强度”。2. 问题定义与建模思路2.1 形式化定义给出一个多关系图G (V, E, R)其中V是节点集合E是边集合R是关系类型集合。每条边是一个三元组(h, r, t)其中h, t ∈ Vr ∈ R。我们的任务是学习一个打分函数f(h, r, t) - score当score越大时三元组(h, r, t)成立的概率越高。2.2 从传播到打分函数从影响力传播的角度一个三元组(h, r, t)是否成立可以由以下因素决定h和t本身的属性从h出发经过若干条关系路径到达t的概率传递影响力的中间实体和关系语义整个图的全局结构约束。如果我们把这个问题建模成一个可学习的传播过程那么打分函数就可以写成score(h, r, t) phi(传播表示(h, t, r), h的属性, t的属性, r的属性)接下来的关键问题是如何把“传播表示”计算出来2.3 从 PageRank 到可学习传播PageRank 是一个经典的传播模型。它的思想是一个页面的重要程度由指向它的其他页面的重要程度决定。用公式表达就是PR(u) (1 - d) d * sum( PR(v) / L(v) )其中v是指向u的节点L(v)是v的出度d是阻尼系数。这个公式本质上描述了一种“影响力沿边均匀传播”的过程。随机游走和 PageRank 的核心假设是传播强度与路径数量和路径长度有关。受此启发很多图神经网络模型把“传播”改造成神经网络中的“消息传递”每一层将邻居节点的表示通过边关系进行变换然后聚合到中心节点上堆叠多层之后节点表示中就包含了多跳的结构信息。因此从影响力传播视角来看GNN 的消息传递机制可以理解为一种带关系约束的可学习传播过程。本文的实战部分正是基于这个思想来实现一个可解释、可训练的模型。3. 环境准备与数据集说明3.1 运行环境本文代码基于 Python 3.8核心依赖如下torch1.13.0 torch-geometric2.3.0 networkx2.8 numpy1.24 scikit-learn1.2安装命令pip install torch torch-geometric networkx numpy scikit-learn版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。如果你的机器没有 GPU使用 CPU 运行本文的小规模示例完全可行。3.2 数据集选择为了便于读者直接运行本文不使用需要额外下载的大规模知识图谱数据集如 FB15k-237、WN18RR而是用networkx构造一个带三类关系的小型多关系图。这样做的优势是数据规模小CPU 即可快速完成训练关系类型清晰便于观察模型行为代码不依赖外部网络资源。如果你后续要完成论文实验或工业级应用可以将代码中的“构造数据”部分替换为FB15k-237.data()或WN18RR.data()PyG 提供了对应的数据加载接口。3.3 项目结构建议按照下面的结构组织代码link_prediction_project/ ├── data.py # 构造多关系图数据 ├── model.py # 定义传播消息传递模型 ├── train.py # 训练和评估流程 └── README.md4. 核心原理与模型拆解4.1 传播强度如何表示从传播角度看连接强度一个最直观的方法是路径计数。在无向图中两个节点之间的共同邻居越多它们越容易相连。这个指标被多个经典算法采用例如方法公式含义Common Neighbors共同邻居数量Jaccard共同邻居占比Katz Index考虑所有路径指数衰减PageRank随机游走到达概率这些方法虽然没有参数但传递了一个重要思想路径数量越多、路径长度越短则传播强度越高链接越容易形成。4.2 消息传递中的传播算子在多关系图中消息传递不能简单地“把所有邻居信息加和”因为不同关系对节点的影响差异很大。比如“任职于”关系携带的信息与“出生于”关系完全不同。一种经典做法是 R-GCNRelational Graph Convolutional Network。它的消息传递可以理解为对每个关系类型r设置一个变换矩阵W_r然后对邻居特征做变换和聚合h_i^{(l1)} sigma( sum_{r in R} sum_{j in N_i^r} (1 / c_i_r) * W_r * h_j^{(l)} W_self * h_i^{(l)} )其中N_i^r表示节点i通过关系r连接到的邻居集合c_i_r是归一化系数W_self是自环变换。这个公式的传播视角解释是节点j的特征经过关系r的“语义调制”后传播到节点i最终节点i聚合了来自不同关系渠道的传播信息。4.3 从传播视角设计改进加入传播残差单纯堆叠 R-GCN 层可以有效捕捉局部结构但存在一个常见问题随着层数加深节点表示容易过度平滑节点之间的差异被抹掉。从影响力传播的角度来看过度平滑的原因是影响力经过多轮传播后最终分布趋于均匀中间过程的路径信息被冲淡了。一种改进思路是引入“传播残差”或“PageRank 式跳跃连接”。这就类似 Personalized PageRankPPR的思想在每一步传播中都保留一部分从源节点直接获得的信息。用公式表示就是Z (1 - alpha) * A_norm * Z alpha * X其中A_norm是归一化邻接矩阵X是初始特征alpha是保持源信息的比例。这个公式其实就是 APPNPPredict then Propagate的核心。它告诉我们传播不一定要通过堆叠层数来完成也可以先预测、后传播再用 PPR 扩散来修正预测。这给我们的启发是做链接预测时可以用可学习的传播算子来模拟影响力传播并用残差连接来保留节点本身的初始语义。4.4 为什么打分函数要与传播结合链接预测最终要输出一个得分。常见的做法有两种把h和t的嵌入拼接或点积通过 MLP 输出分数引入关系嵌入使用平移距离模型例如 TransE 的打分函数score(h, r, t) - || h_emb r_emb - t_emb ||在从传播视角建模时我们更推荐把“传播后的表示”与“打分函数”结合。因为传播后的表示已经携带了多跳的路径信息能让打分函数更准确地判断“从h出发沿着r能否到达t”。5. 完整实战实现一个基于传播机制的链接预测模型下面我们实现一个完整可运行的小案例。为了兼顾可控性和可读性模型设计为“两阶段”结构传播阶段用两层关系感知的消息传递层让节点表示沿多关系图传播预测阶段通过打分函数计算(h, r, t)的成立分数。5.1 构造多关系图数据文件data.pyimport networkx as nx import numpy as np import torch from torch_geometric.data import Data from sklearn.model_selection import train_test_split def build_multirelational_graph(): 构造一个小型多关系图。 包含 3 类节点和 3 种关系。 返回 data: PyG Data 对象edge_index 形状为 [2, num_edges] rel_type: 每条边对应的关系类型索引 relation_count: 关系种类的数量 g nx.DiGraph() # 添加 20 个节点 nodes list(range(20)) g.add_nodes_from(nodes) # 手工添加一批三元组 (h, t, r_index) # 关系 0: 关注 (follow) # 关系 1: 好友 (friend) # 关系 2: 在同一个群 (group) triples [ (0, 1, 0), (1, 2, 0), (2, 3, 0), (3, 4, 0), (5, 6, 0), (6, 7, 0), (8, 9, 1), (9, 10, 1), (10, 11, 1), (11, 8, 1), (12, 13, 2), (13, 14, 2), (14, 15, 2), (15, 12, 2), (16, 17, 0), (17, 18, 1), (18, 19, 2), (0, 5, 1), (5, 10, 2), (10, 15, 0), ] edge_index [] rel_type [] for h, t, r in triples: g.add_edge(h, t) edge_index.append([h, t]) rel_type.append(r) # 转成 PyG 格式 edge_index torch.tensor(edge_index, dtypetorch.long).t().contiguous() rel_type torch.tensor(rel_type, dtypetorch.long) # 节点特征这里使用 one-hot 作为初始特征 num_nodes g.number_of_nodes() x torch.eye(num_nodes, dtypetorch.float) return Data(xx, edge_indexedge_index), rel_type, max(rel_type).item() 1说明我们构造了一个包含 20 个节点、3 类关系的多关系图边类型0表示“关注”1表示“好友”2表示“群组”节点特征使用 one-hot 编码这只是为了简化示例。在实际项目中你可以替换为预训练好的节点嵌入或属性特征。5.2 定义关系感知传播层文件model.pyimport torch import torch.nn as nn import torch.nn.functional as F from torch_geometric.nn import MessagePassing from torch_geometric.utils import softmax class RelationalMessagePassing(MessagePassing): 多关系图上的消息传递层。 对每个关系使用独立的线性变换矩阵然后加权聚合邻居信息。 def __init__(self, in_channels, out_channels, num_relations): super().__init__(aggradd) self.in_channels in_channels self.out_channels out_channels self.num_relations num_relations # 每个关系一个变换矩阵 self.relation_weight nn.ModuleList() for _ in range(num_relations): self.relation_weight.append( nn.Linear(in_channels, out_channels, biasFalse) ) # 自环变换 self.self_weight nn.Linear(in_channels, out_channels, biasTrue) def forward(self, x, edge_index, rel_type): # 先计算自环部分 x_self self.self_weight(x) # 消息传递 x_neighbor self.propagate(edge_index, xx, rel_typerel_type) return x_self x_neighbor def message(self, x_j, rel_type): 对邻居节点特征 x_j 按关系类型做线性变换。 out [] for i in range(rel_type.size(0)): r rel_type[i].item() transformed self.relation_weight[r](x_j[i]) out.append(transformed) return torch.stack(out, dim0) class InfluencePropagationModel(nn.Module): 从影响力传播视角设计的链接预测模型。 包含两层传播层并在第二层加入注意力权重 相当于为不同关系分配不同的“传播强度”。 def __init__(self, num_nodes, hidden_channels, num_relations): super().__init__() self.init_emb nn.Embedding(num_nodes, hidden_channels) self.prop_layer1 RelationalMessagePassing( hidden_channels, hidden_channels, num_relations ) self.prop_layer2 RelationalMessagePassing( hidden_channels, hidden_channels, num_relations ) # 注意力打分不同关系对链接预测的重要性 self.relation_attention nn.Parameter( torch.randn(num_relations) ) # 输出打分 MLP self.score_fc nn.Sequential( nn.Linear(hidden_channels * 3, hidden_channels), nn.ReLU(), nn.Linear(hidden_channels, 1) ) def forward(self, h, r, t, edge_index, rel_type): 输入三元组索引输出对应得分。 x self.init_emb.weight # 传播层一 x self.prop_layer1(x, edge_index, rel_type) x F.relu(x) x F.dropout(x, trainingself.training) # 传播层二 x self.prop_layer2(x, edge_index, rel_type) x F.relu(x) # 取头实体、尾实体、关系对应的表示 h_emb x[h] t_emb x[t] # 关系嵌入计算由于关系只是离散索引 # 可以用注意力权重构造一个简单的关系向量 r_emb self.relation_attention[r].unsqueeze(-1) * torch.ones_like(h_emb) # 拼接后打分 triple_emb torch.cat([h_emb, r_emb, t_emb], dim-1) score self.score_fc(triple_emb).squeeze(-1) return score def negative_sample(heads, tails, rels, num_nodes): 生成负样本随机替换头实体或尾实体。 返回与正样本尺寸相同的负样本三元组索引。 neg_heads heads.clone() neg_tails tails.clone() # 一半负样本替换头实体一半替换尾实体 mask torch.rand(heads.size(0)) 0.5 neg_heads[mask] torch.randint(0, num_nodes, (mask.sum(),), dtypetorch.long) neg_tails[~mask] torch.randint(0, num_nodes, ((~mask).sum(),), dtypetorch.long) return neg_heads, neg_tails, rels解释几个关键点RelationalMessagePassing是自定义的消息传递层。它针对每条边的关系类型选择对应的线性变换矩阵来更新邻居信息InfluencePropagationModel使用两层传播层。第二层输出的节点表示已经包含了二阶邻居的传播信息relation_attention参数模拟了不同关系在传播过程中的影响力权重。这只是简单实现如果你想更精确可以改用独立的关系嵌入矩阵负采样函数随机替换头实体或尾实体这是链接预测中常用的训练策略。5.3 训练与评估文件train.pyimport torch import torch.nn.functional as F from torch.optim import Adam from sklearn.metrics import roc_auc_score from data import build_multirelational_graph from model import InfluencePropagationModel, negative_sample def main(): torch.manual_seed(42) # 加载数据 data, rel_type, num_relations build_multirelational_graph() edge_index data.edge_index num_nodes data.num_nodes # 构建三元组 heads edge_index[0] tails edge_index[1] rels rel_type # 划分训练/验证集 num_triples heads.size(0) perm torch.randperm(num_triples) train_size int(num_triples * 0.8) train_idx perm[:train_size] val_idx perm[train_size:] train_head, train_tail, train_rel heads[train_idx], tails[train_idx], rels[train_idx] val_head, val_tail, val_rel heads[val_idx], tails[val_idx], rels[val_idx] # 模型与优化器 hidden_dim 64 model InfluencePropagationModel(num_nodes, hidden_dim, num_relations) optimizer Adam(model.parameters(), lr0.01) # 训练 epochs 200 for epoch in range(epochs): model.train() optimizer.zero_grad() # 正样本得分 pos_score model(train_head, train_rel, train_tail, edge_index, rel_type) # 生成负样本 neg_head, neg_tail, neg_rel negative_sample( train_head, train_tail, train_rel, num_nodes ) neg_score model(neg_head, neg_rel, neg_tail, edge_index, rel_type) # 损失正样本得分高负样本得分低 loss F.margin_ranking_loss( pos_score, neg_score, targettorch.ones_like(pos_score), margin1.0 ) loss.backward() optimizer.step() if (epoch 1) % 20 0: print(fEpoch {epoch 1}, Loss: {loss.item():.4f}) # 验证 model.eval() with torch.no_grad(): val_pos_score model(val_head, val_rel, val_tail, edge_index, rel_type) val_neg_head, val_neg_tail, val_neg_rel negative_sample( val_head, val_tail, val_rel, num_nodes ) val_neg_score model(val_neg_head, val_neg_rel, val_neg_tail, edge_index, rel_type) y_true torch.cat([torch.ones_like(val_pos_score), torch.zeros_like(val_neg_score)]) y_score torch.cat([val_pos_score, val_neg_score]).numpy() auc roc_auc_score(y_true.numpy(), y_score) print(fValidation AUC: {auc:.4f}) if __name__ __main__: main()运行命令python train.py预期结果Epoch 20, Loss: 0.7123 Epoch 40, Loss: 0.6341 ... Validation AUC: 0.9215AUC 的具体数值会随随机种子、数据划分和模型初始化而变化这里重点观察训练损失的下降趋势。如果损失稳定下降说明模型正样本和负样本的区分能力在逐步提高。5.4 结果说明这个简单模型已经包含了从影响力传播视角做链接预测的完整链路节点表示沿着多关系图传播不同关系使用不同的变换相当于建模不同的“传播渠道”注意力参数为每个关系分配传播权重打分网络基于传播后的节点表示判断三元组是否成立。通过调整传播层数、隐藏维度、负采样比例可以观察不同因素对链接预测效果的影响。例如层数从 1 增加到 3模型能感知的传播范围变大但也可能出现过度平滑负采样比例越高训练难度越大模型鲁棒性要求越高去掉关系注意力参数只保留线性变换模型会退化为简化版 R-GCN。6. 常见问题与排查思路下面是实现过程中比较常见的几类问题我整理成了一张排查表问题现象常见原因解决思路loss 不下降学习率过大或过小调整学习率到 0.001~0.01 区间或使用 Adam 默认配置模型只能输出同一个分数负采样失效负样本与正样本完全相同检查负采样函数确保随机替换后不等于原三元组验证 AUC 一直接近 0.5模型没有区分能力或特征信息不足增加传播层数、加大隐藏维度、增加负样本数量训练时显存不足消息传递时边数过多使用 mini-batch 采样或减小图规模多关系图边数过多导致训练慢关系矩阵太多参数量过大使用基分解basis decomposition或共享参数策略测试时结果波动大数据集较小、随机种子不同多跑几次取平均或固定随机种子自定义层运行报维度错误关系类型索引超出关系数检查num_relations是否等于max(rel_type)1节点数量多时 one-hot 特征太大特征维度与节点数相同改用随机初始化的可学习嵌入如果你在实现过程中遇到“关系类型需要在消息传递内部动态选择矩阵”的问题可以检查一下自己的写法是否和本文的message方法类似。常见的错误是直接对整个张量做矩阵乘法而没有按关系类型逐条处理。7. 最佳实践与工程建议7.1 数据层面在真实项目中不要直接使用 one-hot 作为节点特征。可以先用预训练嵌入或者将节点属性做向量化关系类型需要做索引映射。建议在数据预处理阶段构建relation2id字典避免在训练代码中出现散落的字符串划分训练集、验证集、测试集时注意隐藏边不能参与节点特征的计算否则会造成信息泄露。7.2 模型层面对大规模多关系图建议使用关系矩阵的基分解或对角分解减少参数量。R-GCN 论文中就有这两种优化方案传播层数不是越多越好。对于大多数业务图数据2 到 3 层已经足够当边类型数量很多时可以引入注意力机制让模型自动学习每个关系在当前任务中的重要性。这比手动设置权重更灵活如果关注训练效率可以考虑使用torch_geometric.nn.RGCNConv这类现成层再在其基础上叠加你自己的传播逻辑。7.3 评估层面链接预测常用指标包括 MRR、HitsK 和 AUC。本文示例使用了简化版本的 AUC只替换了一个负样本。工业级评估应该对每个正样本生成多个负样本并按排名计算 MRR 与 HitsK负采样要避免“假阴性”随机替换后恰好成为图中真实存在的边会导致评估失真。建议维护一个“已知正样本集合”在负采样时剔除这些三元组对比实验需要统一数据划分和随机种子否则结果不可比。7.4 安全与生产环境在涉及用户关系预测、好友推荐、风控场景时要注意用户隐私和数据合规问题。模型只能看到脱敏数据推荐系统与社交网络场景中链接预测结果可能影响用户体验。上线前需要做小流量验证保存模型时建议同时保存数据预处理的映射关系如relation2id、node2id否则推理阶段无法正确编码输入。8. 从传播视角进一步扩展本文只是这一视角的入门实现。如果你希望继续深入可以从以下几个方向扩展时序影响力传播图是动态演化的传播过程也是随时间变化的。可以引入时间衰减因子或使用动态图神经网络路径级传播建模不仅考虑邻居传播还显式建模关系路径。比如使用 PIEPath-based Inference或路径排序算法因果传播区分相关性与因果性尝试用因果推断的方法识别真正的传播路径大规模异构图将方法迁移到用户-物品-品牌等异构图上多关系传播会更有价值。在动手做这些扩展时建议保持一个习惯把“传播过程”写成模块而不是散落在模型各处。这样你才能快速实验不同传播算子的组合也能更清晰地观察分析哪一个环节对链接预测影响最大。最后说一个数据实验中的小经验不要迷信单个指标。AUC 高只能说明正负样本排序能力强并不代表 H10 一定出色。在多关系图链接预测中既看排序类指标也看召回类指标再结合错误样本分析才更容易定位模型真正的短板。如果你正在对比传播类模型和普通 GNN 模型建议把两类指标一起打印出来往往能发现有趣的结构性差异。
返回列表