笔记部分
-
消息传递范式
- 用表示层中节点的节点特征, 表示从节点到节点的边的特征,消息传递图神经网络可以描述为
- 公式
图片展示了基于消息传递范式的生成节点表征的过程:
- 在图的最右侧,B节点的邻接节点(A,C)的信息传递给了B,经过信息变换得到了B的嵌入,C、D节点同。
- 在图的中右侧,A节点的邻接节点(B,C,D)的之前得到的节点嵌入传递给了节点A;在图的中左侧,聚合得到的信息经过信息变换得到了A节点新的嵌入。
- 重复多次,我们可以得到每一个节点的经过多次信息变换的嵌入。这样的经过多次信息聚合与变换的节点嵌入就可以作为节点的表征,可以用于节点的分类。
继承
MessagePassing
类的GCNConv
GCNConv的数学定义为
其中,相邻节点的特征首先通过权重矩阵 进行转换,然后按端点的度进行归一化处理,最后进行加总。
步骤细分:
- 向邻接矩阵添加自环边。
- 线性转换节点特征矩阵。
- 计算归一化系数。
- 归一化中的节点特征。
- 将相邻节点特征相加("求和 "聚合)。
作业
- MessagePassing的运行流程:
- 通过线性变换以及利用归一化系数将源节点往目标节点传递特征
- 通过多种方式(max、average、sum)对特征进行聚合
- 将聚合后的信息再次进行转换
- 继承
MessagePassing
类的规范,并请继承MessagePassing
类来自定义几个的图神经网络类
import torch
from torch.nn import functional as F
from torch_geometric.nn import MessagePassing
from torch_geometric.datasets import Planetoid
class MyGNN(MessagePassing):
"""
.. math::
\mathbf{x}^{\prime}_i = \mathbf{x}_i \cdot \mathbf{\Theta}_1 +
\sum_{j \in \mathcal{N}(i)} e_{j,i} \cdot
(\mathbf{\Theta}_2 \mathbf{x}_i - \mathbf{\Theta}_3 \mathbf{x}_j)
"""
def __init__(self, in_channels, out_channels, device):
super(MyGNN, self).__init__(aggr='add')
self.in_channels = in_channels
self.out_channels = out_channels
self.lin1 = torch.nn.Linear(in_channels, out_channels).to(device)
self.lin2 = torch.nn.Linear(in_channels, out_channels).to(device)
self.lin3 = torch.nn.Linear(in_channels, out_channels).to(device)
def forward(self, x, edge_index):
a = self.lin1(x)
b = self.lin2(x)
out = self.propagate(edge_index, a=a, b=b)
return self.lin3(x) + out
def message(self, a_i, b_j):
out = a_i - b_j
return out
def __repr__(self):
return '{}({}, {})'.format(self.__class__.__name__, self.in_channels,
self.out_channels)
device = torch.device('cuda:0')
dataset = Planetoid(root='dataset/Cora', name='Cora')
model = MyGNN(in_channels=dataset.num_features, out_channels=dataset.num_classes, device=device)
print(model)
data = dataset[0].to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
model.train()
for epoch in range(200):
optimizer.zero_grad()
out = model(data.x, data.edge_index).to(device)
pred = out.argmax(dim=1)
accuracy = int((pred[data.test_mask] == data.y[data.test_mask]).sum()) / data.test_mask.sum()
loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print("Train Epoch: {:3} Accuracy: {:.2f}%".format(epoch, accuracy.item() * 100.0))
参考自@天国之影 http://relph.gitee.io/my-team-learning/#/gnn_learning26/task02