Ch12 §04 图注意力网络 · 讲解稿¶
章节定位: 在 GNN 三件套基础上, 把"邻居等权"换成"邻居自适应权重"。覆盖 GAT → GATv2 → Graphormer 三代图注意力。 承接: §03 GNN 邻居聚合 (等权) → §04 GAT (学习权重) + Graphormer (全局注意力 + 结构编码) 走向: §05 在 3D 图上做等变网络 (SchNet / Equiformer) 篇幅: ~4400 字 / 阅读 13 分钟 / 讲解 25-30 分钟
一 · 一句话总览¶
图注意力 = 用注意力机制给不同邻居不同权重。GAT (边结构内注意力)、GATv2 (动态)、Graphormer (全局注意力 + 空间/中心性编码) 是三代图注意力演进。
二 · 心智模型¶
GCN 邻居权重: [1/3, 1/3, 1/3] ← 等权
GAT 邻居权重: [0.5, 0.3, 0.2] ← 数据自适应
Graphormer: 邻居 + 距离 + 度 ← 全局注意力 + 结构编码
三 · 章节主线 (5 段)¶
第 1 段 · GAT: 图注意力机制 (10 分钟)¶
3.1.1 问题: GCN 等权的局限¶
GCN 邻居聚合: \(h_i' = \sum_{j \in \mathcal{N}(i)} \frac{1}{d_i} W h_j\)。所有邻居 ≈ 等权。
但现实: 重要朋友的意见 > 普通同事。
3.1.2 注意力系数¶
GAT (Velickovic 2018) 用与 Transformer (§07) 相同的注意力机制, 但只对邻居计算 (而非所有节点):
- \(W\) 共享线性变换
- \(\|\) 表示拼接
- \(\mathbf{a}\) 学习注意力向量
3.1.3 Softmax 归一化¶
3.1.4 加权聚合¶
关键: 权重 \(\alpha_{ij}\) 从数据学习, 不是图结构固定。
3.1.5 计算复杂度¶
仅在边 \((i, j) \in E\) 上算注意力 — 不是所有节点对。复杂度 \(O(E d)\), 不是 \(O(N^2 d)\)。这让 GAT 比 Transformer 全连接注意力更高效。
第 2 段 · 多头注意力与 GATv2 (8 分钟)¶
3.2.1 多头 GAT¶
K 个独立注意力并行, 每个有自己 \(W^k, \mathbf{a}^k\):
- 中间层: concat \(K\) 头 → \(K \cdot d'\) 维
- 最后层: average → \(d'\) 维
每头关注不同方面: 一头看结构相似, 一头看语义相似 — 与 Transformer 多头同源。
3.2.2 GAT 的静态问题¶
定理 (Brody 2022): 原版 GAT 注意力是静态的 — 节点 \(i\) 和节点 \(k\) 给同一邻居集合的排名相同!
推导: $\(e_{ij} = \mathbf{a}^T [Wh_i \| Wh_j] = \mathbf{a}_1^T W h_i + \mathbf{a}_2^T W h_j\)$
对于 \(i\) 的所有邻居, 第一项 \(\mathbf{a}_1^T W h_i\) 是常数。排名完全由 \(\mathbf{a}_2^T W h_j\) 决定 — 与 query \(i\) 无关。
3.2.3 GATv2 修复¶
把 LeakyReLU 移到拼接之后:
现在 \(e_{ij}\) 是 \((h_i, h_j)\) 的联合非线性函数, 不可分解为独立项。注意力变成动态 — 排名真正取决于 query。
GATv2 = GAT 严格更强, 计算量不变。
3.2.4 PyG GATv2¶
from torch_geometric.nn import GATv2Conv
class GATv2(torch.nn.Module):
def __init__(self, in_dim, hid_dim, out_dim, heads=8):
super().__init__()
self.conv1 = GATv2Conv(in_dim, hid_dim, heads=heads)
self.conv2 = GATv2Conv(hid_dim * heads, out_dim, heads=1)
def forward(self, x, edge_index):
x = torch.dropout(x, p=0.6, train=self.training)
x = torch.elu(self.conv1(x, edge_index))
x = torch.dropout(x, p=0.6, train=self.training)
x = self.conv2(x, edge_index)
return x
第 3 段 · 图 Transformer: 突破局部瓶颈 (10 分钟)¶
3.3.1 局部信息瓶颈¶
GNN 1 层只看 1 跳邻居。K 层后, K 跳外信息"被多步聚合稀释"。长程依赖难捕获。
3.3.2 全局自注意力¶
把图当"完全图", 用 Transformer 自注意力:
每个节点直接 attend 所有节点 — 1 层捕获全局。
3.3.3 问题: 边结构丢失¶
完全图忽略了原图结构。两种修复:
方案 A: Graphormer (Microsoft, NeurIPS 2021) — 把图结构当 bias:
| 编码 | 内容 |
|---|---|
| 空间编码 \(b_{\text{spatial}}\) | 节点 \(i, j\) 最短路距离 (Floyd 预计算) |
| 边编码 \(b_{\text{edge}}\) | 最短路上多边特征融合 |
| 中心性编码 | 节点度数加到输入 embedding |
3.3.4 Graphormer 优势¶
- OGB-mol: Graphormer 是 SOTA
- 全连接注意力 + 结构 bias
- 12 层 + 32 头 = 大模型
3.3.5 GPS: 混合架构¶
Graphormer 是"全局"; 但完全图注意力 \(O(N^2)\) 难扩展。GPS (Rampasek 2022) 混合局部消息传递 + 全局注意力:
每层两个并行分支: 一个 GNN (局部) + 一个 Transformer (全局), 合并。
第 4 段 · 位置编码与结构编码 (8 分钟)¶
图数据没有"自然顺序", Transformer 需要位置编码来打破对称性。
3.4.1 为什么需要¶
全连接 Transformer 注意力是置换不变的 — 节点重排输出不变。位置编码让节点"有身份"。
3.4.2 三类图位置编码¶
| 类型 | 思路 | 代表 |
|---|---|---|
| Laplacian PE | 拉普拉斯特征向量 | Graphormer |
| Random Walk | 随机游走分布 | 早期 GNN |
| 最短路距离 | Floyd 预计算偏置 | Graphormer |
| 节点 id 嵌入 | 直接学习 | 小图 |
3.4.3 Laplacian PE¶
拉普拉斯 \(L\) 最小非零特征向量 \(u_1, u_2, \dots, u_k\) — 反映图的"位置"信息 (类似图的坐标)。加入节点输入 embedding。
3.4.4 中心性编码¶
Graphormer 加节点度数 \(d_v\) 的 embedding 到 \(h_v\) — 让模型感知"我是 hub 还是边缘节点"。
3.4.5 边编码 (Edge Bias)¶
如果有边特征 \(e_{ij}\) (化学键类型、距离), 在注意力分数上加 bias:
分子图常用 — 化学键类型差异大。
第 5 段 · 实践: 异构 GAT 与可扩展 (10 分钟)¶
3.5.1 Cora 上 GAT¶
import torch
from torch_geometric.datasets import Planetoid
from torch_geometric.nn import GATConv
dataset = Planetoid(root='/data/Cora', name='Cora')
data = dataset[0]
class GAT(torch.nn.Module):
def __init__(self):
super().__init__()
self.conv1 = GATConv(dataset.num_features, 8, heads=8, dropout=0.6)
self.conv2 = GATConv(8 * 8, dataset.num_classes, heads=1, concat=False, dropout=0.6)
def forward(self, data):
x, edge_index = data.x, data.edge_index
x = torch.dropout(x, p=0.6, train=self.training)
x = torch.elu(self.conv1(x, edge_index))
x = torch.dropout(x, p=0.6, train=self.training)
x = self.conv2(x, edge_index)
return torch.log_softmax(x, dim=1)
# Cora GAT ~83% 精度
3.5.2 Graphormer 简化实现¶
import torch
import torch.nn as nn
import math
class GraphormerLayer(nn.Module):
def __init__(self, d, nhead):
super().__init__()
self.attn = nn.MultiheadAttention(d, nhead, batch_first=True)
self.spatial_bias = nn.Embedding(20, nhead) # 假设距离 ≤ 20
self.norm1 = nn.LayerNorm(d)
self.norm2 = nn.LayerNorm(d)
self.ffn = nn.Sequential(nn.Linear(d, 4*d), nn.GELU(), nn.Linear(4*d, d))
def forward(self, x, spatial_dist):
# x: [B, N, d]; spatial_dist: [B, N, N]
attn_bias = self.spatial_bias(spatial_dist.clamp(max=19)) # [B, N, N, H]
attn_bias = attn_bias.permute(0, 3, 1, 2) # [B, H, N, N]
# 自注意力 + spatial bias
attn_out, _ = self.attn(x, x, x)
x = self.norm1(x + attn_out)
# FFN
x = self.norm2(x + self.ffn(x))
return x
3.5.3 异构图注意力¶
异构图 (用户-商品-店铺) 用 RGCN 或 HAN:
from torch_geometric.nn import HANConv
# HAN: 分层注意力, 不同元路径不同注意力
conv = HANConv(
in_channels={'user': 64, 'item': 64, 'shop': 64},
out_channels=32,
metadata=data.metadata() # (node_types, edge_types)
)
3.5.4 大规模 Graph Transformer¶
完全图注意力 \(O(N^2)\) — 难处理百万节点。两种扩展:
| 方法 | 思路 |
|---|---|
| 稀疏注意力 | 只对邻居 + 重要节点 attention (NodeFormer, GBP) |
| 全局-局部混合 | GPS: 每层局部 GNN + 全局注意力 |
| 子图采样 | GraphSAINT 采样子图, 子图内 Transformer |
四 · 三大图注意力对比¶
| 模型 | 注意力范围 | 表达力 | 复杂度 | 适用 |
|---|---|---|---|---|
| GAT | 邻居 | 中 | \(O(E d)\) | 小图节点分类 |
| GATv2 | 邻居 | 较强 (动态) | \(O(E d)\) | 同上 |
| Graphormer | 全图 + 结构 | 强 | \(O(N^2 d)\) | OGB-mol 等大图 |
| GPS | 局部 + 全局 | 最强 | 混合 | 超大图 |
五 · 易错点 (5 条)¶
- 静态 vs 动态: 原版 GAT 静态 (排名与 query 无关), GATv2 动态修复。
- 多头合并: 中间层 concat, 最后层 average。
- 空间编码是 bias, 不是位置编码: 直接加到 \(QK^T\), 不加到输入 embedding。
- Graphormer 注意力是全局: \(O(N^2)\), 大图贵。GPS 局部+全局混合降低复杂度。
- Laplacian PE 计算: 特征分解 \(O(N^3)\) — 大图预计算一次, 推理复用。
六 · 跨章连接¶
| 章节 | 关联 |
|---|---|
| Ch07 §04 Transformer | 自注意力机制同源 |
| Ch02 §04 矩阵论 | 拉普拉斯特征分解 |
| Ch11 §05 VLA | 机器人 affordance 图注意力 |
| Ch05 §04 贝叶斯 | 图贝叶斯网络 (早期 GNN 思想) |
七 · 自测 5 题¶
- GAT 与 GCN 在邻居权重上的差别?
- 多头注意力为何能学不同关系?
- GAT 静态问题的根源? GATv2 怎么修?
- Graphormer 如何把图结构融入 Transformer?
- 局部消息传递与全局注意力的取舍?
八 · 一句话总结¶
图注意力 = 把 Transformer 注意力机制应用到图上。GAT (局部注意力) → GATv2 (动态注意力) → Graphormer (全局 + 结构编码)。Graph Transformer 在 OGB 等大图任务上击败传统 GNN, 但代价是 \(O(N^2)\) 复杂度。Ch12 §05 将进入 3D 图 — 那里需要的是等变而非"距离 bias"。
九 · 板书建议¶
[板书 1: GAT]
e_ij = LeakyReLU(a^T [Wh_i || Wh_j])
α_ij = softmax_j(e_ij)
h_i' = σ(Σ α_ij Wh_j)
[板书 2: GATv2 修复]
e_ij = a^T LeakyReLU(W [h_i || h_j])
← LeakyReLU 移到拼接内, 动态注意力
[板书 3: Graphormer]
A_ij = QK^T/√d + b_spatial(i,j) + b_centrality(i)
空间 = 最短路距离 bias
中心性 = 度数 embedding
[板书 4: 复杂度]
GAT/SAGE: O(E d) ← 边数
Graphormer: O(N² d) ← 完全图
GPS: 局部 O(E) + 全局 O(N²/稀疏)
十 · 参考资料¶
- Velickovic et al., "Graph Attention Networks", ICLR 2018
- Brody et al., "How Attentive are Graph Attention Networks?", ICLR 2022 (GATv2)
- Ying et al., "Do Transformers Really Perform Bad for Graph Representation?", NeurIPS 2021 (Graphormer)
- Rampasek et al., "Recipe for a General, Powerful, Scalable Graph Transformer", NeurIPS 2022 (GPS)
- Wu et al., "Demystifying Oversmoothing in Attention-Based Graph Neural Networks", ICLR 2023
- Ch07 §04 Transformer (本套书)