GAT 图注意力:为什么它比 GCN 更会挑邻居
同一批邻居的重要性从来不在一个量级上,GCN 却把它们一视同仁地平均掉;GAT 用一次邻居内的注意力打分,把「该多听谁的」交给数据自己决定。
一、背景与痛点
一个电商用户可能关注了 500 个主播,但真正反复点开、下单的也就那么三个;一张论文合作网络里,一位研究者的合作者有几十位,可连续多年共同署名的核心伙伴往往只有两三个;在分子图里,一个原子与多个原子成键,化学键的强弱也分三六九等。这些场景的共同点是:同一个节点的邻居,重要性根本不在一个量级上。
可传统 GCN 的传播是「各向同性」的——每个邻居拿到的权重只由度数决定,方向无关。当图里的干扰边占多数时(比如每个节点连着一半来自别社区、一半来自本社区的邻居),GCN 的均匀平均会把真正有用的信号稀释掉,模型学到的表示自然差。问题的核心于是很清楚:我们需要一个由数据驱动、可微、且能在邻居集合内自适应的传播权重。这正是 GAT(Graph Attention Network,图注意力网络,Veličković 等人 2018 年发表于 ICLR)要解决的事——它让图的传播算子第一次从「常数」变成了「函数」。
二、核心原理
1. 从固定权重到可学权重。 先把通用的聚合式摆出来:
hi′=σ(∑j∈N(i)∪{i}αijWhj).h_i'=\sigma\Big(\sum_{j\in\mathcal N(i)\cup\{i\}}\alpha_{ij}W h_j\Big).hi′=σ(j∈N(i)∪{i}∑αijWhj).
在 GCN 里,αij\alpha_{ij}αij 是一个提前算好、训练中始终不变的常数 1/didj1/\sqrt{d_id_j}1/didj,只跟两个节点的度数有关。GAT 的全部新意,就是把 αij\alpha_{ij}αij 换成由节点特征算出来、并且可微的量:
αij=softmaxj(e(hi,hj))=exp(e(hi,hj))∑k∈N(i)∪{i}exp(e(hi,hk)).\alpha_{ij}=\text{softmax}_j\big(e(h_i,h_j)\big)=\frac{\exp\big(e(h_i,h_j)\big)}{\sum_{k\in\mathcal N(i)\cup\{i\}}\exp\big(e(h_i,h_k)\big)}.αij=softmaxj(e(hi,hj))=∑k∈N(i)∪{i}exp(e(hi,hk))exp(e(hi,hj)).
注意这个 softmax 的求和范围是邻居集合而不是全部节点——这是 GAT 与整句 Transformer 最根本的区别。权重一旦可学,就带来三样东西:表达力(模型自己决定多看谁、少看谁)、归纳性(α\alphaα 是函数算出来的,新节点、新图都能算)、可解释性(αij\alpha_{ij}αij 本身就是一条可读的「邻居重要性分数」)。
2. 打分函数为什么长这样。 GAT 选的具体形式是
e(hi,hj)=LeakyReLU(a⊤[Whi ∥ Whj]),e(h_i,h_j)=\text{LeakyReLU}\Big(\mathbf a^\top\big[W h_i\,\big\|\,W h_j\big]\Big),e(hi,hj)=LeakyReLU(a⊤[WhiWhj]),
其中 W∈Rh×h′W\in\mathbb R^{h\times h'}W∈Rh×h′ 是共享线性变换,[⋅∥⋅][\cdot\|\cdot][⋅∥⋅] 是拼接,a∈R2h′\mathbf a\in\mathbb R^{2h'}a∈R2h′ 是打分向量。把它拆成 a=[a1∥a2]\mathbf a=[\mathbf a_1\|\mathbf a_2]a=[a1∥a2],就得到更直观的
eij=LeakyReLU(a1⊤(Whi)+a2⊤(Whj)).e_{ij}=\text{LeakyReLU}\big(\mathbf a_1^\top (Wh_i)+\mathbf a_2^\top (Wh_j)\big).eij=LeakyReLU(a1⊤(Whi)+a2⊤(Whj)).
三个细节各有讲究:其一,WWW 负责把两个节点投影到共享语义空间再比较,否则原始特征量纲不同,比较没有意义,它还顺便降维。其二,拆成 a1,a2\mathbf a_1,\mathbf a_2a1,a2 是为了打破对称——只用一个向量的话打分退化成 a⊤(Whi+Whj)\mathbf a^\top(Wh_i+Wh_j)a⊤(Whi+Whj),是对称的,但注意力本可以是非对称的(iii 对 jjj 的重视不必等于 jjj 对 iii)。其三,用 LeakyReLU 而非 ReLU:负分若被 ReLU 归零,「不亲和」的邻居会拿到完全相同的零梯度,模型就没法靠压低它们来区分,注意力也就失去了「把不重要的邻居压下去」的能力。留一个小负斜率(常用 0.2)能让负分持续下降。
3. 多头:拼接与平均。 单头注意力有个老毛病,α\alphaα 的估计方差大,训练早期容易不稳定。解法是并行用 KKK 个独立的头,每个头有自己的 Wk,akW^k,\mathbf a^kWk,ak。中间层把 K 个头拼接(输出维度乘 K,保留不同子空间的信息),最后一层取平均(维度不变,直接用于分类)。
4. 与 GCN 的本质差异——各向异性传播。 GCN 的传播算子固定为 A^\hat AA^;GAT 的传播算子 PPP 依赖数据,Pij∝exp(LeakyReLU(a⊤[Whi∥Whj]))P_{ij}\propto\exp(\text{LeakyReLU}(\mathbf a^\top[Wh_i\|Wh_j]))Pij∝exp(LeakyReLU(a⊤[Whi∥Whj]))。回到统一式 H(l+1)=σ(P(l)H(l)W(l))H^{(l+1)}=\sigma(P^{(l)}H^{(l)}W^{(l)})H(l+1)=σ(P(l)H(l)W(l)),GAT 干的事就是把 PPP 从常数变成函数——这也是「自注意力是图的推广」这一说法的来源:在完全图上做局部 softmax,注意力就退化成 Transformer。
5. 一个必须记住的归一化陷阱。 因为 αij\alpha_{ij}αij 是按节点归一化的(∑jαij=1\sum_j\alpha_{ij}=1∑jαij=1),某类邻居数量越多,它们每个分到的权重反而越小。假设某节点有 20 个「重要」邻居和 2 个「不重要」邻居,即便模型给重要邻居的总权重远大于不重要邻居(比如 0.6 对 0.4),平摊到每个重要邻居身上也只有 0.6/20=0.030.6/20=0.030.6/20=0.03,而不重要邻居是 0.4/2=0.20.4/2=0.20.4/2=0.2——直接比较每条边的原始注意力会得出完全相反的结论。正确做法是用提升度校正:
liftij=αij⋅di,\text{lift}_{ij}=\alpha_{ij}\cdot d_i,liftij=αij⋅di,
均匀注意力时 lift=1\text{lift}=1lift=1,大于 1 才说明这条边比平均更受关注。
三、代码实战
下面这段纯 numpy 的精简代码,手工实现了单头图注意力的完整前向:投影、打分、LeakyReLU、邻居内分段 softmax、加权聚合,全部不依赖任何深度学习框架,可直接运行。
import numpy as np
rng = np.random.RandomState(0)
# 构造一个小图:6 个节点,无向,含自环
N, d_in, d_out = 6, 4, 3
A = (rng.rand(N, N) < 0.5).astype(float)
A = np.maximum(A, A.T) # 无向化
np.fill_diagonal(A, 1.0) # 自环(GAT 的邻居集含自身)
X = rng.randn(N, d_in)
W = rng.randn(d_in, d_out) * 0.5 # 共享线性变换
a1 = rng.randn(d_out) * 0.5 # 打分向量拆成 a1 / a2
a2 = rng.randn(d_out) * 0.5
Z = X @ W # 先投影到共享语义空间
# 打分 e_ij = LeakyReLU(a1^T Z_i + a2^T Z_j)
p1, p2 = Z @ a1, Z @ a2
e = p1[:, None] + p2[None, :]
e = np.where(e > 0, e, 0.2 * e) # LeakyReLU(0.2)
e = np.where(A > 0, e, -1e9) # 只在邻居上做 softmax
# 分段 softmax:按节点(行)归一化
e = e - e.max(axis=1, keepdims=True)
alpha = np.exp(e)
alpha = alpha / alpha.sum(axis=1, keepdims=True)
H = alpha @ Z # 邻居内加权聚合
print("注意力矩阵(每行和为 1):")
print(np.round(alpha, 3))
print("每行和:", np.round(alpha.sum(axis=1), 4))
print("聚合输出形状:", H.shape)
运行结果说明:每一行的注意力只在对应节点的邻居列上非零,且每行严格归一化为 1.0000,这正是「在邻居集合上 softmax」的直接体现;输出形状为 (6,3)(6,3)(6,3),维度由投影矩阵决定。把这段放大成两层多头 GAT(隐藏层 4 头各 8 维、拼接,输出层单头出 3 类),在一张 300 节点、3 社区、每个节点连 6 个同社区邻居加 12 个跨社区邻居(干扰边是有用边的两倍)的合成图上训练,测试准确率能做到 0.708,而同样数据、同样训练量的 GCN 只有 0.317——几乎等于三分类的随机水平。用提升度体检后,同社区邻居平均提升度 1.178、跨社区 0.902,比值 1.31 倍,说明注意力确实主动压低了干扰边。
四、关键经验/避坑
- 打分函数一定要用双参数 a1,a2\mathbf a_1,\mathbf a_2a1,a2:只用一个向量会强迫注意力对称,丢掉方向性。
- 负斜率必须非零:用 LeakyReLU(0.2)而不是 ReLU,否则「不亲和」的邻居拿不到区分梯度,注意力失去挑邻居的能力。
- 中间层拼接、末层平均:隐藏层把 K 个头拼起来扩维、保留信息,输出层取平均保持维度,这是原论文的标准做法。
- softmax 只在邻居集合上做,且必须包含自环:这是它与全序列注意力的根本区别,也是它能把复杂度压在 O(∣E∣)O(|E|)O(∣E∣) 的关键。
- 警惕注意力熵过低:熵接近 0 意味着退化成「只看一个邻居」,可加熵正则或 dropout 缓解;健康的多头应呈现出不同的「性格」。
- 比较注意力别用原始值,要用提升度 liftij=αij⋅di\text{lift}_{ij}=\alpha_{ij}\cdot d_iliftij=αij⋅di 做归一化校正,否则会被邻居数量的差异带偏。
五、完整系列推荐
📚 本文选自《图神经网络与图深度学习》100 期系统教程(第 034 期:GAT 图注意力网络),每期配可运行 Python 代码。
完整系列(100 期正文 + 3 篇番外,每期文章+代码)已在 ima 知识号【Kruptos】持续更新:
- 🗂 70+ 技术知识库:图神经网络、强化学习、计算机视觉、推荐系统、大模型微调、操作系统……几乎覆盖全部软硬件技术栈
- 🧠 8 款 AI 技能:系列生产、知识库管理、CMMI 受管开发、自进化 Agent 等,已在 ima 技能广场上架,即装即用
- ✅ 全部免费订阅,后续更新自动推送
🔍 订阅方式:打开 ima(腾讯智能工作台)→ 搜索「Kruptos」→ 一键订阅;或在 ima 内直接搜索《图神经网络与图深度学习》。
作者:Kruptos(西电毕业,13 年无线通信/DSP/嵌入式科研,现深耕 AI 与云原生)
原创内容,转载注明出处。

9172

被折叠的 条评论
为什么被折叠?



