使用PyTorch实现GCN

释放双眼,带上耳机,听听看~!
本文将介绍如何使用PyTorch框架实现图卷积网络(GCN),并针对Cora数据集进行实验,为对图结构数据进行分类、聚类和预测的读者提供参考。

图卷积网络(Graph Convolutional Network,GCN)是将卷积神经网络(Convolutional Neural Network,CNN)扩展到图结构数据的一种方法。在图结构数据中,节点和边都可以被视为数据点,而传统的神经网络只对向量或矩阵进行处理。因此,GCN成为了解决图结构数据上的分类、聚类和预测等问题的有效工具。本文将介绍使用PyTorch实现GCN的过程。

环境搭建

首先,需要安装Python 3.6及以上版本和PyTorch框架。可以通过以下命令安装:

pip install torch

然后还需要安装以下库:

  • networkx:用于生成图结构数据。
  • numpy:用于进行数据处理。
  • scipy:用于处理稀疏矩阵。
pip install networkx numpy scipy

数据准备

在这里,我们将使用Cora数据集来进行实验。Cora是一个引文网络,其中每个节点代表一篇论文,每个边代表两篇论文之间的引用关系。每篇论文都有一个标签,表示它属于哪个领域,如计算机科学、物理等。我们的目标是根据节点特征和它们之间的连接模式,对每个节点进行分类。

下载Cora数据集并放到与脚本文件相同的目录下,数据集包含以下文件:

  • cora.content:包含节点和特征信息。
  • cora.cites:包含边的信息。

接下来,我们需要将数据加载到Python中。可以使用以下代码块实现:

import os
import networkx as nx
import numpy as np
import scipy.sparse as sp

def load_data(path="./cora"):
    """
    加载Cora数据集
    """
    # 读取特征和标签
    features = np.genfromtxt(os.path.join(path, "cora.content"), dtype=np.float32, usecols=range(1, 1434))
    labels = np.genfromtxt(os.path.join(path, "cora.content"), dtype=np.int64, usecols=-1)

    # 创建图结构
    edges_unordered = np.genfromtxt(os.path.join(path, "cora.cites"), dtype=np.int64)
    edges = np.array(list(map(sorted, edges_unordered))))

    idx_features_labels = np.genfromtxt(os.path.join(path, "cora.content"), dtype=np.dtype(str))

    # 构建索引
    idx = np.array(idx_features_labels[:, 0], dtype=np.int64)

    # 构建特征矩阵
    features = sp.csr_matrix(features[idx])

    # 构建标签向量
    labels = np.array(labels[idx])

    # 构建邻接矩阵
    adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])), shape=(labels.shape[0], labels.shape[0]), dtype=np.float32)

    # 对称归一化邻接矩阵
    adj = normalize_adj(adj + sp.eye(adj.shape[0]))

    # 将稀疏矩阵转换为张量
    features = torch.FloatTensor(np.array(features.todense()))
    labels = torch.LongTensor(labels)
    adj = sparse_mx_to_torch_sparse_tensor(adj)

    return adj, features, labels

def normalize_adj(mx):
    """
    对称归一化邻接矩阵
    """
    rowsum = np.array(mx.sum(1))
    r_inv = np.power(rowsum, -1).flatten()
    r_inv[np.isinf(r_inv)] = 0.
    r_mat_inv = sp.diags(r_inv)
    mx = r_mat_inv.dot(mx)
    mx = mx.dot(r_mat_inv)
    return mx

def sparse_mx_to_torch_sparse_tensor(sparse_mx):
    """
    将稀疏矩阵转换为PyTorch的稀疏张量
    """
    sparse_mx = sparse_mx.tocoo().astype(np.float32)
    indices = torch.from_numpy(np.vstack((sparse_mx.row, sparse_mx.col))).long()
    values = torch.from_numpy(sparse_mx.data)
    shape = torch.Size(sparse_mx.shape)
    return torch.sparse.FloatTensor(indices, values, shape)

模型定义

在Graph Convolutional Networks中,每个节点都会沿着边接收相邻节点的信息,并通过池化聚合这些信息。使用GCN时需要注意以下几点:

  1. 对输入进行归一化处理,以保证每层输出的范围和输入相同。
  2. 对每个节点的邻居节点进行池化操作,以获得相邻节点的聚合信息。
  3. 通过权重矩阵和输入特征计算节点的激活状态。
  4. 对节点激活状态进行归一化处理或使用激活函数来调整范围。

使用PyTorch实现GCN的代码:
首先,让我们导入所需的库:

import dgl
import numpy as np
import scipy.sparse as sp
import torch
import torch.nn as nn
import torch.nn.functional as F
from dgl.data import citation_graph as citegrh
from dgl.nn.pytorch import GraphConv
from sklearn.metrics import f1_score

接下来,我们将加载引文网络数据集,并将其转换为DGL图形对象:

data = citegrh.load_cora()
features = torch.FloatTensor(data.features)
labels = torch.LongTensor(data.labels)
mask = torch.BoolTensor(data.train_mask)
g = dgl.from_scipy(sp.coo_matrix(data.graph))

然后,我们创建模型类,并定义了一个包含两个GraphConv层的GCN模型:

class GCN(nn.Module):
    def __init__(self, in_feats, h_feats, num_classes):
        super(GCN, self).__init__()
        self.conv1 = GraphConv(in_feats, h_feats)
        self.conv2 = GraphConv(h_feats, num_classes)

    def forward(self, g, x):
        h = F.relu(self.conv1(g, x))
        h = self.conv2(g, h)
        return h

在这个模型中,我们将输入通过两个GraphConv层传递,其中第一个层将特征投影到隐藏维度(h_feats),并使用ReLU作为激活函数。第二个层将输出投影到类别数(num_classes),不使用激活函数。

接下来,我们定义模型的一些超参数,以及创建模型实例、优化器和损失函数:

in_feats = features.shape[1]
h_feats = 16
num_classes = data.num_labels

model = GCN(in_feats, h_feats, num_classes)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()

然后,我们将数据传递到GPU,并开始训练模型:

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

for epoch in range(200):
    model.train()
    logits = model(g.to(device), features.to(device))
    loss = criterion(logits[mask], labels[mask])
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    model.eval()
    with torch.no_grad():
        logits = model(g.to(device), features.to(device))
        pred = logits.argmax(1).cpu().numpy()
        val_acc = f1_score(labels[data.val_mask].numpy(), pred[data.val_mask], average='micro')
        print(f'Epoch {epoch + 1}: Loss {loss.item():.4f}, Val F1: {val_acc:.4f}')

在每个epoch中,我们首先将模型的训练状态设置为“训练”,然后计算出当前批次的输出(即网络预测)。然后,我们计算该批次的交叉熵损失和梯度,然后执行反向传播并使用Adam优化器更新模型权重。最后,我们将模型状态设置为“评估”,计算模型的验证集F1分数,并记录训练过程中的损失和F1分数。

最后,我们可以使用测试集评估模型:

model.eval()
with torch.no_grad():
    logits = model(g.to(device), features.to(device))
    pred = logits.argmax(1).cpu().numpy()
    test_acc = f1_score(labels[data.test_mask].numpy(), pred[data.test_mask], average='micro')
    print(f'Test F1: {test_acc:.4f}')
本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

ImageNet预训练的非鲁棒性及解决方案研究

2023-12-6 11:01:14

AI教程

Nginx 零基础教程

2023-12-6 11:07:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索