资讯动态

GCN链路预测实战:在Cora和Citeseer上从数据划分到评估

发布时间:2026/9/8 12:14:06 来源:尧图企业网站定制
简介这是一份基于图卷积神经网络GCN在Cora与Citeseer数据集上实现链路预测的Python工程面向正在学习图表示学习、网络科学或想掌握GCN落地流程的研究者与开发者。资源聚焦学术引用网络中的节点链接预测任务包含数据预处理、模型构建和训练评估三大模块数据读取与图结构/特征矩阵构造、多层GCN网络定义、负采样与损失计算及AUC/准确率评估。全部代码浓缩于3个Python脚本中压缩包仅7KB轻量易读目前已有3067人学习下载。通过该资源读者可清晰看到GCN如何聚合邻居信息生成节点嵌入并将其用于未知链接存在概率的预测同时理解Cora与Citeseer两类经典数据集的图结构特点为后续开展图神经网络相关研究或竞赛提供可直接改写的基线工程。 第一次在Cora上跑通GCN节点分类后我接到的下一个问题就是能不能用GCN预测“两篇论文之间未来会不会出现引用关系”也就是链路预测。当时我以为这只是把分类头换成输出一个边概率真正上手后才发现麻烦不在模型而在数据准备和评估方式——Cora和Citeseer这两个数据集虽然小却把链路预测最容易踩的坑都藏了一遍。这篇文章就写给刚学完GCN基础、想在Cora和Citeseer上实现链路预测又不想盲调库的你。我会从任务定义、边划分、负采样、模型搭建到训练评估把每一步为什么这么做讲清楚并附上可复现的PyTorch风格代码。读完你不仅能跑通一条链路预测流程还能避开那些网上教程里不会明说的坑。1. 引文网络与链路预测先想清楚要预测什么1.1 Cora和Citeseer的数据结构Cora和Citeseer是图学习领域出镜率最高的两个引文网络数据集。Cora包含2708篇机器学习方向的论文Citeseer包含3327篇论文每篇论文是一个节点论文之间的引用关系是边节点特征用词袋模型表示Cora是1433维Citeseer是3703维另外每个节点还有一个所属类别标签。很多教程提到它们都是为了做节点分类但我这次关注的是边——即如何预测两个节点之间是否存在引用关系这正是链路预测的目标。需要留意的是Cora和Citeseer的原始引用是有向的但GCN在传播时通常会把它们当作无向图也就是把每条引用边视为双向边。这样做的理由是在引文网络中施引与被引其实都隐含了研究主题的相关性双向传播能更充分地利用邻域信息。1.2 链路预测的任务定义与信息泄露风险链路预测标准的定义是给定图的一部分已知边预测那些未知的边。在Cora这样的场景里最实际的理解是“这篇论文和那篇论文是否应该存在引用关系”。把这个目标拆开你会发现它比节点分类多了一个关键操作要求模型对任意一个节点对输出一个分数而不是对单个节点输出类别。也正是这个“节点对”级别的预测让链路预测特别容易信息泄露。一个常见但不自知的错误是验证集和测试集的正边还留在训练时GCN的消息传递范围内模型在计算节点表示时已经“看见”了这些边最后在测试集上的AUC会虚高。严格的做法是在训练时使用剔除验证/测试边之后的子图做传播至少要在报告结果时说明你用的是哪种模式。先把任务定义想清楚很重要因为后面所有的数据划分、模型设计和评估指标都是围绕“如何保证待预测的边不被偷看”来组织的。2. 数据准备与边划分链路预测的第一道坎2.1 边集划分的策略与比例做节点分类时划分的是节点做链路预测时划分的是边。我通常把现有的边按85%训练、5%验证、10%测试随机划分同时固定随机种子避免每次跑出来的划分完全不同。这里有个容易被忽视的点划分后训练子图中可能会有节点变成孤立点。尤其当某条边是某个节点的唯一连接、又恰好被分到测试集时训练节点里就出现了一个没有邻居的孤立节点。两层GCN下它只能靠自身特征更新表示一层时它甚至更新不了。所以每次划分后我都习惯性地检查一下训练子图的每个节点度是否都大于0这一步很少有人写进博客却是排查“某些节点嵌入总是不动”的关键。2.2 负采样常见做法与容易犯的错链路预测的监督信号来自正边和负边。正边是图中真实存在的边负边则是“并不存在的节点对”通常需要随机采样。最朴素的做法是随机抽两个节点如果它们之间没有边就作为负样本。第一次实验时我犯过一个典型的错误负采样时只排除了训练正边没有排除验证和测试正边。这会导致同一对节点在验证集里被当作“应预测为正”的边在负样本池里又被当成“不存在的负边”模型学到的是自相矛盾的信号最后指标忽高忽低。正确的做法是先把所有正边trainvaltest收集成一个全量集合采样负边时只从补集里抽并且始终避开对角线上的自环节点对。负采样比例方面我一般先用1:1也就是负边数量等于正边数量。如果想更接近真实场景可以提高到2:1或5:1代价是训练时间变长而且AUC的绝对数值可能会变化做对比实验时要固定这个比例。2.3 传播图中该不该保留验证和测试边这一节是很多人争论的点。在Kipf和Welling那篇经典GCN论文的节点分类实验里整个图的邻接矩阵在训练时就是已知的GCN利用的是全图信息测试节点的标签不被使用但结构信息被使用了。到了链路预测如果把测试边保留在传播用的邻接矩阵里模型的节点表示确实隐含了“这条边存在”的信息理论上会高估性能。实际使用时我给自己定了一个规则如果做消融实验或写论文级别的对比使用“训练子图传播”保证编码器看不到任何val/test边如果只是想快速验证模型有没有写对可以用全图传播但心里要清楚指标会比严格模式高一点。我也试过两种模式的差别在Cora上全图传播的AUC可能比严格模式高0.01到0.02数量级上不算吓人但在评估代码里混着用会让人误判改进方向。建议代码中把传播图的邻接矩阵单独建一个变量命名成adj_train和adj_full避免后来自己都分不清用的是哪一个。3. 模型搭建两层GCN编码器点积解码器3.1 GCN为什么能用于链路预测GCN解决的核心问题是如何让节点表示包含邻居信息。它的传播规则可以写成H^(l1) ReLU( A_norm H^(l) W^(l) )其中A_norm是加了自环并进行对称归一化后的邻接矩阵。用生活化的类比说每一层GCN相当于让每个节点去“问”一遍自己的邻居把邻居的特征加权汇总再和自己原来的特征一起做一次线性变换。两层GCN执行下来每个节点的表示就包含了它两跳以内的结构和特征信息。链路预测需要的是“节点对”的表示但GCN给的是“单节点”的表示。所以我们需要一个解码器把两个节点的嵌入结合起来输出一个分数。这是链路预测与节点分类在模型结构上最直接的区别。构造这个A_norm时有几个实现细节值得注意。以稠密邻接矩阵为例import torch def normalize_adj(adj, num_nodes): adj adj torch.eye(num_nodes) # 加自环 deg adj.sum(dim1) deg_inv_sqrt deg.pow(-0.5) deg_inv_sqrt[torch.isinf(deg_inv_sqrt)] 0. # 孤立点处理 return deg_inv_sqrt.view(-1, 1) * adj * deg_inv_sqrt.view(1, -1)这段代码里最容易被忽略的就是torch.isinf那一行。Citeseer数据里真的存在度为零的节点如果不处理A_norm里会出现NaN模型loss直接不下降。3.2 解码器的三种选择GCN编码器输出的是节点嵌入而预测边需要的是边的得分。我把常用的解码器做了个小对比解码器得分公式参数量特点点积内积z_u^T z_v0简单高效适合稀疏图默认首选双线性z_u^T W z_vd*d表达能力强小数据上容易过拟合DistMultz_u^T diag(w) z_vd常用于知识图谱关系建模在Cora和Citeseer这种只有几千条边的小图上我通常先试点积。原因很简单GCN编码器本身已经通过消息传递把图结构压缩进了嵌入解码器不需要特别复杂如果一开始就上双线性参数量增大训练集AUC会很好看验证集AUC却往往不涨反而要花更多时间调dropout和weight decay。如果后续想提高上限再用双线性并配上更强的正则化。3.3 训练主循环的完整代码下面给一份可以直接跑的PyTorch风格核心代码省略了数据加载细节重点展示链路预测特有的训练循环import torch import torch.nn as nn import torch.nn.functional as F class GCNEncoder(nn.Module): def __init__(self, in_dim, hidden_dim, out_dim, dropout0.5): super().__init__() self.gc1 nn.Linear(in_dim, hidden_dim) self.gc2 nn.Linear(hidden_dim, out_dim) self.dropout dropout def forward(self, x, adj_norm): x F.relu(self.gc1(x)) x F.dropout(x, self.dropout, trainingself.training) x torch.spmm(adj_norm, x) x self.gc2(x) return x def decode(z, edge_index): src, dst edge_index return (z[src] * z[dst]).sum(dim1) def loss_fn(pos_score, neg_score): pos_loss F.binary_cross_entropy_with_logits(pos_score, torch.ones_like(pos_score)) neg_loss F.binary_cross_entropy_with_logits(neg_score, torch.zeros_like(neg_score)) return pos_loss neg_loss训练时只需要把正负边的得分拼起来计算交叉熵然后反向传播opt torch.optim.Adam(model.parameters(), lr0.01, weight_decay5e-4) for epoch in range(200): model.train() opt.zero_grad() z model(x, adj_train_norm) pos_score decode(z, train_pos) neg_score decode(z, train_neg) loss loss_fn(pos_score, neg_score) loss.backward() opt.step()这里有几个值得注意的细节一是传播图中我用的是adj_train_norm它只包含训练边二是负边train_neg已经提前避开了所有正边三是GCN层数控制在两层不在代码里堆砌更多层。4. 训练配置与超参数从能跑通到结果可用4.1 默认配置与为什么这么定对于Cora和Citeseer我常用的默认配置如下超参数推荐值说明层数2两层能覆盖两跳邻域继续加深容易过平滑隐藏维度3216也能跑32在两种数据上更稳激活函数ReLU简单有效dropout0.5主要作用在输入和第一层之间学习率0.01Adam默认比较合适weight decay5e-4轻微正则防止训练AUC过高负采样比例1:1正负样本数量一致early stoppingpatience50观察验证集AUC不只看训练损失隐藏维度为什么选32而不是128Cora的特征是1433维本身信息量足够但样本量有限隐藏维度太大意味着参数更多在几千条边上非常容易过拟合。我试过128维的版本训练集AUC很快就接近1验证集AUC反而比32维低两三个点这就是过拟合的典型信号。4.2 Early Stopping与验证集选择链路预测里最常见的问题不是模型不收敛而是收敛太快、过拟合太快。第1个epoch训练AUC就开始飙升第50个epoch时验证集AUC已经不再上涨。因此直接用200个epoch跑完并汇报最后一个epoch是不合理的应该用验证集AUC做early stopping保存验证集上表现最好的模型再在测试集上评估。一个容易忽略的小细节是验证集也要配固定的负样本。如果每次验证时重新随机采样AUC的波动会很大early stopping的步数判断也不稳定。我的做法是初始化时就把验证正边、验证负边全部固定下来连同测试负样本一起放入一个字典后续所有epoch都用同一批数据评估。5. 实验结果解读与常见坑5.1 一组可供参考的复现经验值严格按照上面配置在我自己的复现里Cora的测试AUC大约在0.92到0.93之间AP在0.92左右Citeseer要略低一些测试AUC在0.90到0.91之间。这个量级和GAE、VGAE论文里报告的结果差不多因为GAE实际上就是“两层GCN编码器加内积解码器”的判别式版本VGAE是它的变分扩展。Citeseer比Cora难一点原因主要是它的特征维度更高3703维稀疏度也更大部分节点处于比较边缘的位置。如果发现Citeseer的指标明显低于Cora先不要急着怀疑模型先检查数据处理阶段是否把孤立节点或非连通子图处理干净。5.2 容易翻车的四个细节邻接矩阵归一化时除零。Cora和Citeseer原始数据都可能有度为零的孤立点构造A_norm时如果直接用度矩阵求负二分之一次方会出现inf或NaN。解决方法是先加自环再对度向量中inf的位置做置零处理。随机种子不固定。链路预测本身就包含边划分和负采样两个随机过程不固定种子会导致每次结果相差很大也没法对比改进是否有效。建议在数据加载、边划分、负采样和模型初始化四处在代码开头都设置随机种子。评估时负样本不一致。训练、验证、测试各用什么负样本必须提前定好测试阶段固定一批较大的负样本多次运行取平均会更稳定。如果每次评估都重新采样AUC波动可能达到正负0.02。只看准确率不看AUC。正负边数量在1:1时准确率还有一定意义一旦调整了采样比例或面对真实的不平衡数据准确率会严重失真AUC和AP才是更稳的排序类指标。这四个细节我基本都亲身踩过。尤其是除零那块第一次在Citeseer上跑的时候一个节点度为零我盯着loss半天没反应最后查数据才发现是邻接矩阵里有NaN浪费了不少时间。如果后续想扩展可以尝试把编码器换成GAT或者把解码器换成双线性并加上更强的正则化也可以试试用训练子图传播对比全图传播观察信息泄露对指标的影响。Cora和Citeseer就像图神经网络领域的Hello World把它们吃透再去接大规模数据会从容很多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价