GraphConv
- class dgl.nn.pytorch.conv.GraphConv(in_feats, out_feats, norm='both', weight=True, bias=True, activation=None, allow_zero_in_degree=False)[source]
基类:
Module
图卷积层,来自 Semi-Supervised Classification with Graph Convolutional Networks
其数学定义如下:
\[h_i^{(l+1)} = \sigma(b^{(l)} + \sum_{j\in\mathcal{N}(i)}\frac{1}{c_{ji}}h_j^{(l)}W^{(l)})\]其中 \(\mathcal{N}(i)\) 是节点 \(i\) 的邻居集合,\(c_{ji}\) 是节点度的平方根的乘积(即 \(c_{ji} = \sqrt{|\mathcal{N}(j)|}\sqrt{|\mathcal{N}(i)|}\)),\(\sigma\) 是一个激活函数。
如果提供了每条边的权重张量,加权图卷积定义为:
\[h_i^{(l+1)} = \sigma(b^{(l)} + \sum_{j\in\mathcal{N}(i)}\frac{e_{ji}}{c_{ji}}h_j^{(l)}W^{(l)})\]其中 \(e_{ji}\) 是从节点 \(j\) 到节点 \(i\) 的边的标量权重。这与论文中的加权图卷积网络公式不等价。
要自定义归一化项 \(c_{ji}\),可以首先将模型的
norm
设置为'none'
,然后将预归一化的 \(e_{ji}\) 传递给前向计算。我们提供了EdgeWeightNorm
,用于按照 GCN 论文对标量边权重进行归一化。- 参数:
in_feats (int) – 输入特征大小;即 \(h_j^{(l)}\) 的维数。
out_feats (int) – 输出特征大小;即 \(h_i^{(l+1)}\) 的维数。
norm (str, 可选) –
如何应用归一化器。可以是以下值之一:
right
,将聚合后的消息除以每个节点的入度,这等价于对接收到的消息求平均。none
,不应用任何归一化。both
(默认),消息按上面的 \(1/c_{ji}\) 进行缩放,等价于对称归一化。left
,将从每个节点发出的消息除以其出度,等价于随机游走归一化。
weight (bool, 可选) – 如果为 True,应用一个线性层。否则,在没有权重矩阵的情况下聚合消息。
bias (bool, 可选) – 如果为 True,在输出中添加一个可学习的偏置。默认值:
True
。activation (可调用的激活函数/层 或 None, 可选) – 如果不为 None,则对更新后的节点特征应用激活函数。默认值:
None
。allow_zero_in_degree (bool, 可选) – 如果图中存在入度为 0 的节点,则这些节点的输出将无效,因为没有消息会传递到这些节点。这对于某些应用是有害的,会导致隐性性能下降。如果此模块在输入图中检测到入度为 0 的节点,将引发 DGLError。通过将其设置为
True
,将抑制此检查,并让用户自行处理。默认值:False
。
- weight
可学习的权重张量。
- 类型:
torch.Tensor
- bias
可学习的偏置张量。
- 类型:
torch.Tensor
注意
入度为零的节点将导致输出值无效。这是因为没有消息会传递到这些节点,聚合函数将应用于空输入。避免这种情况的一种常见做法是,如果图是同构的,则为图中的每个节点添加自环,这可以通过以下方式实现:
>>> g = ... # a DGLGraph >>> g = dgl.add_self_loop(g)
调用
add_self_loop
对于某些图可能无效,例如异构图,因为无法为自环边确定边类型。对于这些情况,将allow_zero_in_degree
设置为True
以解除代码阻塞并手动处理入度为零的节点。一种常见的处理方法是在卷积后过滤掉入度为零的节点。示例
>>> import dgl >>> import numpy as np >>> import torch as th >>> from dgl.nn import GraphConv
>>> # Case 1: Homogeneous graph >>> g = dgl.graph(([0,1,2,3,2,5], [1,2,3,4,0,3])) >>> g = dgl.add_self_loop(g) >>> feat = th.ones(6, 10) >>> conv = GraphConv(10, 2, norm='both', weight=True, bias=True) >>> res = conv(g, feat) >>> print(res) tensor([[ 1.3326, -0.2797], [ 1.4673, -0.3080], [ 1.3326, -0.2797], [ 1.6871, -0.3541], [ 1.7711, -0.3717], [ 1.0375, -0.2178]], grad_fn=<AddBackward0>) >>> # allow_zero_in_degree example >>> g = dgl.graph(([0,1,2,3,2,5], [1,2,3,4,0,3])) >>> conv = GraphConv(10, 2, norm='both', weight=True, bias=True, allow_zero_in_degree=True) >>> res = conv(g, feat) >>> print(res) tensor([[-0.2473, -0.4631], [-0.3497, -0.6549], [-0.3497, -0.6549], [-0.4221, -0.7905], [-0.3497, -0.6549], [ 0.0000, 0.0000]], grad_fn=<AddBackward0>)
>>> # Case 2: Unidirectional bipartite graph >>> u = [0, 1, 0, 0, 1] >>> v = [0, 1, 2, 3, 2] >>> g = dgl.heterograph({('_U', '_E', '_V') : (u, v)}) >>> u_fea = th.rand(2, 5) >>> v_fea = th.rand(4, 5) >>> conv = GraphConv(5, 2, norm='both', weight=True, bias=True) >>> res = conv(g, (u_fea, v_fea)) >>> res tensor([[-0.2994, 0.6106], [-0.4482, 0.5540], [-0.5287, 0.8235], [-0.2994, 0.6106]], grad_fn=<AddBackward0>)
- forward(graph, feat, weight=None, edge_weight=None)[source]
描述
计算图卷积。
- 参数 graph:
图。
- 类型 graph:
DGLGraph
- 参数 feat:
如果给定一个 torch.Tensor,它表示形状为 \((N, D_{in})\) 的输入特征,其中 \(D_{in}\) 是输入特征的大小,\(N\) 是节点数量。如果给定一对 torch.Tensor(适用于二分图),这对张量必须包含形状为 \((N_{in}, D_{in_{src}})\) 和 \((N_{out}, D_{in_{dst}})\) 的两个张量。
- 类型 feat:
torch.Tensor 或一对 torch.Tensor
- 参数 weight:
可选的外部权重张量。
- 类型 weight:
torch.Tensor, 可选
- 参数 edge_weight:
可选的边上的张量。如果给定,卷积将根据消息进行加权。
- 类型 edge_weight:
torch.Tensor, 可选
- 返回值:
输出特征
- 返回类型:
torch.Tensor
- 引发 DGLError:
情况 1:如果输入图中存在入度为 0 的节点,将引发 DGLError,因为没有消息会传递到这些节点。这将导致输出无效。通过将
allow_zero_in_degree
参数设置为True
,可以忽略此错误。情况 2:提供了外部权重,同时模块自身也定义了权重参数。
注意
输入形状:\((N, *, \text{in_feats})\),其中 * 表示任意数量的附加维度,\(N\) 是节点数量。
输出形状:\((N, *, \text{out_feats})\),其中除最后一个维度外,所有其他维度与输入形状相同。
权重形状:\((\text{in_feats}, \text{out_feats})\)。