外观
图神经网络
一句话定义:图神经网络(Graph Neural Network, GNN)是直接在图结构数据上运行的神经网络——它让每个节点通过"收集邻居信息、更新自身表示"的方式来学习,从而把节点、边、乃至整张图编码成低维向量。如果说 CNN 是为"像素网格"设计的神经网络,RNN/Transformer 是为"序列"设计的,那么 GNN 就是为关系设计的:它的每个运算都建立在"谁和谁相连"之上。
现实世界充满了图:社交网络是"人—人"图,分子是"原子—化学键"图,知识图谱是"实体—关系"图,电网、交通路网、蛋白质相互作用网络都是图。这带来一个巨大的落差:深度学习在图像、文本上攻城略地,却无法直接用卷积核或注意力矩阵处理一张"没有固定网格、节点数量不固定、顺序也不固定"的图。GNN 填上的正是这个缺口——它把深度学习带进了关系数据时代。它的数学基础、参数更新方式与监督框架,和你在什么是机器学习中学到的完全一致,只是数据形态从"矩阵"变成了"图"。
一、图数据的问题定义
1. 什么是图
图(Graph)由两部分组成:
图 G = (V, E)
V:节点集合(vertex / node),如用户、原子、实体
E:边集合(edge),如关注关系、化学键、三元组中的关系图可以按边的属性继续细分:
| 类型 | 边是否有方向 | 边是否有权重 | 例子 |
|---|---|---|---|
| 无向无权图 | 否 | 否 | 朋友关系、蛋白质相互作用 |
| 有向无权图 | 是 | 否 | 微博关注、网页链接、引用关系 |
| 无向有权图 | 否 | 是 | 交通网络(边权=距离)、相似度网络 |
| 有向有权图 | 是 | 是 | 资金转账(方向+金额) |
| 异质图 | 节点/边有类型 | — | 知识图谱(实体类型 + 关系类型) |
与图像、文本相比,图有三个"反深度学习直觉"的性质:
- 没有固定网格:不能把一个节点放进"像素 (i,j)",图结构本身是数据的一部分;
- 尺寸可变:每张图的节点数、边数都不同;
- 排列不变性:把节点重排编号,图还是同一张图——模型必须对这种重排鲁棒。
2. 三大类任务
按"预测的对象落在图的哪一级",图上的监督任务分为三类:
| 任务级别 | 预测对象 | 典型问题 | 例子 |
|---|---|---|---|
| 节点级 | 单个节点 | 节点分类、节点回归 | 判断社交网络中某用户是否为机器人;预测某原子是否影响分子毒性 |
| 边级 | 一对节点之间 | 链接预测、边分类 | 预测两人是否会成为好友;预测两个药物是否互相作用;知识图谱补全 |
| 图级 | 整张图 | 图分类、图回归 | 判断一个分子是否可穿越血脑屏障;预测一个代码函数是否含漏洞 |
其中节点分类是"半监督"的经典场景:图上有部分节点有标签,其余没有,模型利用图结构把标签"扩散"出去——这正是 GNN 最擅长的能力。链接预测则是"把边当作标签":从图中挖掉一部分边作训练标签,预测剩下的边。理解这三种任务,才能选对模型输出层:节点分类用每个节点接 softmax,图分类在整图读出一个向量再接分类头。
二、图的表示:邻接矩阵与特征
要让深度学习处理图,第一步是把它变成张量。最标准的表示是邻接矩阵(Adjacency Matrix):
A B C D E
A [0 1 1 0 0]
B [1 0 0 1 0] A_ij = 1 ⇔ 节点 i 与 j 之间有边
C [1 0 0 1 0]
D [0 1 1 0 1] —— 无向图是对称矩阵;加权图用权重代替 0/1
E [0 0 0 1 0]配合邻接矩阵的两个重要对象:
- 度矩阵 D:对角矩阵,
D_ii = Σ_j A_ij,即节点 i 的邻居个数(度)。度是一个节点在网络中"重要程度"的一阶度量,hub 节点的度远高于平均。 - 特征矩阵 X:形状为 n×d,第 i 行是节点 i 的 d 维特征。特征的来源因领域而异:社交网络可以是用户画像向量,分子可以是原子类型 one-hot(C、N、O…),学术网络可以是论文的词袋向量。
有了 A 和 X,一个朴素的想法是直接把 X 喂给 MLP——但这样浪费了图结构:MLP 只看"每个节点自己的特征",节点之间完全独立,邻居信息一点没用上。另一个朴素想法是把 A 和 X 拼接起来喂给全连接层——但这样有两个致命问题:
- 排列敏感性:重新编号节点会重排矩阵的行列,同样的图会得到完全不同的向量;
- 参数不可共享:不同图的节点数不同,无法用固定大小的权重。
GNN 的全部设计,都是在这两个约束下寻找"既利用结构、又对节点重排不变"的运算。好消息是:"聚合邻居信息"这个操作天然满足排列不变性——把邻居特征求和、求平均,与邻居的遍历顺序无关。GNN 正是从这里出发的。
三、核心思想:消息传递(Message Passing)
1. 直觉
想象你要给图上的每个节点学一个向量表示,使它"知道自己处在什么位置、和谁相连"。一个朴素但强大的想法是迭代:
每一轮,每个节点都向邻居发送自己的表示(消息),同时接收所有邻居发来的消息,把它们聚合成一条新消息,再与自己的表示结合,更新自己。
这就像一个村子里的消息扩散:第一轮你只知道邻居在说什么,第二轮你知道了"邻居的邻居"在说什么。轮数越多,每个节点掌握的"视野"越大。这个机制叫消息传递(Message Passing),它是几乎全部 GNN 的共同骨架:
第 0 层: ① ② ③ ④ 每个节点只有自身特征 h⁰
\ /│\ │/
第 1 层: ①′ ③′ ①′ = 聚合(②,③) 与自身特征
/│\ /│\
第 2 层: … ②″ … ②″ 看到了二跳内的所有信息2. 形式化定义
设 h_v^(l) 是节点 v 在第 l 层的表示,N(v) 是 v 的邻居集合。一层消息传递可以写成一个通用模板:
h_v^(l+1) = UPDATE( h_v^(l) , AGGREGATE( { h_u^(l) : u ∈ N(v) } ) )
↑ 更新自己 ↑ 聚合邻居的消息
第 l+1 层 函数 聚合函数 (求和/平均/最大值…)两层各司其职:
- AGGREGATE(聚合):把邻居们的向量"合并"成一个向量。它必须对邻居顺序不敏感(排列不变)——求和、平均、取最大值都满足,而"按顺序拼接"不满足。
- UPDATE(更新):把"自己的旧表示"和"聚合来的邻居信息"融合成新表示,通常是拼接后过一个带非线性激活的全连接层。
3. 多层堆叠 = 感受野扩张
理解 GNN 层数最关键的一句话:
k 层 GNN 之后,节点 v 的表示编码了以 v 为中心、半径 k 跳内的整个子图结构。
与 CNN 的感受野类比:
| CNN | GNN | |
|---|---|---|
| 基本运算 | 卷积核扫过像素邻域 | 聚合函数扫过图邻域 |
| 感受野 | 核大小 × 层数 | 跳数(1 层 = 1 跳邻居) |
| 数据形态 | 固定网格 | 任意拓扑 |
| 权重共享 | 卷积核在全图共享 | 变换矩阵 W 在所有节点共享 |
第一层只能看到直接邻居,第二层能看到二跳(通过中间节点),层数越多,每个节点的表示包含的"大局信息"越多。所有代表模型——GCN、GraphSAGE、GAT、GIN、MPNN——都只是对上面那个模板中 AGGREGATE 和 UPDATE 的具体实例化。这个统一视角来自 Gilmer 等人 2017 年提出的 MPNN(Message Passing Neural Network)框架,它把聚合拆成"消息函数 + 聚合函数 + 更新函数"三段,证明了一大类图模型只是同一框架的不同配置。
4. 最小实现
剥掉所有封装,消息传递本身只有几十行代码(PyTorch 风格):
python
import torch
def message_passing_round(h, adj, W, sigma):
"""h: n×d 节点特征; adj: n×n 邻接矩阵; W: 变换矩阵"""
msgs = adj @ h # ① 每个节点收到邻居特征的总和(消息)
out = sigma(msgs @ W) # ② 变换 + 非线性(更新)
return out # 一层 GNNadj @ h 这一行就是"聚合邻居"——矩阵乘法把每个节点的邻居特征累加起来。它高效、可并行、且天然满足排列不变性。接下来几节的 GCN、GraphSAGE、GAT,全都围绕"如何更好地完成这两步"展开。
四、GCN:谱方法的落地
GCN(Graph Convolutional Network,图卷积网络) 由 Kipf & Welling 在 2017 年提出,是传播最广、最经典的 GNN。理解它需要一条从"谱图理论"到"一阶近似"的推导链。
1. 谱方法的思想:把图变换到频域
图像卷积为什么能局部平滑、去噪?因为卷积定理:时域卷积 = 频域逐点相乘。图的谱方法把同一套思想搬到图上:对图拉普拉斯矩阵
L = D - A (拉普拉斯矩阵,图论中"离散二阶导数")做谱分解 L = UΛUᵀ,U 的列就是图的"频域基"。于是图信号的卷积可以写成:先把信号 x 投影到频域(x̂ = Uᵀx),在频域逐分量缩放(乘上滤波器 gθ),再投影回空间域。Defferrard 等人(2016)提出的 ChebNet 用切比雪夫多项式逼近这个滤波器,避免了昂贵的特征分解,是"谱方法可落地"的关键一步。
2. GCN:一阶近似的巧思
GCN 论文的核心洞察是把滤波器限制为一阶,配合两个技巧,得到了一个异常简洁的传播规则:
H^(l+1) = σ( Â · H^(l) · W^(l) )
其中:
à = A + I —— 给每个节点加自环(让更新时"记得自己")
D̃ = diag(Σⱼ Ãᵢⱼ) —— 加自环后的度矩阵
 = D̃^(-1/2) · à · D̃^(-1/2) —— 对称归一化邻接矩阵拆开看每一步的含义:
