利用官方torch版GCN训练并测试cora数据集

cora数据集

Cora数据集由2708份论文名称,及对应的特征向量组成,分成了七大类,分别是,Case_Based、Genetic_Algorithms、Neural_Networks、Probabilistic_Methods、Reinforcement_Learning、Rule_Learning、Theory。特征向量由1433个独特的单词组成,由01值描述,表示论文中这些单词的频数是否小于10,小于为0,大于为1。该数据集中,每一篇论文至少引用了该数据集里面另外一篇论文或者被另外一篇论文引用。

数据集包含两个文件,分别是Content文件和Cites文件。

Content文件

包含2708行,代表2708条论文的数据,每一行由 “< paper id > + < word attribution > + < class label >” 组成,一共1435列

  • < paper id > 表示论文的id编号
  • < word attribution > 表示论文的单词向量,01值,一共1433列,论文中这些单词的频数如果小于10,为0,大于为1
  • < class label > 改论文所属的类别

Cites文件

包含5429行,两列,< id of cited paper > + < id of citing paper >

< id of cited paper > 表示被引用论文的编号

< id of citing paper> 表示引用论文的编号

代码实现

数据预处理和数据加载

import numpy as np
import scipy.sparse as sp	# 稀疏矩阵

def load_data(path="../data/cora/", dataset="cora"):
    print('Loading {} dataset...'.format(dataset))
	# content数据加载
    idx_features_labels = np.genfromtxt("{}{}.content".format(path, dataset),dtype=np.dtype(str))
    # 获取特征向量,并将特征向量转为稀疏矩阵,
    features = sp.csr_matrix(idx_features_labels[:, 1:-1], dtype=np.float32)
    # 获取标签
    labels = encode_onehot(idx_features_labels[:, -1])
    # 搭建图
    idx = np.array(idx_features_labels[:, 0], dtype=np.int32)
    # 搭建字典,论文编号-->索引
    idx_map = {j: i for i, j in enumerate(idx)}
    # cites数据加载,shape:5429,2
    edges_unordered = np.genfromtxt("{}{}.cites".format(path, dataset),dtype=np.int32)
    # 边,将编号映射为索引,因为编号是非连续的整数
    edges = np.array(list(map(idx_map.get, edges_unordered.flatten())),dtype=np.int32).reshape(edges_unordered.shape)
    # 构建邻接矩阵
    adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])),
                        shape=(labels.shape[0], labels.shape[0]),
                        dtype=np.float32)
	# 转换为对称邻接矩阵 
    adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj)
	# 归一化特征矩阵和邻接矩阵
    features = normalize(features)
    adj = normalize(adj + sp.eye(adj.shape[0]))
	
    # 设置训练、验证和测试的数量
    idx_train = range(140)
    idx_val = range(200, 500)
    idx_test = range(500, 1500)
	
    # 转为Tensor格式
    features = torch.FloatTensor(np.array(features.todense())	# toarray返回ndarray; todense返回矩阵
    labels = torch.LongTensor(np.where(labels)[1])				
    adj = sparse_mx_to_torch_sparse_tensor(adj)
	
    idx_train = torch.LongTensor(idx_train)
    idx_val = torch.LongTensor(idx_val)
    idx_test = torch.LongTensor(idx_test)
	
    return adj, features, labels, idx_train, idx_val, idx_test
    # adj 		2708,2708
    # features 	2708,1433
    # labels	2708,      0~6

GCN网络

class GCN(nn.Module):
    def __init__(self, nfeat, nhid, nclass, dropout):
        super(GCN, self).__init__()

        self.gc1 = GraphConvolution(nfeat, nhid)	# 这里nfeat是1433个特征单词向量
        self.gc2 = GraphConvolution(nhid, nclass)	# 这里nclass是7类
        self.dropout = dropout

    def forward(self, x, adj):
        '''
        step1. gc
        step2. relu
        step3. dropout
        step4. gc
        step5. log_softmax 并输出
        '''
        x = F.relu(self.gc1(x, adj))
        x = F.dropout(x, self.dropout, training=self.training)
        x = self.gc2(x, adj)
        return F.log_softmax(x, dim=1)

    
class GraphConvolution(Module):
    """
    Simple GCN layer, similar to https://arxiv.org/abs/1609.02907
    """
    def __init__(self, in_features, out_features, bias=True):
        super(GraphConvolution, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.FloatTensor(in_features, out_features))
        if bias:
            self.bias = Parameter(torch.FloatTensor(out_features))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        stdv = 1. / math.sqrt(self.weight.size(1))
        self.weight.data.uniform_(-stdv, stdv)
        if self.bias is not None:
            self.bias.data.uniform_(-stdv, stdv)

    # 这里设,特征矩阵feature为H,邻接矩阵adj为A,权重为W,则输出为, 
    # step1. 求 H 和 W 乘积  HW
    # step2. 求 A 和 HW 乘积 AHW,这里 A = D_A ^ -1 · (A + I) 做了归一化, H = D_H^-1 · H 
    # 对于维度
    '''
    # adj 		2708,2708   		A
    # features 	2708,1433			H0
    # labels	2708,      0~6
    第一次gc后为2708,nhid
    第二次gc后为2708,7 (7个类别)
    '''
    def forward(self, input, adj):
        support = torch.mm(input, self.weight)	
        output = torch.spmm(adj, support)
        if self.bias is not None:
            return output + self.bias
        else:
            return output

    def __repr__(self):
        return self.__class__.__name__ + ' (' \
               + str(self.in_features) + ' -> ' \
               + str(self.out_features) + ')'

优化器等加载

optimizer = optim.Adam(model.parameters(),lr=args.lr, weight_decay=args.weight_decay)

训练和测试

def train(epoch):
    t = time.time()
    model.train()
    optimizer.zero_grad()
    output = model(features, adj)
    loss_train = F.nll_loss(output[idx_train], labels[idx_train])
    acc_train = accuracy(output[idx_train], labels[idx_train])
    loss_train.backward()
    optimizer.step()

    if not args.fastmode:
        # Evaluate validation set performance separately,
        # deactivates dropout during validation run.
        model.eval()
        output = model(features, adj)

    loss_val = F.nll_loss(output[idx_val], labels[idx_val])
    acc_val = accuracy(output[idx_val], labels[idx_val])
    print('Epoch: {:04d}'.format(epoch + 1),
          'loss_train: {:.4f}'.format(loss_train.item()),
          'acc_train: {:.4f}'.format(acc_train.item()),
          'loss_val: {:.4f}'.format(loss_val.item()),
          'acc_val: {:.4f}'.format(acc_val.item()),
          'time: {:.4f}s'.format(time.time() - t))
    
def test():
    model.eval()
    output = model(features, adj)
    loss_test = F.nll_loss(output[idx_test], labels[idx_test])
    acc_test = accuracy(output[idx_test], labels[idx_test])
    print("Test set results:",
          "loss= {:.4f}".format(loss_test.item()),
          "accuracy= {:.4f}".format(acc_test.item()))

其他必须的函数

def encode_onehot(labels):
    classes = set(labels)
    classes_dict = {c: np.identity(len(classes))[i, :] for i, c in
                    enumerate(classes)}
    labels_onehot = np.array(list(map(classes_dict.get, labels)),
                             dtype=np.int32)
    return labels_onehot

def normalize(mx):
    """Row-normalize sparse matrix"""
    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)
    return mx

