跳转至

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) 相同的注意力机制, 但只对邻居计算 (而非所有节点):

\[e_{ij} = \text{LeakyReLU}\left(\mathbf{a}^T [W h_i \| W h_j]\right)\]
  • \(W\) 共享线性变换
  • \(\|\) 表示拼接
  • \(\mathbf{a}\) 学习注意力向量

3.1.3 Softmax 归一化

\[\alpha_{ij} = \frac{\exp(e_{ij})}{\sum_{k \in \mathcal{N}(i)} \exp(e_{ik})}\]

3.1.4 加权聚合

\[h_i' = \sigma\left(\sum_{j \in \mathcal{N}(i)} \alpha_{ij} W h_j\right)\]

关键: 权重 \(\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\):

\[h_i' = \Big\|_{k=1}^K \sigma\left(\sum_{j \in \mathcal{N}(i)} \alpha_{ij}^k W^k h_j\right)\]
  • 中间层: 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} = \mathbf{a}^T \text{LeakyReLU}\left(W [h_i \| h_j]\right)\]

现在 \(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 自注意力:

\[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V\]

每个节点直接 attend 所有节点 — 1 层捕获全局。

3.3.3 问题: 边结构丢失

完全图忽略了原图结构。两种修复:

方案 A: Graphormer (Microsoft, NeurIPS 2021) — 把图结构当 bias:

\[A_{ij} = \frac{Q_i K_j^T}{\sqrt{d_k}} + b_{\text{spatial}}(i, j) + b_{\text{edge}}(i, j)\]
编码 内容
空间编码 \(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) 混合局部消息传递 + 全局注意力:

\[h_i' = \text{MLP}\left(h_i^{\text{MPNN}} + h_i^{\text{Attention}}\right)\]

每层两个并行分支: 一个 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:

\[A_{ij} = \frac{Q_i K_j^T}{\sqrt{d_k}} + W_e e_{ij}\]

分子图常用 — 化学键类型差异大。


第 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 条)

  1. 静态 vs 动态: 原版 GAT 静态 (排名与 query 无关), GATv2 动态修复。
  2. 多头合并: 中间层 concat, 最后层 average。
  3. 空间编码是 bias, 不是位置编码: 直接加到 \(QK^T\), 不加到输入 embedding。
  4. Graphormer 注意力是全局: \(O(N^2)\), 大图贵。GPS 局部+全局混合降低复杂度。
  5. Laplacian PE 计算: 特征分解 \(O(N^3)\) — 大图预计算一次, 推理复用。

六 · 跨章连接

章节 关联
Ch07 §04 Transformer 自注意力机制同源
Ch02 §04 矩阵论 拉普拉斯特征分解
Ch11 §05 VLA 机器人 affordance 图注意力
Ch05 §04 贝叶斯 图贝叶斯网络 (早期 GNN 思想)

七 · 自测 5 题

  1. GAT 与 GCN 在邻居权重上的差别?
  2. 多头注意力为何能学不同关系?
  3. GAT 静态问题的根源? GATv2 怎么修?
  4. Graphormer 如何把图结构融入 Transformer?
  5. 局部消息传递与全局注意力的取舍?

八 · 一句话总结

图注意力 = 把 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²/稀疏)

十 · 参考资料

  1. Velickovic et al., "Graph Attention Networks", ICLR 2018
  2. Brody et al., "How Attentive are Graph Attention Networks?", ICLR 2022 (GATv2)
  3. Ying et al., "Do Transformers Really Perform Bad for Graph Representation?", NeurIPS 2021 (Graphormer)
  4. Rampasek et al., "Recipe for a General, Powerful, Scalable Graph Transformer", NeurIPS 2022 (GPS)
  5. Wu et al., "Demystifying Oversmoothing in Attention-Based Graph Neural Networks", ICLR 2023
  6. Ch07 §04 Transformer (本套书)