图注意力网络(GAT)核心原理

说到图注意力网络,我第一反应就是它那个“注意力机制”。你想想看,传统的图卷积网络(GCN)在聚合邻居信息时,对所有邻居是一视同仁的。但在实际项目中,哪有这么理想的情况?

我印象很深,有一次做社交网络分析,一个用户的好友列表里,有家人、有同事、有广告号。如果都给相同的权重,那模型很容易学偏。GAT解决的就是这个问题——让模型自己学会哪些邻居更重要。

核心思想:GAT通过可学习的注意力系数,为不同的邻居节点分配不同的重要性权重。说白了,就是给关系好的邻居更大的发言权。

GAT的四个核心步骤

整个流程可以分为四个部分,我们一个一个过。

1. 节点特征映射

输入进来的是原始节点特征,但直接计算注意力不合适。我们需要先做个线性变换,把特征映射到更高维或更合适的空间。

具体来说,每个节点 i 有一个特征向量 hi。我们先乘以一个共享的权重矩阵 W:

h'_i = W * h_i

这个 W 是所有节点共用的。我个人习惯把这一步看作是“特征对齐”——把不同节点的特征统一投影到一个语义空间里,才能做后续的比较。

2. 注意力系数计算

特征映射完之后,我们要计算节点 i 和它的邻居 j 之间的注意力系数。怎么算?

GAT用的是拼接(concatenate),然后喂给一个单层前馈网络:

e_ij = LeakyReLU( a^T * [h'_i || h'_j] )

这里 || 表示拼接,a 是一个可学习的注意力向量。LeakyReLU 是激活函数。算出来的 eij 就是节点 j 对 i 的“原始”注意力分数。

经验之谈:我在项目中遇到过图结构非常稀疏的情况。如果你的图里邻居很少,eij 的值可能差异不明显。这时候可以试试把注意力向量的维度调大一点,让模型有更多参数去区分。

3. Softmax归一化

原始分数 eij 的范围不统一,没法直接当权重用。我们需要用 softmax 做归一化:

α_ij = softmax_j(e_ij) = exp(e_ij) / Σ_k∈N(i) exp(e_ik)

归一化后的 αij 满足和为 1。这其实就是注意力权重——越大表示邻居 j 对节点 i 越重要。

避坑指南:我曾经踩过一个坑——做归一化时忘记考虑自连接(self-loop)。如果不把节点自己算进去,模型在聚合时就丢了自己的信息,在一些任务上效果会明显变差。建议在构造邻接矩阵时,确保每个节点有一条指向自己的边。

4. 多头注意力(Multi-Head Attention)

单头注意力容易学到片面的关系。GAT 引入了多头注意力机制——并行跑 K 个独立的注意力计算,然后把结果拼接或取平均。

每个头的计算过程是一样的:

h''_i = ||_k=1^K  σ( Σ_j∈N(i)  α_ij^k * h'_j )

如果是用拼接,最终的特征维度会变成 K 倍。如果是用平均(比如在最后一层),通常这样:

h''_i = σ( (1/K) * Σ_k  Σ_j∈N(i)  α_ij^k * h'_j )

我个人在实践中发现,中间层用拼接、最后一层用平均 是个很稳的配置。你可以试试。

为什么用多头? 每个头关注的模式可能不同:有的头关注结构相似的邻居,有的头关注特征相似的邻居。多个头综合起来,模型更稳定,泛化能力也更强。

用一个简单的例子串起来

假设我们有 3 个节点,每个节点有 64 维特征。我们设注意力头数为 4。

输入: x_i (64维)
→ 线性映射: W * x_i (128维)
→ 4个头分别计算注意力系数
→ 每个头做softmax归一化
→ 加权聚合邻居特征
→ 4个头的输出拼接: 128 * 4 = 512维
→ (如果是最后一层) 取平均 + 激活函数

每一步都有它存在的意义。线性映射保证特征可比,注意力系数让模型学会权衡,多头并行让学习更充分。

GAT 的核心逻辑图

GAT 核心流程 节点特征 h_i 1. 线性变换 h'_i = W * h_i 2. 计算注意力系数 e_ij 3. Softmax 归一化 α_ij 4. 多头注意力 (K 个头并行) 拼接或平均后输出新特征 每个注意力头独立学习不同的关系模式,综合结果更鲁棒

总结一下

GAT 的精髓就这四个动作:映射 → 算分 → 归一化 → 多头并行。每一步都不难,但组合起来就能让图神经网络学会“看人下菜碟”。

嗯,记住这一点就够了:GAT 不是平均主义,它给每个邻居动态分配权重。后面我们解读注意力权重图时,其实就是去看这些权重长什么样、有没有道理。