def accuracy(output, labels):
    preds = output.max(1)[1].type_as(labels)
    correct = preds.eq(labels).double()
    correct = correct.sum()
    return correct / len(labels)

def sparse_mx_to_torch_sparse_tensor(sparse_mx):
    """Convert a scipy sparse matrix to a torch sparse tensor."""
    sparse_mx = sparse_mx.tocoo().astype(np.float32)
    indices = torch.from_numpy(
        np.vstack((sparse_mx.row, sparse_mx.col)).astype(np.int64))
    values = torch.from_numpy(sparse_mx.data)
    shape = torch.Size(sparse_mx.shape)
    return torch.sparse.FloatTensor(indices, values, shape)

输出结果

...
Epoch: 0188 loss_train: 0.4158 acc_train: 0.9200 loss_val: 0.7011 acc_val: 0.7886 time: 0.0070s
Epoch: 0189 loss_train: 0.4453 acc_train: 0.9467 loss_val: 0.6996 acc_val: 0.7914 time: 0.0080s
Epoch: 0190 loss_train: 0.4042 acc_train: 0.9400 loss_val: 0.6973 acc_val: 0.7914 time: 0.0070s
Epoch: 0191 loss_train: 0.4443 acc_train: 0.9267 loss_val: 0.6966 acc_val: 0.7914 time: 0.0070s
Epoch: 0192 loss_train: 0.4082 acc_train: 0.9400 loss_val: 0.6956 acc_val: 0.7914 time: 0.0070s
Epoch: 0193 loss_train: 0.3909 acc_train: 0.9467 loss_val: 0.6937 acc_val: 0.7971 time: 0.0080s
Epoch: 0194 loss_train: 0.4289 acc_train: 0.9333 loss_val: 0.6921 acc_val: 0.7971 time: 0.0070s
Epoch: 0195 loss_train: 0.4092 acc_train: 0.9267 loss_val: 0.6898 acc_val: 0.8000 time: 0.0070s
Epoch: 0196 loss_train: 0.3790 acc_train: 0.9600 loss_val: 0.6882 acc_val: 0.8029 time: 0.0080s
Epoch: 0197 loss_train: 0.3813 acc_train: 0.9467 loss_val: 0.6867 acc_val: 0.8000 time: 0.0070s
Epoch: 0198 loss_train: 0.4018 acc_train: 0.9533 loss_val: 0.6858 acc_val: 0.8057 time: 0.0080s
Epoch: 0199 loss_train: 0.3730 acc_train: 0.9467 loss_val: 0.6847 acc_val: 0.8114 time: 0.0070s
Epoch: 0200 loss_train: 0.3693 acc_train: 0.9600 loss_val: 0.6845 acc_val: 0.8143 time: 0.0080s
Optimization Finished!
Total time elapsed: 1.7434s
Test set results: loss= 0.7244 accuracy= 0.8230

