https://zhuanlan.zhihu.com/p/113862170这篇讲的很好。
官方文档中GCNConv的详细注释
图卷积类似于CNN的卷积操作,图卷及网络与常规CNN网络的搭建流程类似,区别在于自定义的图卷积层
图卷积层需要自己实现三个函数:forward,message,update
根据上面公式,我们要做三件事:
- 邻居信息变换:
- 邻居信息聚合:
- 自身信息与聚合后邻居信息的变换得到自身的最终信息:
在PyG中,这个流程被对应到self.propagate这个操作中,self.propagate将分别执行上述三件事:
- self.message,
,输入为处理信息所需参数,
,边信息等,需要什么输入什么
- self.aggregate,
,自动完成,初始化时指定方式即可
- self.update,
,输入为:aggr_out及其他所需参数
以GCNConv为例,对节点实现的操作公式如下:
上面公式与符号一一对应如下:
- self.message中对邻居节点作linear变换,然后归一化
- self.aggregate以sum方式聚合
- self.update对聚合后特征不作处理
代码如下:
import torch
from torch_geometric.nn import MessagePassing
from torch_geometric.utils import add_self_loops, degree
class GCNConv(MessagePassing):
def __init__(self, in_channels, out_channels):
super(GCNConv, self).__init__(aggr='add') # "Add" aggregation.
self.lin = torch.nn.Linear(in_channels, out_channels)
def forward(self, x, edge_index):
# x has shape [N, in_channels]
# edge_index has shape [2, E]
# Step 1: Add self-loops to the adjacency matrix.
edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))
# Step 2: Linearly transform node feature matrix.
x = self.lin(x)
# Step 3-5: Start propagating messages.
return self.propagate(edge_index, size=(x.size(0), x.size(0)), x=x)
def message(self, x_j, edge_index, size):
# x_j has shape [E, out_channels]
# Step 3: Normalize node features.
row, col = edge_index
deg = degree(row, size[0], dtype=x_j.dtype)
deg_inv_sqrt = deg.pow(-0.5)
norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]
return norm.view(-1, 1) * x_j
def update(self, aggr_out):
# aggr_out has shape [N, out_channels]
# Step 5: Return new node embeddings.
return aggr_out
message详细说明:
linear变换可在messge内也可在forward内做
计算度矩阵:
edge_index的size为(2,边数目),row,col 分别为一条边的两个顶点
degree函数其实是对row或col作统计运算,顶点在edge_index中出现的次数就是该顶点的度