dgl.ops
用于图上消息传递的框架无关操作符。
GSpMM 函数
广义稀疏矩阵-稠密矩阵乘法函数。 它将融合两个步骤到一个内核中。
通过加/减/乘/除源节点和边特征来计算消息,或将节点特征复制到边。
通过求和/最大值/最小值/平均值将消息聚合为目标节点的特征。
我们的实现支持在PyTorch/MXNet/Tensorflow中的CPU/GPU上的张量作为输入。所有操作符都配备了自动求导(计算给定输出梯度的输入梯度)和广播(如果操作数的特征形状不匹配,我们首先将它们广播到相同的形状,然后应用二元操作符)。我们的广播语义遵循NumPy,请参阅 https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html 了解更多详情。
我们所说的fuses是指消息不在边上具体化,而是直接在目标节点上计算结果,从而节省内存成本。GSpMM操作的空间复杂度是\(O(|N|D)\),其中\(|N|\)指的是图中节点的数量,\(D\)指的是特征的大小(如果你的特征是一个多维张量,则\(D=\prod_{i=1}^{N}D_i\))。
以下是一个展示GSpMM如何工作的示例(我们在这里使用PyTorch作为后端,您可以通过类似的使用方式在其他框架上享受相同的便利):
>>> import dgl
>>> import torch as th
>>> import dgl.ops as F
>>> g = dgl.graph(([0, 0, 0, 1, 1, 2], [0, 1, 2, 1, 2, 2])) # 3 nodes, 6 edges
>>> x = th.ones(3, 2, requires_grad=True)
>>> x
tensor([[1., 1.],
[1., 1.],
[1., 1.]], requires_grad=True)
>>> y = th.arange(1, 13).float().view(6, 2).requires_grad_()
tensor([[ 1., 2.],
[ 3., 4.],
[ 5., 6.],
[ 7., 8.],
[ 9., 10.],
[11., 12.]], requires_grad=True)
>>> out_1 = F.u_mul_e_sum(g, x, y)
>>> out_1 # (10, 12) = ((1, 1) * (3, 4)) + ((1, 1) * (7, 8))
tensor([[ 1., 2.],
[10., 12.],
[25., 28.]], grad_fn=<GSpMMBackward>)
>>> out_1.sum().backward()
>>> x.grad
tensor([[12., 15.],
[18., 20.],
[12., 13.]])
>>> y.grad
tensor([[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.]])
>>> out_2 = F.copy_u_sum(g, x)
>>> out_2
tensor([[1., 1.],
[2., 2.],
[3., 3.]], grad_fn=<GSpMMBackward>)
>>> out_3 = F.u_add_e_max(g, x, y)
>>> out_3
tensor([[ 2., 3.],
[ 8., 9.],
[12., 13.]], grad_fn=<GSpMMBackward>)
>>> y1 = th.rand(6, 4, 2, requires_grad=True) # test broadcast
>>> F.u_mul_e_sum(g, x, y1).shape # (2,), (4, 2) -> (4, 2)
torch.Size([3, 4, 2])
对于所有操作符,输入图可以是同构图或二分图。
|
广义稀疏矩阵乘法接口。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
|
广义SpMM函数。 |
GSDDMM 函数
广义采样密集-密集矩阵乘法。 它通过在源/目标节点或边上进行加/减/乘/除/点积特征来计算边特征。
与GSpMM类似,我们的实现支持PyTorch/MXNet/Tensorflow中的CPU/GPU张量作为输入。所有操作符都配备了自动求导和广播功能。
GSDDMM的内存成本是\(O(|E|D)\),其中\(|E|\)指的是图中的边数,而\(D\)指的是特征大小。
请注意,我们支持dot
操作符,其语义上与通过求和减少最后一个维度的mul
操作符的结果相同。然而,dot
操作符在内存使用上更为高效,因为它融合了mul
和求和减少操作,这在最后一个维度上的特征大小不可忽视的情况下(例如,类似Transformer模型中的多头注意力机制)尤为重要。
以下是一个展示GSDDMM如何工作的示例:
>>> import dgl
>>> import torch as th
>>> import dgl.ops as F
>>> g = dgl.graph(([0, 0, 0, 1, 1, 2], [0, 1, 2, 1, 2, 2])) # 3 nodes, 6 edges
>>> x = th.ones(3, 2, requires_grad=True)
>>> x
tensor([[1., 1.],
[1., 1.],
[1., 1.]], requires_grad=True)
>>> y = th.arange(1, 7).float().view(3, 2).requires_grad_()
>>> y
tensor([[1., 2.],
[3., 4.],
[5., 6.]], requires_grad=True)
>>> e = th.ones(6, 1, 2, requires_grad=True) * 2
tensor([[[2., 2.]],
[[2., 2.]],
[[2., 2.]],
[[2., 2.]],
[[2., 2.]],
[[2., 2.]]], grad_fn=<MulBackward0>)
>>> out1 = F.u_div_v(g, x, y)
tensor([[1.0000, 0.5000],
[0.3333, 0.2500],
[0.2000, 0.1667],
[0.3333, 0.2500],
[0.2000, 0.1667],
[0.2000, 0.1667]], grad_fn=<GSDDMMBackward>)
>>> out1.sum().backward()
>>> x.grad
tensor([[1.5333, 0.9167],
[0.5333, 0.4167],
[0.2000, 0.1667]])
>>> y.grad
tensor([[-1.0000, -0.2500],
[-0.2222, -0.1250],
[-0.1200, -0.0833]])
>>> out2 = F.e_sub_v(g, e, y)
>>> out2
tensor([[[ 1., 0.]],
[[-1., -2.]],
[[-3., -4.]],
[[-1., -2.]],
[[-3., -4.]],
[[-3., -4.]]], grad_fn=<GSDDMMBackward>)
>>> out3 = F.copy_v(g, y)
>>> out3
tensor([[1., 2.],
[3., 4.],
[5., 6.],
[3., 4.],
[5., 6.],
[5., 6.]], grad_fn=<GSDDMMBackward>)
>>> out4 = F.u_dot_v(g, x, y)
>>> out4 # the last dimension was reduced to size 1.
tensor([[ 3.],
[ 7.],
[11.],
[ 7.],
[11.],
[11.]], grad_fn=<GSDDMMBackward>)
|
广义采样-密集-密集矩阵乘法接口。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数。 |
|
广义SDDMM函数,将源节点特征复制到边。 |
|
广义SDDMM函数,将目标节点特征复制到边。 |
与GSpMM类似,GSDDMM操作符支持同质图和二分图。
分段减少模块
DGL 提供了操作符来按段沿第一维度减少值张量。
|
分段归约操作符。 |
GatherMM 和 SegmentMM 模块
SegmentMM: DGL 提供了根据段执行矩阵乘法的操作符。
GatherMM: DGL 提供了根据给定索引收集数据并执行矩阵乘法的操作符。
|
根据给定的索引收集数据并执行矩阵乘法。 |
|
根据段执行矩阵乘法。 |
支持的数据类型
在dgl.ops
中定义的运算符支持浮点数据类型,即操作数必须是half
(float16
) /float
/double
张量。
输入张量必须具有相同的数据类型(如果一个输入张量的类型是float16,而另一个输入张量的数据类型是float32,用户必须将其中一个转换为与另一个对齐)。
float16
数据类型支持默认是禁用的,因为它对GPU的计算能力有最低要求,即 sm_53
(Pascal、Volta、Turing和Ampere架构)。
User can enable float16 for mixed precision training by compiling DGL from source (see Mixed Precision Training tutorial for details).
与消息传递API的关系
dgl.update_all
和 dgl.apply_edges
调用内置的消息/减少函数
将被分派到 dgl.ops
中定义的操作符函数调用:
>>> import dgl
>>> import torch as th
>>> import dgl.ops as F
>>> import dgl.function as fn
>>> g = dgl.rand_graph(100, 1000) # create a DGLGraph with 100 nodes and 1000 edges.
>>> x = th.rand(100, 20) # node features.
>>> e = th.rand(1000, 20)
>>>
>>> # dgl.update_all + builtin functions
>>> g.srcdata['x'] = x # srcdata is the same as ndata for graphs with one node type.
>>> g.edata['e'] = e
>>> g.update_all(fn.u_mul_e('x', 'e', 'm'), fn.sum('m', 'y'))
>>> y = g.dstdata['y'] # dstdata is the same as ndata for graphs with one node type.
>>>
>>> # use GSpMM operators defined in dgl.ops directly
>>> y = F.u_mul_e_sum(g, x, e)
>>>
>>> # dgl.apply_edges + builtin functions
>>> g.srcdata['x'] = x
>>> g.dstdata['y'] = y
>>> g.apply_edges(fn.u_dot_v('x', 'y', 'z'))
>>> z = g.edata['z']
>>>
>>> # use GSDDMM operators defined in dgl.ops directly
>>> z = F.u_dot_v(g, x, y)
由用户决定是否使用消息传递API或GSpMM/GSDDMM操作符,两者的效率相同。使用消息传递API编写的程序看起来更像DGL风格,但在某些情况下调用GSpMM/GSDDMM操作符更为简洁。
请注意,在PyTorch中,所有在dgl.ops
中定义的运算符都支持高阶梯度,因此消息传递API也支持,因为它们完全依赖于这些运算符。