图神经网络(GNN)是在做什么?

一句话:每个节点反复地"听取邻居的意见"来更新自己。堆叠几层以后,节点的表示就同时包含了自身特征和周围的结构信息。

数据长什么样

很多数据天然是图:社交网络(人 - 关注关系)、分子(原子 - 化学键)、论文引用网络、推荐系统中的用户 - 商品。

图 = 节点 + 边;每个节点通常带一个特征向量。

常见任务

  • 节点分类:判断每篇论文属于哪个领域
  • 链接预测:两个用户会不会成为好友
  • 图分类:一个分子有没有毒性

为什么不用普通神经网络

普通 MLP 只看节点自己的特征,看不见"谁和谁相连"。CNN 要求规则网格,而图中每个节点的邻居数量不同、也没有固定顺序。

所有模型共享同一个框架:消息传递

① 收集
每个邻居发来一条"消息"
→
② 聚合
把多条消息合成一条(求和 / 平均 / 最大 / 加权)
→
③ 更新
结合自身旧表示,得到新表示

各种 GNN 的差别,基本上就是"给邻居分配多大权重"以及"用什么函数聚合"这两点:

模型邻居权重怎么来聚合方式一句话特点
GCN由节点度数固定算出归一化加权求和(近似平均)最经典、最简单的基线
GraphSAGE平均分配(可采样部分邻居)mean / max / sum 可选;自身单独处理适合大图、能处理新节点
GAT用注意力机制学出来注意力加权求和,可多头不同邻居重要性不同
GIN全部为 1(自身为 1+ε)求和,再过 MLP区分图结构的能力最强

本页怎么用

  • 每个模型页面都有一个可点击的小图:点击任意节点,右侧会显示这个节点在这一层的真实计算过程(每个邻居的权重、贡献、最终结果)。
  • 控制栏里的开关和滑块对应 PyTorch Geometric(PyG)中真实存在的超参数,名字与官方 API 保持一致(例如 GCNConv 的 add_self_loops),方便你以后读代码时一一对应。
  • 每页底部的超参数卡片说明:它是什么、调大/调小会怎样、常用取值。
  • 为了让数字能画在二维平面上,演示中节点特征都是 2 维,线性变换 取单位矩阵(真实模型中 W 是训练学出来的,它只会对结果做一个线性变换,不影响"邻居怎么加权"这一核心逻辑)。
  • 第 7 节会在浏览器里真的训练一个 GCN,你可以直接感受学习率、dropout 等训练超参数的作用。

符号速查

符号含义符号含义
节点 i 的输入特征节点 i 的邻居集合
节点 i 经过一层后的新表示节点 i 的度(邻居个数)
邻接矩阵,相连为 1可学习的权重矩阵和偏置
单位矩阵(给每个节点加自环)向量拼接

图与消息传递

先看清楚"图"在计算机里是怎么表示的,再看一层 GNN 里信息是怎么流动的。

1.1 图的三种表示

下面是一张 8 个节点的小图,本页后面所有模型都用它演示。节点颜色表示它所属的社区(蓝 = 社区 A,橙 = 社区 B),节点下方的数字是它的 2 维特征 。

图结构鼠标悬停节点查看信息
邻接矩阵
节点特征矩阵 X形状 [num_nodes, num_features] = [8, 2]
edge_index(PyG 的存边方式)形状 [2, num_edges]

PyG 不存整个邻接矩阵(大图里几乎全是 0,太浪费),而是只存"有边的位置":第一行是源节点,第二行是目标节点。无向图的每条边要存两次(两个方向),所以 10 条边存成 20 列。

1.2 一层 GNN 里发生了什么

点击任意节点作为目标节点,再按"播放"。这里用最简单的"取平均"作为聚合方式;后面各模型的区别只在于第 ② 步怎么加权。