参考链接

https://github.com/tkipf/pygcn

好的,我可以给你提供一个简单的 PyTorch GNN 实现,并且附带训练测试代码。这个实现是基于 GCN (Graph Convolutional Network)的,你可以根据需要进行改进。 首先,我们需要安装 PyTorch 和 DGL(Deep Graph Library): ```bash pip install torch dgl ``` 然后,我们可以定义一个简单的 GNN 模型: ```python import torch import torch.nn as nn import dgl.function as fn from dgl.nn.pytorch import GraphConv class GNN(nn.Module): def __init__(self, in_feats, hidden_feats, out_feats): super(GNN, self).__init__() self.conv1 = GraphConv(in_feats, hidden_feats) self.conv2 = GraphConv(hidden_feats, out_feats) def forward(self, g, features): h = self.conv1(g, features) h = torch.relu(h) h = self.conv2(g, h) return h ``` 这个 GNN 模型包含两个 GraphConv 层,每个层都是由输入特征到输出特征的线性变换,然后通过 ReLU 激活函数进行非线性变换。在这个例子中,我们使用了两个 GraphConv 层,但你可以根据需要添加更多层。 接下来,我们可以定义一个简单的训练循环: ```python import dgl import torch.optim as optim def train(model, g, features, labels, train_mask, epochs): optimizer = optim.Adam(model.parameters()) criterion = nn.CrossEntropyLoss() for epoch in range(epochs): model.train() logits = model(g, features) loss = criterion(logits[train_mask], labels[train_mask]) optimizer.zero_grad() loss.backward() optimizer.step() print('Epoch %d | Loss: %.4f' % (epoch, loss.item())) ``` 在这个训练循环中,我们使用 Adam 优化器和交叉熵损失函数对 GNN 进行训练训练过程中,我们计算模型的预测值(logits),然后根据训练集上的标签和掩码计算交叉熵损失。最后,我们通过反向传播和优化器来更新模型参数。 最后,我们可以定义一个简单的测试函数: ```python import torch.nn.functional as F def test(model, g, features, labels, test_mask): model.eval() with torch.no_grad(): logits = model(g, features) pred = logits.argmax(1) acc = F.accuracy(logits[test_mask], labels[test_mask]) print('Accuracy: %.4f' % acc.item()) ``` 在这个测试函数中,我们首先将模型设置为评估模式,然后使用模型对测试集进行预测。最后,我们计算准确率并输出结果。 现在,我们可以使用这些代码来训练测试我们的 GNN 模型: ```python import dgl.data dataset = dgl.data.CoraGraphDataset() g = dataset[0] features = g.ndata['feat'] labels = g.ndata['label'] train_mask = g.ndata['train_mask'] test_mask = g.ndata['test_mask'] model = GNN(features.shape[1], 16, dataset.num_classes) train(model, g, features, labels, train_mask, epochs=100) test(model, g, features, labels, test_mask) ``` 在这个例子中,我们使用 Cora 数据集测试我们的 GNN 模型。训练测试代码将使用上述函数进行训练测试,最后输出测试准确率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小风_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值