其中:
- 代表节点i在第k层的特征。
- 是节点i的邻居集合。
- 是节点i的度(或邻居的数量)。
- 是可学习的权重矩阵。
- 是偏置向量。
数学公式的含义
- 加入自循环:通过在求和符号中包含 ,我们考虑了每个节点自己的特征,即添加了自循环。
- 归一化系数:系数 是为了对节点的度进行归一化,这有助于模型的稳定性和训练效果。
- 线性特征转换:通过乘以权重矩阵 并添加偏置 ,每个节点的特征进行了线性变换。
- 特征聚合:通过对所有邻居的转换后的特征进行求和,我们实际上在每个节点上聚合了其所有邻居的信息。
- 使用“add”聚合
- 这是一个将所有邻接节点与自己特征先归一化再加和成为新节点的过程
添加自循环
1 | Examples: |
节点的度
在您之前提到的GCN的公式中,i
和 j
分别代表两个节点,其中节点 i
是中心节点,而节点 j
是它的一个邻居或它自己(当考虑自循环时)。
自循环产生的边缘特征如何计算
在图神经网络(GNN)中,自循环指的是节点到自身的边。当我们在图中添加自循环时,通常是为了确保节点在消息传递时可以考虑自己的特征。但这通常涉及到节点特征,而不是边的特征。
当涉及到具有边特征的图时,处理自循环的方式可能会有所不同:
- 没有边特征 :在这种情况下,添加自循环只会影响节点特征的聚合,并不涉及边的特征。
- 有边特征 :当图中存在边特征时,为自循环添加特定的边特征会更加复杂。在这种情况下,您有几种选择:
- 零向量 :为自循环边分配一个零向量作为其特征。
- 特殊值 :为自循环边分配一个固定的特殊值或标志。
- 节点特征衍生 :从相关节点的特征衍生出自循环边的特征。
- 学习 :如果网络结构允许,可以设计一个机制来学习自循环边的特征。需要注意的是,不是所有的GNN都会使用或需要边特征。在某些情况下,特别是当使
用基本的GNN架构如GCN时,只考虑节点特征可能就足够了。
计算
lin进行W矩阵的全连接,norm求出了所有ij节点的归一化系数
propagate包含了message方法 norm.view(-1, 1) * x_j
讲norm转置,再与节点特征相乘
然后加上b求出了新xi
实现边缘卷积
边缘卷积相对于标准的GCN(Graph Convolutional Network)层更多地实现了以下方面的功能和考虑:
上图公式有错误,应该为(xi, xi-xj)
边缘卷积利用了"max"聚合方式,这意味着它在考虑一个节点的所有邻居节点时,会选择MLP计算后的最大值作为该节点的新特征。
具体来说:
- 对于节点i的每一个邻居j,计算特征差值
- 将这个差值通过MLP得到新的特征。
- 对于节点i的所有邻居,选择这些新特征中的最大值作为节点i的新特征。
因此,当我们说选择的是"最大值"时,我们是指对于节点i,我们选择其所有邻居经过MLP处理后的最大特征值来表示节点i的新特征。这种选择方式捕获了节点与其邻居之间的最显著差异,这可能在某些任务中是很有用的。
练习题
对于 GCNConv:
- row 和 col 分别保存了什么信息?
edge_index
的形状是[2, E]
,其中E是边的数量。row
和col
是edge_index
的两个维度。在这种情况下,col
表示边的起始节点,而row
表示边的目标节点。 - degree()函数的作用是什么?
degree()
函数计算图中每个节点的度。在图论中,一个节点的度是指与它相连的边的数量。 - 为什么使用 degree(col, …) 而不是 degree(row, …)?
在GCN中,通常使用degree(col, ...)
来计算每个节点的出度,这与信息传播的方向有关。 - deg_inv_sqrt[col] 和 deg_inv_sqrt[row] 的作用是什么?
这两者都是GCN归一化步骤中用到的因子。使用节点的度的平方根的倒数作为归一化因子,这有助于训练的稳定性和模型的性能。 - 在 message() 函数中 x_j 保存了哪些信息?如果 self.lin 表示恒等函数,x_j 的确切内容是什么?
x_j
保存了源节点的特征。如果self.lin
是恒等函数,那么x_j
就是原始的、未经修改的节点j
的特征。 - 为 GCNConv 添加一个 update() 函数,该函数将转换后的中心节点特征添加到聚合输出中。
1 | def update(self, aggr_out, x): |
对于 EdgeConv:
- x_i 和 x_j - x_i 分别是什么?
x_i
是目标节点的特征,而x_j - x_i
表示的是源节点与目标节点特征之间的差异。 - torch.cat([x_i, x_j - x_i], dim=1) 的作用是什么?为什么选择 dim = 1?
torch.cat([x_i, x_j - x_i], dim=1)
的作用是将x_i
和x_j - x_i
沿着特征维度进行拼接。选择dim=1
是因为我们希望在特征维度上进行拼接,这样每个节点的新特征长度会是原来的两倍。