这一步在算什么
三个关键性质
  • 所有节点同时更新:一层里每个节点都在做同样的事,用的都是上一层的旧表示。勾选"所有节点同时"可以看到这一点。
  • 参数共享:所有节点共用同一组参数 W,所以模型大小与图的大小无关。
  • 与邻居顺序无关:邻居没有"第一个、第二个"之分,所以聚合函数必须对顺序不敏感(求和、平均、最大值都满足)。

GCN 图卷积网络

Kipf & Welling, 2017 · PyG 中的类名:torch_geometric.nn.GCNConv

核心思想:节点的新表示 = 自己和所有邻居特征的加权平均。权重不是学出来的,而是由两端节点的度数决定:邻居越多的节点,它发出的每条消息越"稀释"。

:加上自环的邻接矩阵 :加自环后的度 = 邻居数 + 1 右边是等价的矩阵写法
为什么要除以 ? 如果直接求和,度数大的节点会收到很多消息,数值越来越大,训练不稳定。除以度数相当于"取平均";用 两边各除一半(对称归一化),还能让"人气很高的邻居"( 大)对每个人的影响变小。
点击节点查看它的计算边的粗细 = 该邻居的权重
计算明细
特征空间:一层之后所有节点移动到了哪里
空心 = 输入 x实心 = 输出 h'
观察要点
  • 点击节点 7(只有 1 个邻居)和节点 3(3 个邻居)对比:度数越小,自环权重越大,节点越"保留自己"。
  • 散点图里,同一社区的点彼此靠拢:这就是 GCN 的"平滑"作用,相邻节点的表示变得更相似,对分类有帮助。
  • 节点 3 和 4 是两个社区之间的"桥",它们被拉向对方社区,所以在两个社区的边界处最容易分错。
  • 关闭 normalize:权重全变成 1,变成纯求和。散点图里点的位置明显"跑远",而且度数越大跑得越远。
  • 关闭 add_self_loops:节点完全不看自己,只看邻居,节点 7 的输出直接等于节点 6 的特征(按度数缩放后)。

GCNConv 超参数

GraphSAGE

Hamilton et al., 2017 · PyG 中的类名:torch_geometric.nn.SAGEConv(配合 NeighborLoader 做邻居采样)

核心思想:把"自己"和"邻居"分开处理再相加;邻居的聚合方式可选(mean / max / sum ...);训练时每个节点只随机采样一部分邻居,这样再大的图也能分批训练。因为学到的是"怎么聚合"的函数而不是每个节点的固定向量,所以训练后出现的新节点也能直接计算(归纳式学习,inductive)。

:采样得到的邻居子集 AGG:mean / max / sum 等 演示中
和 GCN 的关键区别:GCN 把自己当成"一个普通邻居"混在一起平均;GraphSAGE 让自己走一条单独的通道(W₁),邻居走另一条(W₂)。这样模型可以分别学习"自己有多重要"和"邻居有多重要"。
点击节点查看它的计算虚线 = 本轮没有被采样的邻居
计算明细
特征空间
空心 = 输入 x实心 = 输出 h'
观察要点
  • 切换 aggr:mean 结果最平稳;max 在每一维上分别取最大值(表格中高亮的是"赢家");sum 会随邻居数量变大。
  • 把采样数设为 1,然后反复点"重新采样":同一个节点每次的输出都不一样,这就是采样带来的随机性(方差)。采样越多越接近真实结果,但计算量越大。
  • 打开 normalize:所有输出都被拉到半径为 1 的圆上(虚线圆),只保留方向、丢掉长度。
  • 关闭 root_weight:节点完全不看自己的特征,只由邻居决定。

SAGEConv / NeighborLoader 超参数

GAT 图注意力网络

Veličković et al., 2018 · PyG 中的类名:torch_geometric.nn.GATConv

核心思想:GCN 中邻居的权重由度数固定死了,GAT 让模型根据两个节点的特征自己决定"这个邻居值得听多少",这个权重就是注意力系数 。同一个邻居对不同节点的重要性可以不同。

