首页 / 资讯中心 / 文章详情

图神经网络GNN核心体系解读:从模型分类到工程实践

图神经网络GNN核心体系解读:从模型分类到工程实践 ★ FEATURED ARTICLE
图神经网络综述论文阅读笔记从零到一吃透GNN核心体系从去年下半年开始我陆续做了几个图结构数据的项目越做越觉得逃不开GNN这关。不夸张地说图神经网络现在几乎成了处理关系数据的默认选项无论你做社交推荐、分子性质预测还是流量异常检测都能看到相关工作的影子。但真要推荐一篇入门总览绕不开的始终是那篇A Comprehensive Survey on Graph Neural Networks。我前后精读了两遍一边读一边在项目里做验证确实踩了不少坑也积累了一些心得。这篇文章把我的阅读笔记整理出来结合自己的复现经历来讲对准备入门GNN、或者已经把模型跑起来但缺乏体系化认知的朋友应该会有实际帮助。这篇综述最核心的价值是它给了一个清晰的三层分类框架模型分类GCN、GAT、GAU、GAE等怎么归类、任务分类节点级、边级、图级任务各是什么、训练方法分类半监督、无监督、监督各自怎么玩。把这几个维度串起来读其他GNN论文时基本能快速定位它在整个地图里的位置。我下面按自己阅读时的思考顺序把整篇笔记展开写。1. 综述整体框架与核心分类体系1.1 为什么这篇综述能当入门教材用先聊一个我自己的体会。GNN领域的论文有个特点看起来代码都不难但概念极其分散今天一篇GCN变体明天一篇注意力机制改进后天又来个基于图池化的分层模型新入门的人很容易淹没在各种术语里。我最早看单篇论文的时候也是云里雾里总觉得每篇都差不多但又说不出具体差别在哪。这篇综述的价值就在于它把整个领域的模型做了一个很完整的归类。作者没有简单按时间线罗列工作而是归纳出四大模型族图卷积网络GCN、图注意力网络GAT、图自编码器GAE和图循环网络Graph RNN/GNN。每个类别之下又拆出若干变体让人一眼看清楚哪些工作是同一脉络下的演进哪些是真正开辟了新的技术路线。就好比你要了解一座城市导航地图上标明主干道、支路和地标远比只看一堆街景照片更有用。另外综述还梳理了任务类型和训练方式两个维度任务分为节点级Node-Level如节点分类、链接预测、边级Edge-Level和图级Graph-Level如图分类、图生成训练方法则分为半监督、无监督和监督三大类。这个三维坐标一旦建立起来再去读新论文的时候第一件事就是问自己它改了哪个维度改了什么为什么改有了这个问题意识读论文的效率会高很多。1.2 整体内容组织三个维度看GNN我梳理了一下综述的骨架大致是三块内容第一块是基础定义与符号体系。综述在开头统一了图表示法包括邻接矩阵、度矩阵、特征矩阵的符号定义还把图神经网络的一般层间传播规则抽象成一个统一框架。这一部分看着基础但特别重要因为不同论文用的符号不统一经常读着读着发现这个X到底是不是特征这种尴尬问题。这篇综述至少给你一套坐标系后续看其他文章时脑内可以先翻译成统一符号。第二块是模型分类学。综述把已有模型按架构族归类并且对每个模型的核心思想、传播公式、适用的任务场景做了介绍。这块是地图的主体也是我花时间最多的地方。第三块是应用与未来方向。包括在计算机视觉、自然语言处理、推荐系统等领域的典型应用场景以及作者认为领域内还没解决好的挑战比如动态图建模、可扩展性、模型可解释性等。从阅读策略上讲我的建议是第一遍顺着读抓住符号定义和分类框架不用每个公式都推倒第二遍跳着读按自己手头的任务去找对应章节。如果你正在做图分类那就重点看GCN和注意力变体的图级汇聚部分如果你要做链路预测重点看GAE和图自编码器部分。结合任务读综述比通读一遍的效果好得多。1.3 我建立的知识地图速查下面这张表是我读完综述后自己整理的我觉得比单纯摘录笔记更好用。每当看到一篇新论文我会先在表格里找一个位置然后判断这篇论文的创新点落在哪个模块。模型族代表作核心思想典型任务图卷积网络GCN、GraphSAGE、GIN邻居特征聚合谱域或空间域卷积节点分类、图分类图注意力网络GAT、GATv2邻居权重自适应学习节点分类、关系推理图自编码器GAE、VGAE、GraphSAGE的无监督版本编码-重构学习低维嵌入链路预测、节点表示学习图循环网络Graph LSTM、GGNN时序传播可建模迭代消息传递图生成、物理模拟、程序验证当然这四个族之间不是完全互斥的很多模型实际上是混合体比如GraphSAGE既可以归为卷积族又带有采样聚合的思想GAE经常用GCN作为编码器。但正是这种模糊地带意味着研究空间的丰富性——你完全可以把两个族的优势结合起来做出一个新模型。读一篇论文如果能在表格里定位到属于A族但融合了B族的思路你对它的理解就已经超过了很多只会跑代码的人。2. 模型细节拆解和关键机制分析2.1 图卷积网络谱域方法到空间域方法的演进逻辑GCN这块是综述里着墨最多的部分我读的时候特别注意了谱域方法和空间域方法这条演进线。谱域方法的思想是把图信号变换到谱域通过卷积定理定义卷积操作。经典的GCN其实是谱域方法的一个简化版本它用切比雪夫多项式的一阶近似避开了拉普拉斯矩阵特征分解的高昂计算成本。这个设计的巧妙之处在于最终推导出的传播规则非常简单每一层只做一次邻居特征的平均变换加一次线性映射。但代价是模型深度受限我实测下来GCN堆到三层以上性能反而下降这就是所谓的过度平滑问题。邻居信息反复传播后图中相距较远的节点表示会趋向一致从分类角度说就是特征丧失了区分度。空间域方法从另一个角度切入直接在图的拓扑结构上定义聚合操作。GraphSAGE就是一个典型它通过采样固定数量的邻居再对邻居特征做平均、池化或LSTM聚合让模型适用于归纳式学习场景。也就是说训练时见过的节点和测试时新来的节点可以用同一套参数处理这在工业场景里非常重要。GIN图同构网络则关注的是表达能力问题它证明了普通的邻居平均聚合只能达到一阶Weisfeiler-Lehman算法的区分能力必须用求和聚合加多层感知机才能逼近WL测试。读到这里我才意识到为什么有些GNN模型在简单数据集上表现很好一到结构区分度高的任务上就翻车——很多是聚合方式限制死了表达能力。实际使用中我还有一个很深的体会在稀疏图上GCN类模型通常比在稠密图上表现更好因为稀疏意味着每个节点的邻居平均度数低聚合得到的信号噪声较小而稠密图上的GCN非常容易过平滑。如果想在稠密图上用GCN最直接的办法是加残差连接或者随机丢边这一点在后面的问题排查部分我会再展开。2.2 图注意力网络自适应邻居权重背后的直觉图注意力网络GAT的出发点特别直观不同邻居对中心节点的重要性是不一样的。GCN用度矩阵做归一化实际上是在隐式地假设所有邻居同等重要这在很多场景下是不合理的。比如在社交网络中你某个朋友对你的影响力可能远超其他几百个点赞之交。GAT在计算注意力系数时先把节点特征经过一个线性变换然后用一个共享的注意力向量计算两两之间的相关分数再用softmax对所有邻居归一化。这个分数就是聚合时的权重。作者在综述中特别提到这个注意力机制带有一个LeakyReLU非线性激活让负相关也能够被传递——这一点容易被初学者忽略因为大多数默认写法里注意力分数没经过激活时正负数值的大小差异并没有被有效放大。我自己做实验时发现GAT比GCN更容易学到有意义的邻居权重尤其在异质图节点类型多样的图上有明显优势。比如说在风控场景里一个用户的特征可能同时受好友、设备、IP等多个实体影响注意力机制可以自适应地识别出提供关键证据的邻居类型。但GAT的代价是计算量偏大双头注意力在大型图上训练速度明显不如GCN。另外GATv2这种后来的改进版本在综述里也有提及它调整了注意力计算顺序提升了表达力。如果你在项目中感觉GAT注意力学不出来可以试试GATv2的表现有时效果差异非常明显。2.3 图自编码器、图循环网络与池化机制图自编码器的核心思路是编码器-解码器结构。编码器通常是一个GCN或GAT把节点映射到低维向量解码器则通过向量内积或更复杂的函数重构邻接矩阵。训练目标就是让重构出来的邻接矩阵尽量接近原始邻接矩阵。这本质上是一个无监督表示学习方法得到的节点嵌入可以直接用于链路预测、节点聚类或者作为下游任务的特征。我在做链路预测时用过VGAE变分图自编码器它把节点向量建模成高斯分布的参数用重参数化技巧采样对链路预测任务的效果确实比普通GAE要好。但要注意一个细节训练时如果简单地把所有节点对的重构误差都算进损失模型倾向于把邻接矩阵重构得稀疏、所有边概率都压得很低。常见做法是负采样确保模型在正负样本上都有监督信号。图循环网络如GGNN、Graph LSTM的思想则是把消息传递视为一个迭代过程每一轮每个节点聚合邻居信息并更新自己的隐状态类似循环神经网络在时间维度上展开。这类模型适合节点间影响需要多轮传播才能充分建模的任务比如物理系统的动力学模拟、程序验证。不过迭代多轮后同样会遇到信息过度混合的问题计算量也大所以在工业界用的人相对少。还有一个很容易被忽略但又很关键的组件是图池化。图分类任务中模型必须把不同尺寸的图映射到固定大小的表示。综述提到了多种池化策略全局平均池化最简单但可能丢失结构信息DiffPool这类可微分池化可以学习分层分配矩阵把节点映射到若干簇实现层次化图表示SAGPool则用自注意力机制选择重要节点。我实测下来如果图规模不大、任务简单用全局加和池化的效果还过得去一旦图结构复杂节点数量差异大就必须考虑层次池化。这里的经验是先跑一个简单基线再决定是否引入复杂池化不要一上来就堆DiffPool。2.4 从任务视角理解模型选择综述最实用的一点是把任务类型和模型选择对应了起来。节点分类任务通常用GCN、GAT这类直推式或归纳式模型链路预测任务用GAE类模型更自然图分类任务在特征提取后需要图级读出搭配池化层图生成任务则要借助图循环网络或自回归方案逐节点生成。我的理解是没有通吃所有任务的万能模型关键是先搞清楚你的数据长什么样任务核心是判别还是生成推断时是否会遇到没见过的图。把这些弄清楚后再去选模型方向就不会跑偏。比如我最初做图分类时一味参照别人论文里最好的模型配置结果因为数据平均节点数比论文里大一个数量级模型训练慢且效果差。后来换回带层次池化的GCN情况立刻改善。读综述时建立任务维度很多弯路是可以提前避开的。3. 实操事项与训练方式详解3.1 三种训练范式半监督、无监督、监督怎么选综述里把训练方式分为半监督、无监督和监督三类这个分化我觉得非常关键直接决定项目里数据标注成本和模型结构设计。半监督学习是GNN最经典的设定。拿节点分类来说整个图中只有一小部分节点有标签训练时通过图结构把标签信息传播到无标签节点。GCN最初就是在这样的设定下提出的它的损失函数只计算有标签节点的交叉熵但梯度会通过图卷积层传播到无标签节点的表示上。这种做法的妙处是即使一个节点没有标签它的邻居特征和结构信息也能在训练过程中影响模型参数更新。实际项目里标注成本高的场景很常见能用一个几十个标签节点的图训练出像样的分类器这种诱惑力是很大的。无监督训练则更适合标签完全缺失的场景。这时候图自编码器就派上用场了通过重构邻接矩阵或者利用负采样学习节点嵌入学到的向量可以进一步做聚类、最近邻检索或作为下游分类的特征输入。这种方式做出来的嵌入质量我观察下来通常和监督训练有差距但在完全没有标签数据的情况下已经是非常实用的方案。近年来自监督对比学习如GraphCL、GRACE也被综述一带而过提及核心思想是通过正负样本对拉近同类表示、推开不同类表示让模型在无标签数据上仍然学到有区分度的特征。监督训练就是常规的端到端有标签训练适合图级任务如图分类、图回归。这种做法要求的数据标注成本最高但效果也最稳定。实际项目中我的策略是先用无监督在全部数据上预训练得到初始嵌入再用少量标签做微调。这个流程综合了三种训练方式的优点也是我在多个项目中验证过的实用组合。3.2 从零复现一个GCN的实操记录把综述读完真正动手前我建议先从最经典的GCN开始复现。它的传播公式如下[ H^{(l1)} \sigma\left( \tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}} H^{(l)} W^{(l)} \right) ]其中 (\tilde{A}AI)(\tilde{D})是(\tilde{A})的度矩阵。这个公式看起来简单每一步的坑却不少。我用的环境是PyTorch和PyTorch Geometric。核心代码如下import torch import torch.nn as nn import torch.nn.functional as F from torch_geometric.nn import GCNConv class TwoLayerGCN(nn.Module): def __init__(self, in_dim, hidden_dim, out_dim, dropout0.5): super().__init__() self.conv1 GCNConv(in_dim, hidden_dim) self.conv2 GCNConv(hidden_dim, out_dim) self.dropout dropout def forward(self, x, edge_index): x self.conv1(x, edge_index) x F.relu(x) x F.dropout(x, pself.dropout, trainingself.training) x self.conv2(x, edge_index) return x在Cora数据集上这个模型能达到80%以上的测试准确率。但在实际操作中我第一次复现时测试准确率只有75%后来发现是训练轮数太少、早停设得太早。GCN在Cora上通常需要200轮左右才能收敛早停耐心值patience建议至少50轮。数据加载部分PyG封装得很好from torch_geometric.datasets import Planetoid from torch_geometric.transforms import NormalizeFeatures dataset Planetoid(root/tmp/Cora, nameCora, transformNormalizeFeatures()) data dataset[0]这里有个细节提醒如果不做特征归一化Cora这类词袋特征会出现数值范围差异过大的问题有时会导致梯度爆炸。综述的符号体系里经常用X表示特征矩阵但在PyG中特征被存在data.x中邻接关系以edge_index的形式给出两个shape为[2, E]的数组。初学时不适应这个格式很正常但只要明白它存的是边的起点和终点索引逻辑就很清晰。训练循环本身不复杂model.train() optimizer.zero_grad() out model(data.x, data.edge_index) loss F.cross_entropy(out[data.train_mask], data.y[data.train_mask]) loss.backward() optimizer.step()注意这里的data.train_mask它标记了哪些节点参与损失计算。Cora的标准设定是每类20个节点作为训练集共140个标签节点其余用于验证和测试。这个设定是半监督学习的典型代表充分体现了GNN在小标注样本下利用无标注节点结构信息的能力。为了更直观地理解GCN的传播过程我在复现时还手动写了邻接矩阵归一化过程然后对比PyG的内部实现确认归一化系数 (\tilde{D}^{-\frac{1}{2}} \tilde{A} \tilde{D}^{-\frac{1}{2}}) 是否一致。这个验证过程强烈推荐新手做一遍能极大帮助理解公式的实际含义而不仅仅是把模型当黑盒。3.3 实操现场记录可视化Cora节点嵌入的细节在模型训练到150轮左右时我顺手把最后一层之前的节点嵌入用TSNE投影到二维空间做了可视化。Cora数据集包含7个类别的论文理想情况下同类别节点在嵌入空间里应该聚成一团。我在做可视化时发现两个问题。第一个是TSNE的超参数对图形结果影响很大perplexity设得过小时每个类别会碎成许多小簇设得过大时所有类别又会混在一起。我通过尝试perplexity5和30两种情况最终选了视觉上类别边界更清晰的参数组合。第二个问题是由于训练集节点数少有的类别在训练时只见过少量节点嵌入空间的类别边界会模糊这是数据本身的限制不是代码的bug。可视化的价值不仅在于发论文时画一张好看的图更在于帮助判断表示质量。如果同一类的节点在嵌入空间中完全分裂说明模型或许没有学到该类别的典型特征如果不同类重叠严重则可能需要更强的聚合或更大的嵌入维度。这种诊断方式我在亲自实验后觉得非常有价值。4. 训练数据、评估方法与问题排查4.1 常用公开数据集与选择思路综述后半部分介绍了大量公开数据集我先说几个最常见的也是复现GNN论文时绕不开的。对于节点分类Cora、Citeseer、PubMed这三个是经典的引用网络数据集特点是节点数量适中、每个节点带有词袋特征、类别数若干。这里的引用网络中边代表论文之间的引用关系标签是论文的研究方向。这些数据集足够小跑GCN只需要几秒钟非常适合调试模型。对于图分类常用的有MUTAG、PROTEINS、NCI1等它们来自生物化学领域每个图规模不大适合测试池化层和图级读出模块。如果你的任务是对分子性质分类这类数据集是最自然的出发点。对于链路预测常用数据集往往来自社交网络或协同过滤场景比如各类论文里频繁使用的部分社交数据集。这一块我在做实际项目时感触比较深真实场景的链路预测数据往往极其稀疏正样本存在的边远少于负样本不存在的边如何构建负样本对直接影响模型效果。综述里对评估方法的讨论虽然不算长篇大论但足以提醒读者链路预测不能只看AUC还要关注正负样本比例、随机负采样还是难负样本挖掘这些细节会在结果上带来巨大差异。4.2 过平滑、欠拟合和训练不稳定的排查速查表在实际用GNN的过程中我整理了一份常见问题速查表这里直接分享出来。问题现象可能原因排查方法解决方案网络层数加深后效果严重下降过平滑看节点嵌入是否趋于一致加残差连接、PairNorm、减少层数训练损失下降慢学习率不合适打印每层梯度范数调整学习率尝试AdamW在验证集上表现不稳定标签节点太少统计各类别训练节点数调整类别权重、增加标签传播图规模大导致显存溢出batch太大观察显存占用曲线用GraphSAINT、Cluster-GCN采样训练同质图上效果好、异质图效果差聚合方式单一检查邻居类型分布换用GAT或R-GCN测试集上结果和论文差距大数据划分不同对比划分方式使用标准划分或多次随机划分取均值过平滑问题值得单独多说一句。GCN层数超过一定阈值后所有节点表示会趋于一致这本质上是信息传播导致特征被平均得太厉害。在实际项目中如果任务需要多跳邻居信息我的经验是优先考虑跳数连接加残差而不是直接硬堆层数。比如在社交网络节点分类中为了拿到两跳邻居信息可以先用一跳GCN再把原始特征残差拼接回去这样既保留了局部信息又融合了远程结构信息远比三层GCN的效果来得稳定。4.3 训练稳定性的几条独家技巧除了上面表格里的解决方案我再分享几个自己实验里非常有效的土办法。第一个是关于自环和度归一化的细节。GCN之所以要加自环是让中心节点在聚合时保留自身信息否则经过归一化后相当于把自身信息丢了。有些新手复现时会忘记加自环导致模型效果差很多。检查方法也很简单看PyG的GCNConv默认参数add_self_loopsTrue是否保持默认。第二个是dropout的位置。我发现很多人在GNN里把dropout放在ReLU之后这只是约定俗成但放在特征变换之前也有奇效。具体原因在于如果先dropout再做线性变换相当于对特征向量的某些维度进行随机屏蔽如果先变换再dropout屏蔽的则是变换后的信息维度。二者在某些数据集上的差异能达到两到三个百分点强烈建议多试几种组合。第三个是关于损失权重的调整。在半监督节点分类中如果某些类别的样本特别少建议用类别权重或者直接用Focal Loss。我在一个只有5个类别但其中两个类别标签数不超过10的数据集上把交叉熵换成带类别权重的版本准确率直接提升了4%左右。第四个小技巧是小batch训练时的随机性控制。GNN模型在节点维度上的batch之间不是完全独立的邻居采样和批次划分都会带来额外随机性。如果你的实验复现性要求较高建议固定随机种子包括PyG中邻居采样的种子也要固定。这个看似不起眼的行为能让你的最终数字稳定下来而不是每次重启脚本结果都不一样。第五个是嵌入维度设置的经验法则。很多人在GNN里直接把隐藏维度设成16或者64但更合理的做法是从输入特征维度的一半开始尝试然后观测验证集效果走向。我在Cora上试过16、32、64、128维Cora这种小数据在32维就已经接近饱和64维和128维并不会带来更多提升反而增加过拟合风险。5. 真实项目中的技术选型与改进路径5.1 一个通用决策框架GCN、GAT还是GAE读了综述不等于会用真正的考验是在实际项目里做选型。我总结了一个简单的决策框架在过项目方案评审时非常有用。如果任务是节点分类、边分类这种直推式或归纳式任务且图规模较小、特征较强首选GCN因为它结构简单、训练快、效果稳定。如果图中邻居重要性差异大比如异质图、社交网络、风控场景优先考虑GAT或GATv2用注意力机制自适应挑选重要邻居。如果任务是无监督表示学习、链路预测GAE或VGAE是更自然的选择。如果任务涉及动态演化、时序信息Graph RNN这类模型值得考虑或者把时序信息作为额外特征喂给GCN。选择时还需要考虑的一个因素是部署成本。GCN的结构简单在工业界落地容易很多框架里都有现成算子GAT计算成本高在低延迟服务上可能成为瓶颈GAE类模型训练开销大通常离线更新。项目如果对推理延迟敏感结构更简单的模型往往是更务实的选择。5.2 工业场景遇到的新问题及可行解决路径工业场景比论文里的标准数据集复杂得多我总结出三个最常见的新问题以及相应的解决思路。第一个是超大图的可扩展性问题。如果你的图有上亿节点直接在整图上跑GCN完全不现实。综述中也提到了一些采样方法但实际项目里我常用的方案是Cluster-GCN先把图划分成多个簇每个batch训练一个簇内的节点这样内存占用可控还能够利用分布式训练。另一个选择是GraphSAGE的邻居采样模式每层采样固定数量的邻居计算量不随节点总度数增长。第二个是动态图问题。金融交易网络、社交网络都在不断变化边持续增加或删除。传统GNN在静态图上的表现再好也无法适应这种动态性。我最近的实践是用时间戳切片配合增量训练每隔一段时间做一次全量训练生成基础嵌入新数据到来时用GraphSAGE这类归纳模型快速更新受影响节点的嵌入而不用全图重新训练。这套思路在很多工业级图学习框架里已经有成熟实现。第三个是多关系异质图问题。例如电商场景里用户与商品之间既有点击关系又有购买关系这两种关系对推荐的意义完全不同。综述里提到的R-GCN通过为每种关系单独学习变换矩阵来解决这个问题但在关系数非常多的情况下参数爆炸。我在实际项目中用过关系感知注意力机制用一个可学习的映射把关系类型转化成向量与节点特征拼接到一起效果很好且参数增加有限。5.3 未来方向从综述结尾看科研切入点综述最后提到了一些开放性挑战我把它们当作论文选题或项目预研的方向来读。动态图建模依然是难点真正好用且轻量的动态GNN框架还不存在可解释性是另一个大坑图神经网络被当成黑盒使用在很多行业难以落地尤其是医疗、金融这类强监管领域异构图的统一建模也还没有标准答案各种方法都只在特定场景下互有胜负。对我自己来说从综述的未来方向章节挖出切入点比漫无目的地刷论文要高效得多。我的操作方法是这样先选一个你感兴趣且数据易得的方向然后去近期顶会论文库里搜索相关关键词看现有工作的缺陷再回头对照综述里的定位来决定自己的改进重点。这样一来综述的系统性框架、前沿论文的最新进展和实际任务的数据约束三者就串起来了。6. 阅读笔记之外的几条心得把这篇综述读完、代码复现完我最大的体会是读综述不是目的建立自己的图学习知识坐标系才是目的。没有这篇综述之前我读单篇GNN论文总觉得每篇都是独立的新发明很难判断创新的价值有了这个坐标系之后再看到一篇新论文我会先问它在模型族谱中的位置、改动了哪个模块、是否解决了综述里提到的某个挑战。这样读论文的效率明显提高也更容易在具体任务中做出合理的技术选型。另外复盘阅读过程我建议重点关注符号体系和分类框架这两点而不要纠结于每个公式的推导细节。GNN领域的公式推导大多有雷同之处真正影响理解的是不同论文之间概念的关联与差异。把综述里的分类框架建立起来再结合自己的任务去读细节学习成本会低很多。最后分享一个我的习惯读完一篇综述后自己动手画一张思维导图每个模型族下面列出代表性工作标注它们解决了什么问题、关键创新是什么、适合哪些任务。这张图远比笔记更能帮助你形成长期记忆。等到后续读新论文时随时往导图上添加节点你的知识结构就会持续生长。这一点我觉得比单纯多读几十篇论文更值得坚持。
阅读完成 · 觉得有帮助?
咨询建站