程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2022-06(8)

2022-07(1)

从0到1实现GCN——最详细的代码实现 从0到1的GCN代码实现。详细介绍了基于GCN公式的代码实现,以及更加简单高效的基于Pytorch Geometric(PyG)的GCN的代码实现。帮助小白快速入手GCN!!!

发布于2023-03-19 12:21     阅读(370)     评论(0)     点赞(24)     收藏(5)


最近论文中需要使用图卷积神经网络(GNN),看了一些关于GCN的代码,还有基于PyTorch Geometric Temporal的代码实现,在这里做一下记录。

GCN原始代码

关于GCN的原理在这里不进行过多阐述,其他文章里面解释的已经很详细了,这里就直接进入到代码的部分。GCN的公式如下:

G C N(A, X)=\sigma\left(\widehat{D}^{-\frac{1}{2}} \hat{A} \widehat{D}^{-\frac{1}{2}} X W\right)

其中A为邻接矩阵;X为t时刻输入的节点的特征矩阵;\widehat{D}^{-\frac{1}{2}} \hat{A} \widehat{D}^{-\frac{1}{2}}是近似的图卷积滤波器,其中\hat{A}=A+I_{N}(I_{N}是N维的单位矩阵);\hat{D}是度矩阵W代表需要神经网络训练的权重矩阵;\sigma \left ( \cdot \right )是激活函数Relu。

根据公式逐步实现GCN的代码如下:

  1. def get_gcn_fact(adj):
  2. '''
  3. Function to calculate the GCN factor of a certain network snapshot
  4. 计算GCN因子(图卷积因子D^-1/2AD^-1/2)的函数
  5. :param adj: the adjacency matrix of a specific network snapshot 特定网络快照的邻接矩阵
  6. :return: the corresponding GCN factor 对应的GCN因子 DAD
  7. '''
  8. adj_ = adj + np.eye(node_num, node_num) # A+IN
  9. row_sum = np.array(adj_.sum(1)) # 求度矩阵D
  10. d_inv_sqrt = np.power(row_sum, -0.5).flatten() # D^-1/2
  11. d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0. # 将一些计算得到的NAN值赋0值
  12. d_mat_inv_sqrt = np.mat(np.diag(d_inv_sqrt)) # 将D^-1/2对角化
  13. gcn_fact = d_mat_inv_sqrt*adj_*d_mat_inv_sqrt # 计算D^-1/2AD^-1/2
  14. return gcn_fact

这里根据输入数据adj代表的邻接矩阵A,如果图的拓扑结构不会发生变化,那么GCN因子的值就是固定的,否则要根据时序变化分别计算其对应的GCN因子。

基于PyTorch Geometric的GCN实现

但由于上述代码中需要对矩阵进行复杂的计算,并且大部分图数据的邻接矩阵比较稀疏,因此这种计算方法会造成内存资源的浪费,计算效率也比较低。但是幸运的是,PyTorch Geometric(PyG)中封装了大量已经编写好的图神经网络,我们只需要调库进行使用就好了(哈哈哈大家最喜欢的部分)。

PyG库介绍

PyG的下载:Installation — pytorch_geometric documentation里面提供了各种安装方法。

 提供的各种神经网络层:

 部分图卷积操作层:

 (哈哈哈哈哈哈因为最近一直在看GNN方面的文章,有这些库可以直接调真的是救大命了)。

