图神经网络(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 维特征 。
PyG 不存整个邻接矩阵(大图里几乎全是 0,太浪费),而是只存"有边的位置":第一行是源节点,第二行是目标节点。无向图的每条边要存两次(两个方向),所以 10 条边存成 20 列。
1.2 一层 GNN 里发生了什么
点击任意节点作为目标节点,再按"播放"。这里用最简单的"取平均"作为聚合方式;后面各模型的区别只在于第 ② 步怎么加权。
- 所有节点同时更新:一层里每个节点都在做同样的事,用的都是上一层的旧表示。勾选"所有节点同时"可以看到这一点。
- 参数共享:所有节点共用同一组参数 W,所以模型大小与图的大小无关。
- 与邻居顺序无关:邻居没有"第一个、第二个"之分,所以聚合函数必须对顺序不敏感(求和、平均、最大值都满足)。
GCN 图卷积网络
Kipf & Welling, 2017 · PyG 中的类名:torch_geometric.nn.GCNConv
核心思想:节点的新表示 = 自己和所有邻居特征的加权平均。权重不是学出来的,而是由两端节点的度数决定:邻居越多的节点,它发出的每条消息越"稀释"。
- 点击节点 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)。
- 切换
aggr:mean 结果最平稳;max 在每一维上分别取最大值(表格中高亮的是"赢家");sum 会随邻居数量变大。 - 把采样数设为 1,然后反复点"重新采样":同一个节点每次的输出都不一样,这就是采样带来的随机性(方差)。采样越多越接近真实结果,但计算量越大。
- 打开
normalize:所有输出都被拉到半径为 1 的圆上(虚线圆),只保留方向、丢掉长度。 - 关闭
root_weight:节点完全不看自己的特征,只由邻居决定。
SAGEConv / NeighborLoader 超参数
GAT 图注意力网络
Veličković et al., 2018 · PyG 中的类名:torch_geometric.nn.GATConv
核心思想:GCN 中邻居的权重由度数固定死了,GAT 让模型根据两个节点的特征自己决定"这个邻居值得听多少",这个权重就是注意力系数 。同一个邻居对不同节点的重要性可以不同。
- 演示中预设了 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 能达到的上限。因此它特别适合图分类(如分子性质预测)。
nn 传入
演示中 MLP 视为恒等映射,只看括号里的聚合结果
5.1 为什么是"求和":一个区分能力实验
中心节点(灰色)有不同的邻居组合。蓝色邻居特征为 ,橙色邻居为 。如果两张图的聚合结果相同,GNN 就无法区分它们。
5.2 在示例图上计算一层 GIN
- 和 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 取单位矩阵、不加激活,只看"平滑"本身),观察节点特征如何变化。
数值越大,不同社区越容易分开;等于 1 时,同社区和不同社区的节点一样远,已经无法区分。
JumpingKnowledge(拼接各层输出)、DropEdge、PairNorm 等。
训练超参数实验室
在浏览器里真实训练一个 GCN(前向传播、反向传播、Adam 优化器都是真实计算)。调节超参数,观察训练曲线和预测结果的变化。
训练超参数说明
速查与选型
把前面的内容浓缩成两张表,方便随时查阅。
四种模型对比
| 模型 | PyG 类 | 邻居权重 | 优点 | 局限 | 适合场景 |
|---|---|---|---|---|---|
| GCN | GCNConv | 由度数决定,固定 | 简单、快、参数少,是很强的基线 | 所有邻居同等看待(仅按度数);原始形式需要全图 | 中小规模的节点分类(Cora、PubMed) |
| GraphSAGE | SAGEConv | 平均(mean)或 max / sum | 支持邻居采样和小批量训练;能处理新节点 | 采样带来随机性;mean/max 会丢失邻居数量信息 | 大规模图、动态图、推荐系统 |
| GAT | GATConv、GATv2Conv | 注意力,可学习 | 能区分重要和不重要的邻居;注意力可解释 | 计算与显存开销更大;原始 GAT 的注意力排序较"静态"(GATv2 已改进) | 邻居重要性差异大的图、异质性较强的数据 |
| GIN | GINConv、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_decay | L2 正则,限制权重大小 | 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 | 一般影响不大 |
推荐的调参顺序
- 先跑通默认值:2 层、hidden 16~64、dropout 0.5、lr 0.01、weight_decay 5e-4、Adam。
- 调学习率:观察 loss 曲线,是否平稳下降。它通常是最影响结果的一个。
- 看是否过拟合:训练准确率远高于验证准确率时,加大 dropout / weight_decay,或减小 hidden_channels。
- 再调结构:层数(2~3)、模型种类(GCN → GAT / SAGE)、聚合方式、注意力头数。
- 始终用验证集做早停和选模型,测试集只在最后评估一次。
说明:本页所有超参数名称与 PyTorch Geometric 官方 API 一致;不同版本的默认值可能略有差异,以你所用版本的官方文档为准。