图注意力网络(GAT):参考答案#
说明#
以下答案与正文10道题逐题对应。前四题给出主要推导与计算;第 5--7 题给出可以运行的程序和检查方法;第 8--10 题说明比较时需要保持哪些条件相同,以及实验结果能够说明什么、不能说明什么。
分母为 \(\exp(0)+\exp(\log3)=1+3=4\),所以权重为 \(1/4\) 和 \(3/4\)。聚合结果为
\[\frac14(2,0)+\frac34(0,4)=\left(\frac12,3\right).\]若全图 Softmax 还包含非邻居得分 \(e_{\mathrm{non}}\),两个邻居的分母会多出 \(\exp(e_{\mathrm{non}})\);事后清零不会重新放大剩余权重,其和小于 1。掩码必须在 Softmax 前进入归一化集合。
对固定接收节点 \(i\),有
\[\alpha_{ij}= \frac{\exp(e_{ij})}{\sum_{k\in\mathcal N(i)\cup\{i\}}\exp(e_{ik})}.\]指数为正,所以权重非负,沿有效邻居求和等于 1。邻居置换只重排得分向量;Softmax 对应地重排权重,而 \(\sum_j\alpha_{ij}\bz_j\) 的配对项不变。同步置换全图节点时,每个旧邻域被映射到相应新邻域,共享投影和打分参数不变,因此节点输出按同一置换变化。若参数依赖节点编号或聚合包含存储顺序,结论不成立。
掩码后有效最大值为 1000,平移得分得到 \((0,-1,-\infty)\),因此
\[\boldsymbol{\alpha} =\left(\frac{1}{1+e^{-1}}, \frac{e^{-1}}{1+e^{-1}},0\right) \approx(0.7311,0.2689,0).\]若所有有效得分加 \(c\),分子、分母同时乘 \(e^c\),比值不变。直接计算 \(e^{1000}\) 会溢出;先减最大值后最大的指数为 1,其余不超过 1。必须先掩码再寻找有效最大值,并保证每个分段至少有一条边。
每头 \(\bW_r\in\mathbb R^{16\times8}\)、\(\ba_r\in\mathbb R^{16\times1}\),每头投影结果为 \(100\times8\),4 头拼接输出为 \(100\times32\)。忽略偏置和额外输出投影,参数量为
\[4(16)(8)+4(16)=512+64=576.\]加入 100 个自环后,每头处理 \(500+100=600\) 条边,4 头共计算 \(2400\) 个边得分。若输入的 500 条边已经含自环,程序应先确认并避免重复添加。
分段 Softmax 的 NumPy 参考实现可使用
maximum.at和add.at:import numpy as np def segment_softmax(scores, receive, num_nodes): scores = np.asarray(scores, dtype=float) receive = np.asarray(receive, dtype=np.int64) if scores.ndim != 1 or receive.shape != scores.shape: raise ValueError("scores 与 receive 应为同形一维数组") if np.any((receive < 0) | (receive >= num_nodes)): raise IndexError("接收端点越界") count = np.zeros(num_nodes, dtype=np.int64) np.add.at(count, receive, 1) if np.any(count == 0): raise ValueError("每个节点至少需要一条入边或自环") maximum = np.full(num_nodes, -np.inf) np.maximum.at(maximum, receive, scores) numerator = np.exp(scores - maximum[receive]) denominator = np.zeros(num_nodes, dtype=float) np.add.at(denominator, receive, numerator) return numerator / denominator[receive]
重复边是不同的 Softmax 项,除非预处理明确合并。每个接收节点的返回权重应分别求和为 1。
单头实现先计算
z = x @ W,再对每条边拼接z[receive]与z[send]计算 Leaky ReLU 得分,调用 分段 Softmax,最后按receive对alpha[:, None] * z[send]做index_add。多头可在头轴批量计算;隐藏层拼接头轴,输出层可平均头轴。逐节点循环版必须采用相同自环、边方向和 Leaky ReLU 斜率。使用
float64小图逐项比较投影、得分、权重和输出,再比较输入、\(\bW\) 与 \(\ba\) 的梯度。若对接框架库,还要对齐注意力 Dropout、偏置和多头输出约定。相同最终输出不能排除边权恰好抵消,因此应比较中间量。置换测试同步改写边端点和节点行;邻居顺序测试只打乱同一接收节点的边次序;平移测试给一个分段得分加常数;分段和、非边和相同值测试按正文性质断言。全空邻域必须报错,极大得分仍应有限;无向边若只存一个方向,应有意得到不同邻域,用于暴露存储错误。还可构造两个接收节点,若错误地对所有边做一个全局 Softmax,分段和测试必然失败。
两个模型使用相同的数据划分、特征、随机种子、近似参数量和最大更新次数,并使用同一个验证集和相同标准选择模型;模型和训练方法确定后,测试集只使用一次。报告准确率或 macro-F1、训练时间、固定图批量预测时间、峰值内存、参数量、实际边得分数和图层运行次数。
GAT 根据数据学习邻居权重,可能提高预测质量,但也会增加每条边的打分计算和多头存储;GCN 可以作为使用固定邻居权重的对照模型。单个注意力权重较大并不能直接说明模型作出预测的原因,模型比较也不能只使用一个随机种子。
第一组比较保持总输出宽度不变,例如令每个注意力头的宽度为 \(d/K\);第二组比较保持每个头的宽度不变,此时总宽度、参数量和计算成本会随 \(K\) 增长。各组使用相同的数据划分、随机种子、层数、更新次数与评价程序;在同一个验证集上尝试相同的一组学习率,并按照相同标准选择。报告测试指标、训练时间、固定批量预测时间、峰值内存、参数量、边得分数和图层运行次数。
在总宽度固定时,头数改变子空间分解而非简单增加容量;在每头宽度固定时,性能变化混合了头数和容量。两组结果不能合并成“头越多越好”的结论。
均匀权重版本令每个接收节点的有效边权为其入度倒数,并保留与 GAT 相同的投影、层数和宽度。三种模型共享划分、种子集合、更新次数、优化器与评价程序;报告测试指标、训练和固定批量预测时间、峰值内存、边得分数及层调用次数。稠密实现只在小图上核验数值,正式规模使用稀疏实现。
若学习权重优于均匀权重,说明该设置下数据依赖混合有帮助;若差异很小,不能据此断言注意力“没有作用”,还需考虑优化、头宽和数据同质性。稠密与稀疏实现结果应一致,但效率结论必须按实际实现分别报告。