言归正传,根据PyG实现GCN的代码如下:

  1. from torch_geometric.nn import GCNConv
  2. class GCN(torch.nn.Module):
  3. def __init__(self, node_features, input_size, output_size):
  4. super(GCN, self).__init__()
  5. self.conv1 = GCNConv(node_features, input_size)
  6. self.MLP = torch.nn.Sequential(
  7. torch.nn.Linear(input_size, input_size // 2),
  8. torch.nn.ReLU(inplace=True),
  9. torch.nn.Linear(input_size // 2, input_size // 4),
  10. torch.nn.ReLU(inplace=True),
  11. torch.nn.Linear(input_size // 4, output_size))
  12. self.relu = torch.nn.ReLU()
  13. def forward(self, x, edge_index, edge_weight):
  14. '''
  15. GCN
  16. '''
  17. x = self.relu(self.conv1(x, edge_index))
  18. x = F.dropout(x, training=self.training)
  19. x = self.MLP(x)
  20. return x

这里面我们使用GCN对输入数据进行编码,MLP全连接层对提取到的数据进行解码,实现了一个简单的对输入数据进行特征提取的网络。

现有模型中,有许多模型在特征提取时对GCN的处理结果进行拼接处理的,比如:\left ( X,GCN \right ),因此对上述模型进行改进:

  1. class GCN(torch.nn.Module):
  2. def __init__(self, node_features, input_size, output_size):
  3. super(GCN, self).__init__()
  4. self.conv1 = GCNConv(node_features, input_size)
  5. self.linear = torch.nn.Linear(node_features+input_size, input_size)
  6. self.MLP = torch.nn.Sequential(
  7. torch.nn.Linear(input_size, input_size // 2),
  8. torch.nn.ReLU(inplace=True),
  9. torch.nn.Linear(input_size // 2, input_size // 4),
  10. torch.nn.ReLU(inplace=True),
  11. torch.nn.Linear(input_size // 4, output_size))
  12. self.relu = torch.nn.ReLU()
  13. def forward(self, x, edge_index, edge_weight):
  14. '''
  15. (x, GCN)
  16. '''
  17. lst = list()
  18. lst.append(x)
  19. x = self.relu(self.conv1(x, edge_index, edge_weight)) #根据数据集确定有没有edge_weight
  20. x = F.dropout(x, training=self.training)
  21. lst.append(x)
  22. x = torch.cat(lst, dim=1)
  23. # print('cat', x.shape)cat torch.Size([node_num, node_features+input_size])
  24. x = self.relu(self.linear(x))
  25. x = F.dropout(x, training=self.training)
  26. x = self.MLP(x)

完整代码

模型中简单的随机生成了图数据,只是为了展示GCN模型在具体代码中应该如何使用。

分类模型:

  1. import torch
  2. import random
  3. import matplotlib.pyplot as plt
  4. from tqdm import tqdm
  5. import numpy as np
  6. import networkx as nx
  7. import torch.nn.functional as F
  8. from torch_geometric.nn import GCNConv
  9. def create_mock_data(number_of_nodes, edge_per_node, in_channels):
  10. """
  11. Creating a mock feature matrix and edge index.
  12. """
  13. graph = nx.watts_strogatz_graph(number_of_nodes, edge_per_node, 0.5)
  14. edge_index = torch.LongTensor(np.array([edge for edge in graph.edges()]).T)
  15. X = torch.FloatTensor(np.random.uniform(-1, 1, (number_of_nodes, in_channels)))
  16. return X, edge_index
  17. def create_mock_edge_weight(edge_index):
  18. """
  19. Creating a mock edge weight tensor.
  20. """
  21. return torch.FloatTensor(np.random.uniform(0, 1, (edge_index.shape[1])))
  22. def create_mock_target(number_of_nodes, number_of_classes):
  23. """
  24. Creating a mock target vector.
  25. """
  26. return torch.LongTensor([random.randint(0, number_of_classes-1) for node in range(number_of_nodes)])
  27. class GCN(torch.nn.Module):
  28. def __init__(self, node_features, input_size, num_classes):
  29. super(GCN, self).__init__()
  30. self.conv1 = GCNConv(node_features, input_size)
  31. self.MLP = torch.nn.Sequential(
  32. torch.nn.Linear(input_size, input_size // 2),
  33. torch.nn.ReLU(inplace=True),
  34. torch.nn.Linear(input_size // 2, input_size // 4),
  35. torch.nn.ReLU(inplace=True),
  36. torch.nn.Linear(input_size // 4, num_classes))
  37. self.relu = torch.nn.ReLU()
  38. def forward(self, x, edge_index, edge_weight):
  39. '''
  40. GCN
  41. '''
  42. x = self.relu(self.conv1(x, edge_index))
  43. x = F.dropout(x, training=self.training)
  44. x = self.MLP(x)
  45. return F.log_softmax(x, dim=1)
  46. node_features = 100
  47. node_count = 1000
  48. input_size = 32
  49. num_classes = 10
  50. edge_per_node = 15
  51. epochs = 200
  52. learning_rate = 0.01
  53. weight_decay = 5e-4
  54. model = GCN(node_features=node_features, input_size=input_size, num_classes=num_classes)
  55. optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
  56. model.train()
  57. loss_list = []
  58. for epoch in tqdm(range(epochs)):
  59. optimizer.zero_grad()
  60. x, edge_index = create_mock_data(node_count, edge_per_node, node_features)
  61. edge_weight = create_mock_edge_weight(edge_index)
  62. scores = model(x, edge_index, edge_weight)
  63. target = create_mock_target(node_count, num_classes)
  64. loss = F.nll_loss(scores, target)
  65. loss_list.append(loss.item())
  66. loss.backward()
  67. optimizer.step()
  68. plt.plot(loss_list)
  69. plt.xlabel("Epoch")
  70. plt.ylabel("MSE")
  71. plt.title("loss")
  72. plt.show()

损失函数:

 预测模型:

这里不再赘述预测模型的代码,其实预测问题和分类问题非常相像,预测模型只需要去掉模型最后的softmax函数,改变output_size就好。

如果想看预测模型,或者其他图神经网络模型的欢迎大家在评论区讨论。有哪里写的不对的地方也欢迎指正!

原文链接:https://blog.csdn.net/m0_53961910/article/details/127856355



所属网站分类: 技术文章 > 博客

作者:hghgh

链接:https://www.pythonheidong.com/blog/article/1940069/0edf4d58c3e1fd9ad4a5/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

24 0
收藏该文
已收藏

评论内容:(最多支持255个字符)