:可学习的注意力向量(原论文写成 ,与左式等价) softmax 保证每个节点收到的权重之和 = 1
每个头有自己独立的 ,能关注不同类型的邻居
点击节点查看注意力边越粗 = 注意力越大
计算明细
特征空间
空心 = 输入 x实心 = 输出 h'
观察要点
  • 演示中预设了 4 个头的注意力参数:头 1 偏好"维度 0 大"的邻居(蓝色社区),头 2 偏好"维度 1 大"的邻居(橙色社区)。选中桥节点 3 或 4,切换头 1 / 头 2,看注意力如何转向。
  • 头 3 的打分大多是负数:把 negative_slope 从 1 调到 0,负分被压扁,注意力变得越来越平均。
  • 打开"训练模式"并调大 dropout:部分注意力被随机置 0(虚线),剩下的按 1/(1-p) 放大。每次"重新采样"结果都不同,相当于训练时随机"屏蔽"部分邻居,防止过拟合。
  • 看输出维度:concat=True 时维度 = heads × out_channels,所以下一层的 in_channels 要相应设置。

GATConv 超参数

GIN 图同构网络

Xu et al., 2019 · PyG 中的类名:torch_geometric.nn.GINConv

核心思想:mean 和 max 聚合会丢失信息,例如"1 个蓝邻居"和"2 个蓝邻居"取平均后完全一样。GIN 用求和保留邻居的数量信息,再用一个MLP(多层感知机)做足够灵活的变换。理论上它区分不同图结构的能力与 Weisfeiler-Lehman(WL)图同构测试一样强,是消息传递类 GNN 能达到的上限。因此它特别适合图分类(如分子性质预测)。

:自身特征的额外权重(eps) MLP:由参数 nn 传入 演示中 MLP 视为恒等映射,只看括号里的聚合结果

5.1 为什么是"求和":一个区分能力实验

中心节点(灰色)有不同的邻居组合。蓝色邻居特征为 ,橙色邻居为 。如果两张图的聚合结果相同,GNN 就无法区分它们。

5.2 在示例图上计算一层 GIN

点击节点查看它的计算所有邻居权重都是 1
计算明细
特征空间(MLP 之前)
空心 = 输入 x实心 = 聚合结果
观察要点
  • 和 GCN 对比:GIN 的输出长度与度数相关(邻居越多,和越大),这正是它能"数出邻居个数"的原因。
  • 拖动 eps:ε 越大,节点越看重自己;ε = -1 时完全忽略自身。
  • 实际使用时,因为求和会让数值变大,MLP 里通常会加 BatchNorm 来稳定训练。
  • 做图分类时,最后要用一个读出函数(Readout,如 global_add_pool)把所有节点的表示汇总成整张图的表示。

GINConv 超参数

层数、感受野与过平滑

num_layers 是 GNN 最重要的结构超参数之一。它决定每个节点"能看多远",但并不是越深越好。

6.1 感受野:K 层 = 看到 K 跳以内的邻居

第 1 层之后,节点只包含直接邻居的信息;第 2 层时,邻居本身已经包含了邻居的邻居的信息……点击节点并拖动层数。

感受野

6.2 过平滑:层数太多,所有节点都变得一样

下面是一张 45 个节点、3 个社区的图,每个节点有 2 维带噪声的特征。我们反复执行 GCN 的传播操作 (W 取单位矩阵、不加激活,只看"平滑"本身),观察节点特征如何变化。

节点特征分布
社区 A社区 B社区 C
可区分程度 = 类间平均距离 / 类内平均距离

数值越大,不同社区越容易分开;等于 1 时,同社区和不同社区的节点一样远,已经无法区分。

结论:前 1~3 层,同社区的节点互相靠拢、噪声被抹平,可区分度上升,这正是 GNN 有效的原因;继续加深,所有节点都被"搅拌"到一起,可区分度下降,这就是过平滑(over-smoothing)。所以大多数 GNN 只用 2~3 层。 如果确实需要更深的网络,常用的缓解办法有:残差连接、JumpingKnowledge(拼接各层输出)、DropEdge、PairNorm 等。

