Skip to content

图神经网络

本页速览 图神经网络(GNN)用消息传递让模型学会"看图说话"——在社交网络、分子、知识图谱上做节点分类、链接预测与图分类。本文拆解消息传递机制、GCN/GraphSAGE/GAT 三大代表模型,并讨论过平滑、表达力上限等深层问题。

图神经网络 ​

一句话定义:图神经网络(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 拼接起来喂给全连接层——但这样有两个致命问题:

  1. 排列敏感性:重新编号节点会重排矩阵的行列,同样的图会得到完全不同的向量;
  2. 参数不可共享:不同图的节点数不同,无法用固定大小的权重。

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 的感受野类比:

CNNGNN
基本运算卷积核扫过像素邻域聚合函数扫过图邻域
感受野核大小 × 层数跳数(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                # 一层 GNN

adj @ 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:一张表看清 ​

维度GCNGAT
聚合权重来源图结构(度)内容(注意力)
权重是否可学习否,预先算好是,随训练优化
是否依赖邻接矩阵是(需要 Â)否,只需边表
对不同邻居的区分只能按度区分可按内容精细区分
计算开销更低多头略高
适用场景基线、同质图邻居重要性差异明显的图

一个经验规律: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 很难做远距离推理"的根源之一。缓解方向包括图重连(减少瓶颈边)、可学习传播距离等。

图上的三个最容易踩的坑

  1. 数据泄漏:邻居归一化用了全部节点、或用整图做了任何形式的预计算再划分训练集,都会让测试指标虚高——图上的数据划分必须"先划好再算结构信息";
  2. 同质性假设:GNN 的朴素设计默认"邻居更像自己",在异质图(欺诈、蛋白质互作、部分电商图)上直接套 GCN 常常不如线性基线,需要先做同质性系数检验;
  3. 只比 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 测试、拉普拉斯矩阵),随时查阅术语表。

延伸阅读 ​

参考资料 ​