跳过正文
  1. blog/

GNN中的scatter离散运算

·1164 字·
Blog ST
Aoidayo
作者
Aoidayo
懒人
目录

PyG中的图神经网络中,scatter和gather运算(离散和聚集)都是非常常见的操作。

我们以GAT为例,说明scatter在GAT中的应用:一是相似度系数的归一,二是 \(h_i^{’}\) 的加权邻域聚合。

相似度系数归一

$$ \alpha_{i,j} = \frac{\exp(LeakyReLU(e_{i,j}))}{\sum \limits_{k \in N(i)} \exp(LeakyReLU(e_{i,k})) } $$

其中,分母部分聚合节点 \(i\) 所有邻边相似度系数的运算可以用 scatter_add 简洁优雅的实现。

\(h_i^{’}\) 的加权邻域聚合

$$ h_{i}^{’} = \sigma( \sum \limits_{k \in N(i)} \alpha_{i,k} h_{k} ) $$

这里的邻域聚合的操作逻辑基本同上,同样可以使用 scatter_add

Q:为什么要用scatter?同样一个邻域聚合的操作,我们可以使用for循环,预处理出neighbor照样可以聚合,那么我们为什么需要使用scatter呢? A:答案就是scatter支持pytorch的并行化,同时更加便于pytorch的梯度传播。(for循环本身的效率其实不低,因为一个node只有有限个neighbor)

scatter
#

self.scatter(dim, index, src)

self: tensor张量
Args:
	dim: 沿dim轴索引
	index(LongTensor): 索引
	src: 写入self中的值张量类型需要和self相同


作用将src中的每个值按index的指定索引scatter至self中

以3维张量为例,self中的值由如下公式决定:

同时self,index以及src需要满足如下条件:

  1. self、index、src的维度相同(即dim相同:self.dim()=index.dim()=src.dim(),非维度大小) 具体来说就是 index.shape (3,4,5), dim()=3,同理 src.shape=(4,5,6), dim()=3。因为需要执行如上公式的赋值,所以self、index、src的维度需要相同。
  2. index每一个维度的大小 <= src每一个维度的大小, 即 \(index.shape(i) \leq src.shape(i),\ i \in [0,src.dim())\)
  3. \(index.shape(i) \leq self.shape(i),\ i \in [0,src.dim()) \ 且 i \neq dim_{arg}\), 即除开操作维度dim之外(指的是scatter的参数dim之外),其他维度的大小都必须小于self的对应维度大小。 理解起来同样很简单,因为其他维度需要索引self,而 index[i][j][k] 在操作维度之上无所谓,他只需要满足 index[i][j][k]<=self.dim(arg_dim) 即可。

张量index的数值大小约束:

  1. index中的任意值,其大小需要在 [0, self.dim(arg_dim)-1] 的范围内。
  2. index沿dim维度的一行,值必须唯一(弱约束,违反不报错,但是会产生无意义的重复scatter)

例1.index沿dim的一行数组,值不唯一,产生scatter的重复赋值

|725

例2. index沿dim的一行数组,值唯一,scatter离散到不同的位置

|625

scatter_add
#

scatter_add的基本原理同scatter,但是self的同一位置可以多次add(即不需要满足上面数学约束的第五条,允许index沿dim轴有重复值),以GAT的邻域聚合为例:

单头GAT
#

|725

邻域相加,得到最后的结果,即 neighbor_sum[i] 为node_i的邻域聚合值。

多头GAT
#

基本原理仍然和单头相似,具体如下图所示:

image-20250421203255067

reference
#

相关文章

Miniforge:Conda solving enviroment终结者
·1308 字
Blog ST
数据清洗
·2060 字
Report ST