如何利用PyG实现一个图卷积神经网络并在Cora数据集上进行训练

PyG是基于PyTorch的一个图神经网络框架,包含了大量经典图神经网络论文中的模型结构

Cora是一个小规模图神经网络数据集,包含2708篇论文,每篇论文分为7个类别,每篇论文的属性用一个1433维的向量表示,论文之间通过引用关系形成一个无向图的结构。

Cora数据集上的任务是判断一篇论文的所属类别,是一个在图结构数据上的分类问题。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
from torch_geometric.datasets import Planetoid
from torch_geometric.utils import to_networkx
import networkx as nx
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'


#####################################
# 数据准备
#####################################
# 通过Planetoid下载数据
dataset = Planetoid(
    # 数据保存的路径
    # 如果指定目录下不存在数据集
    # PyG会自己进行下载
    root='../data/Cora',
    # 要使用的数据集
    name='Cora',
)
# 查看数据集基本信息
print(dataset.data)
print('包含的类别数:', dataset.num_classes)
print('边特征的维度:', dataset.num_edge_features)
print('边的数量:', dataset.data.edge_index.shape[1] / 2)
print('节点特征的维度:', dataset.num_node_features)
print('节点的数量:', dataset.data.x.shape[0])
print('节点属性特征:', dataset.data.x)

# 查看网络数据中节点的连接方式
# 列表中的每一对代表对应节点之间有边相连接,比如0-633,0-1862
print(dataset.data.edge_index)

# 获取训练集、测试集、验证集数据量
print('训练集节点数量:', sum(dataset.data.train_mask))
print('验证集节点数量:', sum(dataset.data.val_mask))
print('测试集节点数量:', sum(dataset.data.test_mask))

# 检查数据集是否是无向图
print(dataset.data.is_undirected())

# 将数据集转换为网络的形式方便可视化处理
CoraNet = to_networkx(dataset.data)
# 转换为无向图,因为前面我们判断出来Cora是无向图
CoraNet = CoraNet.to_undirected()
# 在网络形式上获取数据集的信息
print('网络中边的数量:', CoraNet.number_of_edges())
print('网络中节点的数量:', CoraNet.number_of_nodes())
# 每个节点的类别
Node_class = dataset.data.y.data.numpy()
print(Node_class)
# 获取每个节点的度
Node_degree = pd.DataFrame(
    data=CoraNet.degree,
    columns=['Node', 'Degree']
)
# 降序排列
Node_degree = Node_degree.sort_values(
    by=['Degree'],
    # 降序
    ascending=False
)
# 因为排序后索引会改变,重置一下索引
Node_degree = Node_degree.reset_index(drop=True)
# 使用直方图可视化度最多的前30个节点
# 代表了被引用最多
# 说明节点上代表的论文是比较重要的
Node_degree.iloc[0:30, :].plot(x='Node', y='Degree', kind='bar', figsize=(10, 10))
plt.xlabel('Node', size=12)
plt.ylabel('Degree', size=12)
plt.show()
# 可视化训练集数据
# 网络中每个节点的位置
pos = nx.spring_layout(CoraNet)
# 为不同类别的节点赋予不同的颜色
nodecolor = [
    'red',
    'blue',
    'green',
    'yellow',
    'peru',
    'violet',
    'cyan',
]
# 训练数据集数据的索引
nodelabel = np.arange(0, 140)
# 对应的类别
Node_class = dataset.data.y.data.numpy()[0:140]
plt.figure(figsize=(10, 10))
# np.unique(Node_class)表示获取训练集的类别
# len()表示获取类别数
# np.arange()表示生成从0到类别数的索引
for ii in np.arange(len(np.unique(Node_class))):
    # 获取相应类别的节点
    nodelist = nodelabel[Node_class == ii]
    # 绘图
    nx.draw_networkx_nodes(
        # 要画的图
        CoraNet,
        # 每个节点在图中的位置
        pos,
        # 具体要画哪些节点
        nodelist=list(nodelist),
        # 节点大小
        node_size=50,
        # 节点颜色
        node_color=nodecolor[ii],
        # 透明度
        alpha=0.8,
    )
plt.show()


##############################################
# 搭建图卷积神经网络
##############################################
class GCNNet(torch.nn.Module):
    # 网络初始化
    def __init__(self, input_feature, num_class):
        '''
        :param input_feature: 每个节点属性的维度
        :param num_class: 节点所属的类别数
        '''
        super(GCNNet, self).__init__()
        self.input_feature = input_feature
        self.num_classes = num_class
        # 图卷积层
        # 出自于论文《Semi-Supervised Classification with Graph Convolutional Networks》
        # 具体计算公式可查阅论文
        self.conv1 = GCNConv(input_feature, 32)
        self.conv2 = GCNConv(32, num_class)

    # 前向传递过程
    def forward(self, data):
        # data是输入到网络中的训练数据
        # 获取节点的属性信息和边的连接信息
        x, edge_index = data.x, data.edge_index
        # 卷积,利用相邻节点的信息更新自己的特征表示
        x = self.conv1(x, edge_index)
        # 激活
        x = F.relu(x)
        x = self.conv2(x, edge_index)
        # 预测类别
        output = F.softmax(x, dim=1)
        return output


# 初始化网络
input_feature = dataset.num_node_features
num_class = dataset.num_classes
mygcn = GCNNet(input_feature, num_class)

####################################################################
# 训练图卷积神经网络
####################################################################
# 当前设备
# 如果GPU可用用GPU,否则用CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 将模型和训练数据放在设备上
model = mygcn.to(device)
data = dataset[0].to(device)
# Adam优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
# 损失值
train_loss_all = []
val_loss_all = []
# 开启训练
model.train()
print('图卷积神经网络开始训练!')
# 训练200轮
for epoch in range(200):
    print('=' * 100)
    print('当前训练轮次:', epoch)
    # 梯度清零
    optimizer.zero_grad()
    # 获取模型对于数据的类别预测
    out = model(data)
    # 计算损失
    loss = F.cross_entropy(out[data.train_mask], data.y[data.train_mask])
    # 反向传播
    loss.backward()
    optimizer.step()
    train_loss_all.append(loss.data.numpy())

    # 计算在验证集上的损失
    loss = F.cross_entropy(out[data.val_mask], data.y[data.val_mask])
    val_loss_all.append(loss.data.numpy())
    print('Epoch:{},train_loss:{},val_loss:{}'.format(epoch, train_loss_all[-1], val_loss_all[-1]))


# 保存训练完毕的网络
torch.save(model, 'GCNNet.pt')

你可能感兴趣的:(代码,神经网络,深度学习)