训练超参数实验室

在浏览器里真实训练一个 GCN(前向传播、反向传播、Adam 优化器都是真实计算)。调节超参数,观察训练曲线和预测结果的变化。

任务设定(模拟 Cora 这类半监督节点分类):45 个节点、3 个社区,每个节点有 8 维噪声很大的特征。只有 9 个节点有标签(每类 3 个,粗黑框),模型要预测其余 36 个节点的类别。只看特征很难分对,利用"相连的节点往往同类"这一图结构才能做好。
预测结果填充色 = 预测类别;✕ = 预测错误
类别 A类别 B类别 C节点内字母 = 真实类别
损失(Loss)越低越好
准确率(Accuracy)越高越好

训练超参数说明

速查与选型

把前面的内容浓缩成两张表,方便随时查阅。

四种模型对比

模型PyG 类邻居权重优点局限适合场景
GCNGCNConv由度数决定,固定简单、快、参数少,是很强的基线所有邻居同等看待(仅按度数);原始形式需要全图中小规模的节点分类(Cora、PubMed)
GraphSAGESAGEConv平均(mean)或 max / sum支持邻居采样和小批量训练;能处理新节点采样带来随机性;mean/max 会丢失邻居数量信息大规模图、动态图、推荐系统
GATGATConv、GATv2Conv注意力,可学习能区分重要和不重要的邻居;注意力可解释计算与显存开销更大;原始 GAT 的注意力排序较"静态"(GATv2 已改进)邻居重要性差异大的图、异质性较强的数据
GINGINConv、GINEConv全部为 1(自身 1+ε)表达能力理论最强,区分结构能力好数值随度数增长,需要 BatchNorm;节点分类上不一定优于 GCN图分类、分子性质预测

超参数一览与起步值

超参数作用推荐起步值出问题时的信号
num_layers感受野(看几跳邻居)2(节点分类);3~5(图分类,配合残差/BatchNorm)太深:训练、测试准确率都下降(过平滑)
hidden_channels隐藏表示的维度 / 模型容量16~64(小图);128~256(大图)太小:训练准确率也上不去;太大:训练很好、测试差
dropout随机丢弃神经元,防止过拟合0.5(GCN);0.6(GAT)过大:欠拟合、曲线抖动;过小:过拟合
lr每步更新的步长0.01(GCN);0.005(GAT);0.001(大模型)过大:loss 震荡或爆炸;过小:loss 下降很慢
weight_decayL2 正则,限制权重大小5e-4过大:欠拟合;为 0:容易过拟合
epochs训练轮数200(小图),配合早停验证集 loss 开始上升 → 该停了
heads(GAT)注意力头数隐藏层 8;输出层 1头多显存线性增长
aggr(SAGE)邻居聚合方式mean需要数邻居数量时改 sum
num_neighbors(采样)每跳采样邻居数[25, 10] 或 [15, 10]太少:结果方差大;太多:显存不够
eps / train_eps(GIN)自身特征的额外权重0 / False一般影响不大

推荐的调参顺序

  1. 先跑通默认值:2 层、hidden 16~64、dropout 0.5、lr 0.01、weight_decay 5e-4、Adam。
  2. 调学习率:观察 loss 曲线,是否平稳下降。它通常是最影响结果的一个。
  3. 看是否过拟合:训练准确率远高于验证准确率时,加大 dropout / weight_decay,或减小 hidden_channels。
  4. 再调结构:层数(2~3)、模型种类(GCN → GAT / SAGE)、聚合方式、注意力头数。
  5. 始终用验证集做早停和选模型,测试集只在最后评估一次。

说明:本页所有超参数名称与 PyTorch Geometric 官方 API 一致;不同版本的默认值可能略有差异,以你所用版本的官方文档为准。