- 自环
A + I:否则聚合结果里"自己的信息"占比会随度数变化,加自环让每个节点总能在更新时保留自身表示; - 对称归一化
D̃^(-1/2)(·)D̃^(-1/2):每个节点的贡献按度缩放——高连接度的 hub 节点的消息被摊薄,低度节点被放大,防止聚合结果被度数支配、也防止数值不稳定; H·W:每个节点把自己的特征经过同一个权重矩阵 W 变换——这正是"权重共享",与 CNN 卷积核在全图共享一脉相承。
于是每一层 GCN 做的其实就是"邻居特征的加权平均(权重由结构决定),再过一层线性变换和非线性激活"。以节点 i 的更新为例,等价地写成分量形式:
h_i^(l+1) = σ( Σ_{j ∈ N(i) ∪ {i}} (1/√(d̃ᵢ·d̃ⱼ)) · W·h_j^(l) )python
import torch
import torch.nn as nn
def normalize(A):
"""构造对称归一化邻接矩阵 Â"""
A_tilde = A + torch.eye(A.shape[0])
d = A_tilde.sum(dim=1)
D_inv_sqrt = torch.diag(d ** -0.5)
return D_inv_sqrt @ A_tilde @ D_inv_sqrt
class GCNLayer(nn.Module):
def __init__(self, in_dim, out_dim):
super().__init__()
self.W = nn.Linear(in_dim, out_dim)
def forward(self, x, A_hat):
return A_hat @ self.W(x) # 聚合(结构加权)+ 特征变换
class GCN(nn.Module):
def __init__(self, nfeat, nhid, nclass):
super().__init__()
self.layer1 = GCNLayer(nfeat, nhid)
self.layer2 = GCNLayer(nhid, nclass)
def forward(self, x, A_hat):
h = torch.relu(self.layer1(x, A_hat))
return torch.log_softmax(self.layer2(h, A_hat), dim=1)3. 半监督节点分类
GCN 论文的经典设定是半监督节点分类:Cora、CiteSeer、PubMed 等引文网络上,每篇论文是一个节点、引用关系是边、论文主题是标签,但只有少量节点有标签(Cora 每类仅 20 个)。GCN 的做法是在整个图上训练:没有标签的节点的表示也参与前向传播(它们的邻居有标签,反向传播会把梯度传到这些节点),这等于让结构信息充当了"标签扩散"的通道。论文报告 Cora 上 81.5% 的准确率,明显超过当时的特征方法。这个设定至今仍是评估 GNN 的标准基线之一。
一个直觉
为什么"结构"本身就能提供监督?在引文网络中,论文的主题高度局部聚集——同一主题的论文互相引用。于是"邻居是什么主题"对"我是谁"是极强信号。GNN 学的就是把这种同质性(homophily)规律编码进表示。这也预告了后面的一个坑:当图是异质性结构(如欺诈网络,邻居往往不同类)时,朴素 GNN 会失效。
五、GraphSAGE:采样与归纳
GCN 有两个实用痛点:① 整个训练过程需要全图的邻接矩阵,图一大内存就爆炸;② 它是**转导式(transductive)**的——训练时见过的节点才能做预测,新节点(如刚注册的用户)无法即时推理。GraphSAGE(Hamilton 等,NeurIPS 2017)同时解决了这两个问题。
1. 归纳学习与邻居采样
GraphSAGE 的核心改动是:不聚合全部邻居,而是每次随机采样固定数量(如 25 个)的邻居。好处有三:
- 内存可控:每步只需要一小块子图,图再大也能训练;
- 归纳能力:模型学到的是"如何聚合邻居"这个函数,而不是"某个节点的表示"——新节点只要算出特征、连好边,立即可以推理。这让 GraphSAGE 能处理训练时完全没见过的图(这就是归纳式 / inductive,与转导式相对);
- 方差可作正则:采样本身带来随机性,等价于一种隐式正则化。
2. 三种聚合器
GraphSAGE 的更新规则在通用模板上加了"拼接 + 归一化":
h_N(v)^(k) = AGGREGATE_k( { h_u^(k-1) : u ∈ N_sample(v) } )
h_v^(k) = σ( W^(k) · CONCAT( h_v^(k-1), h_N(v)^(k) ) )
h_v^(k) = h_v^(k) / ‖h_v^(k)‖₂ ← L2 归一化,稳定训练"自己"和"邻居"被分开编码后再拼接,这让模型能显式区分"我是谁"与"我周围是谁"。论文比较了三种聚合函数:
| 聚合器 | 做法 | 特点 |
|---|---|---|
| Mean | 对邻居向量取平均 | 简单快速,等效于 GCN 的简化版 |
| LSTM | 邻居随机打乱后过 LSTM | 表达能力更强,但牺牲排列不变性(靠随机打乱弥补)、速度慢 |
| Pool(max-pooling) | 每个邻居过共享 MLP 后逐维取最大 | 平衡表达力与效率,实践中常用 |
3. 训练目标
GraphSAGE 支持监督与无监督两种训练。无监督损失使用图邻接性作为自监督信号:让相连节点的表示更接近、随机负采样节点的表示更远(本质上是一个 skip-gram 式的目标)。这样学到的表示可以复用到多种下游任务,是"图预训练"思想的早期实践。
python
def sage_update(h_v, h_neighbors, W, sigma):
h_agg = h_neighbors.mean(dim=0) # Mean 聚合
h_new = sigma(W @ torch.cat([h_v, h_agg])) # 拼接 + 变换
return h_new / h_new.norm() # L2 归一化GraphSAGE 家族中最著名的工程变体是 PinSage——Pinterest 用它在大规模商品-用户图上做推荐(详见应用场景),在十亿级边规模上实现了工业落地。
六、GAT:注意力机制接管权重
GCN 的聚合权重完全由图结构决定:1/√(d̃ᵢd̃ⱼ) 只依赖两个节点的度。但真实图里,邻居的重要性并不一样——一个用户的所有朋友中,可能只有一个朋友的兴趣与你高度一致。GAT(Graph Attention Network,图注意力网络,Veličković 等,ICLR 2018) 让权重改由内容决定:像 Transformer 学"哪些词值得关注"一样,学"哪些邻居值得关注"。
1. 注意力系数的计算
对节点 i 的每个邻居 j,GAT 先算一个"相关度打分",再在邻居集合内做 softmax 归一化:
e_ij = LeakyReLU( aᵀ · [ W·h_i ‖ W·h_j ] ) —— 拼接后打分
α_ij = exp(e_ij) / Σ_{k∈N(i)} exp(e_ik) —— 邻居间归一化
h_i' = σ( Σ_{j∈N(i)} α_ij · W·h_j ) —— 按注意力加权聚合其中 a 是一个可学习的注意力向量,‖ 表示拼接。注意归一化发生在每个节点的邻居集合内部(Σ_k∈N(i)),而不是全图——这保证了与 GCN 同样的"每个节点只关心自己的邻域"的局部性。
2. 多头注意力
GAT 直接借用了 Transformer 的多头技巧:用 K 组独立的注意力头并行计算,中间层把结果拼接,最后一层取平均:
h_i' = ‖_{k=1..K} σ( Σ_{j∈N(i)} α_ij^(k) · W^(k) · h_j ) (中间层:拼接)
h_i' = σ( (1/K) Σ_{k=1..K} Σ_{j∈N(i)} α_ij^(k) · W^(k) · h_j ) (输出层:平均)多个头相当于"从多个关系视角同时看邻居",提高稳定性与表达力。
python
def gat_update(h_i, h_neighbors, W, a, sigma):
Wh_i = W @ h_i
Wh_j = W @ h_neighbors
e = (a @ torch.cat([Wh_i, Wh_j], dim=1)).squeeze() # 打分
alpha = torch.softmax(e, dim=0) # 邻居间归一化
return sigma((alpha * Wh_j).sum(dim=0)) # 加权求和3. GCN vs GAT:一张表看清
| 维度 | GCN | GAT |
|---|---|---|
| 聚合权重来源 | 图结构(度) | 内容(注意力) |
| 权重是否可学习 | 否,预先算好 | 是,随训练优化 |
| 是否依赖邻接矩阵 | 是(需要 Â) | 否,只需边表 |
| 对不同邻居的区分 | 只能按度区分 | 可按内容精细区分 |
| 计算开销 | 更低 | 多头略高 |
| 适用场景 | 基线、同质图 | 邻居重要性差异明显的图 |
一个经验规律:GAT 通常不会比 GCN 差太多,但在"少数重要邻居决定结果"的图上优势明显;代价是参数更多、更易过拟合,在小图上需要更强的正则。注意力可视化还能给出"模型认为谁最关键"的线索——但要小心,注意力权重 ≠ 因果解释,这与Transformer 与 NLP中"注意力不等于解释"的告诫一脉相承。
七、图池化与图级任务
前面几节讨论的都是节点级表示。图分类(如"这个分子是否有毒")需要把整张图变成一个向量。这有两种路线:全局读出与分层池化。
1. 全局读出(Readout)
最朴素的做法是对所有节点表示做一次排列不变的聚合:
h_graph = READOUT( { h_v : v ∈ V } ) —— mean / sum / max / 或三者拼接"对所有节点取平均"看起来简单,却有一个深刻陷阱:平均会丢失结构信息。"两个环"和"一个四环"的平均特征可能完全相同。这引出聚合函数的选择问题:sum 保留的信息最多,mean 次之,max 最少。我们在表达力一节会看到,这个选择直接决定模型在图同构问题上的上限。
2. 分层池化:把图压缩成"图"
更强大的做法是分层池化(Hierarchical Pooling):像 CNN 的空间下采样一样,逐层把图缩小,保留关键结构。代表工作是 DiffPool(Ying 等,NeurIPS 2018):学一个分配矩阵,把节点软分配到若干"簇",再把簇当作新节点、簇间连边当作新边,图由此逐层变小。TopKPool 则是硬选择:每层按一个可学习的打分,只保留最重要的节点。
原始图(n 节点)→ [GNN + 池化] → 压缩图(k 节点)→ [GNN + 池化] → … → 读出 → 分类分层池化的好处是让"粗粒度结构"(如分子里的官能团、图中的社区)被显式建模,在分子与蛋白质数据集上常优于单次读出。
3. 表达力上限:GIN 与 WL 测试
图神经网络能"认出"两张图是不是同构(结构相同、只是节点标号不同)吗?这个问题有一个漂亮的理论答案:GNN 的表达力上限恰好等于 Weisfeiler-Lehman(WL)图同构测试——一个经典的、通过迭代地"给节点打标签并压缩邻居标签的多重集"来判断图同构的算法。
WL 测试每轮:
① 每个节点收集邻居的当前标签
② 把邻居标签作为一个多重集,与自身标签一起哈希成新标签
③ 若两张图在某一轮出现不同的标签集合 → 图不同构
GIN 每层:
h_v^(k) = MLP^(k)( (1+ε) · h_v^(k-1) + Σ_{u∈N(v)} h_u^(k-1) )《How Powerful are Graph Neural Networks?》(Xu 等,ICLR 2019)证明:只要聚合函数是"单射"的(sum 满足),GNN 就能达到 WL 测试的表达力;而 mean/max 会丢失多重集信息(区分不了"两个相同值"与"一个值"),表达力更弱。这同时给出两个工程结论:
- sum 聚合表达力最强(GIN 因此指定 sum 聚合 + MLP 更新);
- 任何 GNN 都无法超过 1-WL——超过它需要更高阶的模型(如消息带高维结构、等变模型),复杂度随之剧增。
八、应用场景
GNN 的价值最终体现在它让深度学习第一次"有意识"地利用了关系结构。四大主战场如下。
1. 社交网络
社交平台是天然的图:用户为节点,关注/好友为边。典型任务包括:
- 好友推荐:本质是链接预测——两个用户共享大量共同邻居、且邻居彼此熟悉(强三角闭包),则未来更可能建立连接;
- 社区发现:聚类用户的表示,找到兴趣小组;
- 机器人识别:社交机器人常表现为"大量关注但无人回关、聚集在少数 hub 周围",这类结构异常是特征方法难以捕捉的。
2. 推荐系统
推荐系统的核心是"用户—物品"二分图:用户与物品是两类节点,购买/点击/评分是边。GNN 在这里的价值是在交互图上学习用户与物品的表示,把"协同过滤"做成了端到端的图上学习:
- PinSage(KDD 2018):Pinterest 的工业级方案,在 30 亿条边的图上做随机游走采样 + 聚合,用于"图钉与画板"推荐,被证明对冷启动与个性化都有明显提升;
- LightGCN:去掉 GCN 中的特征变换与非线性,只保留聚合——简洁且在主流推荐数据集上表现优异,是"少即是多"在 GNN 上的又一个案例。
推荐系统更完整的图景(含传统协同过滤、双塔模型、排序漏斗)见推荐系统,GNN 是其中"图上建模"的一支重要力量。
3. 分子性质预测
分子是"原子为节点、化学键为边"的图,且原子种类、键型天然是离散属性——这让 GNN 成为量子化学与药物发现的主力工具:
- 分子指纹:传统 ECFP 指纹手工枚举子结构;GNN 用消息传递自动学习子结构表示,且可微、可端到端优化;
- 性质回归:QM9 数据集(13 万个小分子)上预测 HOMO-LUMO 能隙、自由能等量子性质,MPNN(消息传递框架的提出论文)等模型大幅超越手工特征;
- 药物发现:预测毒性、血脑屏障通透性,以及药物—靶标相互作用(本质上又是链接预测)。GNN 还能与大语言模型结合,把分子 SMILES 字符串与图表示融合,是当前研究热点。
4. 知识图谱
知识图谱把世界表示为"实体—关系—实体"的三元组(如 <爱因斯坦, 出生于, 德国>),本质是异质图:节点有类型、边有类型。GNN 在这里要解决两个问题:
- 图谱嵌入:早期方法(TransE,Bordes 等 2013)把实体与关系嵌入向量空间,用向量运算建模关系(
h + r ≈ t),但它不能利用图中多跳结构; - 关系图卷积(R-GCN,ESWC 2018):让每种关系类型拥有一套独立的变换矩阵,消息按关系类型聚合,把 GCN 推广到异质图,用于图谱补全(链接预测)与实体分类;
- 图问答:把问题解析成子图,用 GNN 推理答案所在实体。
其他应用还包括:欺诈检测(转账图中抓异常团伙)、交通预测(路网上的时序 GNN)、物理模拟(把粒子当作节点预测运动轨迹)、程序分析(代码抽象语法树上的缺陷检测)。
九、局限与挑战
GNN 远非万能。理解它的三个硬伤,是工程师与研究者都必需的素养。
1. 过平滑(Oversmoothing)
层数加深后,节点表示会趋同:多次应用归一化邻接矩阵等价于反复做"邻居平均",节点特征随层数收敛到图的平稳分布,最终所有节点几乎一样,分类精度反而随层数增加而下降——与 CNN 越深越强截然相反。
测试精度
↑ /
│ / GNN 的典型曲线:层数过深反而下滑
│╱
├───────→ 层数
1 2 3 4 5 6缓解手段包括:残差连接(把输入与输出相加)、JK-Net(各层输出拼接后学权重)、DropEdge(训练时随机丢边)、APPNP(把邻居聚合换成可调系数的 Personalized PageRank 式传播)。工程上记住一条经验:GNN 很少需要超过 3~5 层,除非结构上专门处理过平滑。
2. 表达力上限
如第七节所述,绝大多数 GNN 的表达力不超过 1-WL 图同构测试。这意味着:
- 某些结构上不同的图,模型永远无法区分(如"六边形环"与"两个三边形环"在某些聚合下表示相同);
- 需要区分复杂子结构时,模型会失败。超过上限需要高阶 GNN(如 2-WL、基于子结构的模型),但计算代价通常是指数级的,工程上很少使用。
3. 可扩展性
全批量 GCN 需要把整个邻接矩阵和所有中间表示放进内存——百万节点就接近单卡极限,十亿级(真实社交网络)直接不可行。常见的工程化路线:
| 策略 | 做法 | 代价 |
|---|---|---|
| 邻居采样 | 每步只取固定规模的子图(GraphSAGE 思路) | 可能丢失全局结构信息 |
| 子图划分 | Cluster-GCN 等按社区切图分块训练 | 边界信息需处理 |
| 图蒸馏/简化 | 学一个小图代表大图 | 信息有损 |
此外,真实图还常是动态的(边随时间增减)与异质的(多类型节点边)——把静态同质 GNN 直接搬上去通常不奏效,需要专门设计。这些"数据现实"在数据集与工具档案里能找到更多一手资料。
4. 过度挤压与长程依赖
消息传递的本质是"逐跳扩散",信息每过一跳都会被压缩一次。当任务需要关联图上很远的两个节点时,远距离信息经过多跳后往往已被稀释——这被称为过度挤压(Over-squashing)。这与 RNN 的梯度消失问题同构,也是"GNN 很难做远距离推理"的根源之一。缓解方向包括图重连(减少瓶颈边)、可学习传播距离等。
图上的三个最容易踩的坑
- 数据泄漏:邻居归一化用了全部节点、或用整图做了任何形式的预计算再划分训练集,都会让测试指标虚高——图上的数据划分必须"先划好再算结构信息";
- 同质性假设:GNN 的朴素设计默认"邻居更像自己",在异质图(欺诈、蛋白质互作、部分电商图)上直接套 GCN 常常不如线性基线,需要先做同质性系数检验;
- 只比 SOTA 不比基线:别急着上最新模型——先跑 GCN、GraphSAGE 一层和特征 MLP 基线,确认"结构真的有帮助",再谈改进。这与任何机器学习项目的第一步完全相同。
十、工具简介
今天几乎没有团队会手写消息传递——两个成熟框架提供了工业级实现与大量内置数据集。
1. PyTorch Geometric(PyG)
PyG 是 PyTorch 生态的事实标准,核心抽象是 torch_geometric.data.Data(封装 x、edge_index、y)与 MessagePassing 基类:
python
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
from torch_geometric.datasets import Planetoid
dataset = Planetoid(root="/tmp/data", name="Cora") # 内置经典数据集
data = dataset[0] # data.x / edge_index / y
class Net(torch.nn.Module):
def __init__(self):
super().__init__()
self.conv1 = GCNConv(dataset.num_features, 16)
self.conv2 = GCNConv(16, dataset.num_classes)
def forward(self, x, edge_index):
x = F.relu(self.conv1(x, edge_index))
x = F.dropout(x, training=self.training)
return self.conv2(x, edge_index)
model = Net()
out = model(data.x, data.edge_index) # 半监督训练:只用有标签节点算损失PyG 内置了 GCNConv、SAGEConv、GATConv、GINConv、TopKPooling、DiffPool 等几乎所有主流层,且提供 Planetoid、QM9、OGB 等一整套标准数据集与评测协议(OGB 基准也是评价 GNN 的公共擂台)。
2. Deep Graph Library(DGL)
DGL 以"图作为一等公民的算子抽象"著称,把消息传递拆成 send(构造消息)与 recv(聚合消息)两段,支持 PyTorch/TensorFlow/MXNet 多个后端:
python
import dgl
import torch.nn.functional as F
def gcn_message(edges):
return {"m": edges.src["h"]} # 消息 = 源节点特征
def gcn_reduce(nodes):
return {"h": nodes.mailbox["m"].mean(dim=1)} # 聚合 = 取平均
class GCNLayer(torch.nn.Module):
def forward(self, g, h, W):
g.ndata["h"] = h
g.update_all(gcn_message, gcn_reduce) # 内置消息传递调度
return F.relu(W(g.ndata["h"]))DGL 的性能优化(尤其是对大规模稀疏聚合的调度)在工业界有很好的口碑,也内置了 dgl.nn.GraphConv、dgl.nn.GATConv 等高层模块。围绕两家的选择更多是生态偏好:深度绑定 PyTorch 选 PyG,追求跨框架与超大规模聚合性能可考虑 DGL。
3. 周边生态
图的预处理/可视化常用 NetworkX;分子专用库如 RDKit(分子图构造)与 DeepChem(药物化学 ML 工具包);模型与论文复现可参考 Papers with Code 的 Graph 榜单与 OGB 基准。这些资源在数据集与工具档案有进一步整理。
十一、权衡与取舍
实践中做 GNN,处处是选择。以下是四组最核心的权衡:
- 谱方法 vs 空间方法:GCN 等谱方法数学优美、简洁高效,但归一化权重依赖完整图结构、难以迁移到动态图;空间方法(GraphSAGE/GAT)只依赖"边表 + 聚合函数",灵活、可归纳,是当今主流。
- 均值 vs 求和聚合:mean 稳定但表达力弱、无法区分多重集;sum 表达力最强(GIN 的依据)但可能被大度节点/异常值放大。图同质、度数分布均匀用 mean 即可,需要精细区分结构时选 sum。
- 层数 vs 过平滑:1~2 层 GNN 只能编码局部信息,堆到 5 层以上又会因过平滑掉精度。先确定任务的"依赖半径"再定层数,永远好过盲目加深;必要时用残差、JK-Net 等结构延后过平滑。
- 全图 vs 采样训练:小图(万级节点)直接全批量最省事且信息完整;大图必须采样或划分子图,省内存的代价是结构信息损失与训练方差。
- 通用 GNN vs 领域定制:同质图直接用 GCN/GAT 很快;异质图(知识图谱)、动态图、带时序的图(交通预测)需要专门模型(R-GCN、Temporal GNN),"先通用后定制"是最稳妥的路径。
最后一条总原则和机器学习其他领域完全一致:先建简单基线(GCN、GraphSAGE 一层),再谈复杂模型。GNN 论文里的 SOTA 往往建立在精心调参之上,而你的业务数据未必吃这一套。理解了监督学习的评估与深度学习的训练机制,GNN 只是换了一种数据结构而已——剩下的坑(过拟合、数据泄漏、评测协议),都和教科书里写得一模一样。若需快速检索文中术语(消息传递、过平滑、WL 测试、拉普拉斯矩阵),随时查阅术语表。
延伸阅读
- 什么是机器学习——GNN 所属的学科框架与四要素定义
- 深度学习基础——GNN 的神经网络底座:权重共享、反向传播
- 监督学习——节点分类、链接预测背后的监督范式
- 无监督学习——GraphSAGE 自监督损失的理论家园
- 推荐系统——PinSage / LightGCN 所在的完整推荐图景
- Transformer 与 NLP——GAT 的注意力机制从何而来
- 大语言模型——GNN 与 LLM 融合的前沿方向
- 术语表——图论与 GNN 术语速查
- 数据集与工具档案——Cora、QM9、OGB 与 PyG/DGL 的入口
参考资料
- Kipf & Welling. Semi-Supervised Classification with Graph Convolutional Networks(ICLR 2017) —— GCN 原始论文,半监督节点分类的经典设定
- Hamilton, Ying & Leskovec. Inductive Representation Learning on Large Graphs(NeurIPS 2017) —— GraphSAGE:邻居采样与归纳学习
- Veličković et al. Graph Attention Networks(ICLR 2018) —— GAT:注意力机制在图上的一步
- Gilmer et al. Neural Message Passing for Quantum Chemistry(ICML 2017) —— MPNN:消息传递统一框架与分子性质预测
- Defferrard, Bresson & Vandergheynst. Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering(NeurIPS 2016) —— ChebNet:谱方法可落地的关键一步
- Xu et al. How Powerful are Graph Neural Networks?(ICLR 2019) —— GIN 与 WL 测试:GNN 表达力上限的理论分析
- Li, Han & Wu. Deeper Insights into Graph Convolutional Networks for Semi-Supervised Learning(AAAI 2018) —— 过平滑现象的早期系统研究
- Xu et al. Representation Learning on Graphs with Jumping Knowledge Networks(ICML 2018) —— JK-Net:多跳信息自适应组合
- Ying et al. Hierarchical Graph Representation Learning with Differentiable Pooling(NeurIPS 2018) —— DiffPool:可微分层池化
- Ying et al. Graph Convolutional Neural Networks for Web-Scale Recommender Systems(KDD 2018) —— PinSage:工业级图推荐系统
- Schlichtkrull et al. Modeling Relational Data with Graph Convolutional Networks(ESWC 2018) —— R-GCN:异质图(知识图谱)上的关系图卷积
- Bordes et al. Translating Embeddings for Modeling Multi-relational Data(NeurIPS 2013) —— TransE:知识图谱嵌入的奠基工作
- Wu et al. A Comprehensive Survey on Graph Neural Networks(IEEE TNNLS 2021) —— GNN 系统综述(中英文摘要与分类)
- PyTorch Geometric 官方文档 —— PyG 官网、教程与内置数据集
- Deep Graph Library 官方文档 —— DGL 官网与消息传递 API 参考