GCN实战

数据集采用的是Cora数据集
首先导入所需的库

import itertools
import os
import os.path as osp
import pickle
import urllib
from collections import namedtuple

import numpy as np
import scipy.sparse as sp
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

创建一个类CoraData对数据进行预处理

Data = namedtuple('Data', ['x', 'y', 'adjacency','train_mask', 'val_mask', 'test_mask'])
def tensor_from_numpy(x,device):
	return torch.from_numpy(x).to(device)
class CoraData(object):
	download_url = "https://raw.githubusercontent.com/kimiyoung/planetoid/master/data"
	filenames = ["ind.cora.{}".format(name) for name in ['x', 'tx', 'allx', 'y', 'ty', 'ally', 'graph', 'test.index']]
	def __init__(self, data_root="cora", rebuild=False):
		 self.data_root = data_root
		 save_file = osp.join(self.data_root, "processed_cora.pkl")
		 if osp.exists(save_file) and not rebuild:
		 	print("Using Cached file: {}".format(save_file))
		 	self._data = pickle.load(open(save_file, "rb"))
		 else:
			 self.maybe_download()
			 self._data = self.process_data()
			 with open(save_file, "wb") as f:
				 pickle.dump(self.data, f)
			print("Cached file: {}".format(save_file))
	def data(self):
		return self._data
	def process_data(self):
		print("Process data ...")
		_, tx, allx, y, ty, ally, graph, test_index = [self.read_data(osp.join(self.data_root, "raw", name)) for name in self.filenames]
		train_index = np.arange(y.shape[0])
		val_index = np.arange(y.shape[0], y.shape[0] + 500)
		sorted_test_index = sorted(test_index)
		x = np.concatenate((allx, tx), axis=0)
		y = np.concatenate((ally, ty), axis=0).argmax(axis=1)
		x[test_index] = x[sorted_test_index]
		y[test_index] = y[sorted_test_index]
		num_nodes = x.shape[0]
		train_mask = np.zeros(num_nodes, dtype=np.bool)
		val_mask = np.zeros(num_nodes, dtype=np.bool)
		test_mask = np.zeros(num_nodes, dtype=np.bool)
		train_mask[train_index] = True
		val_mask[val_index] = True
		
		test_mask[test_index] = True
		adjacency = self.build_adjacency(graph)
		print("Node's feature shape: ", x.shape)        
		print("Node's label shape: ", y.shape)        
		print("Adjacency's shape: ", adjacency.shape)
		print("Number of training nodes: ", train_mask.sum())        
		print("Number of validation nodes: ", val_mask.sum())        
		print("Number of test nodes: ", test_mask.sum()) 
		return Data(x=x, y=y, adjacency=adjacency,train_mask=train_mask, val_mask=val_mask, test_mask=test_mask)
	def maybe_download(self):#返回最后文件名,最后的/后面的        
		save_path = os.path.join(self.data_root,"raw")        		
		for name in self.filenames:            
			if not osp.exists(osp.join(save_path,name)):                
				self.download_data("{}/{}".format(self.download_url,name),save_path)
	def build_adjacency(adj_dict):                
		edge_index = []        
		num_nodes = len(adj_dict)        
		for src,dst in adj_dict.items():            
			edge_index.extend([src,v] for v in dst)            
			edge_index.extend([v,src] for v in dst)        #去除重复的边        
		edge_index = list(k for k, _ in itertools.groupby(sorted(edge_index)))#相同的归为一组        
		edge_index = np.asarray(edge_index)#变成数组        
		adjacency = sp.coo_matrix((np.ones(len(edge_index)),(edge_index[:,0],edge_index[:,1])),shape = (num_nodes,num_nodes),dtype = "float32")#构造稀疏矩阵        
		return adjacency	

	def read_data(path):       
		name = osp.basename(path)       
		if name == "ind.cora.test.index":            
	 		out = np.genfromtxt(path,dtype="int64")          
	 		return out        
	 	else:            
	 		out = pickle.load(open(path,"rb"),encoding = "latin1")            
	 		out = out.toarray() if hasattr(out,"toarray") else out            
	 		return out

	def download_data(url, save_path):              
		if not os.path.exists(save_path):            
			os.makedirs(save_path)        
		data = urllib.request.urlopen(url)        
		filename = os.path.split(url)[-1]         
		with open(os.path.join(save_path, filename), 'wb') as f:            
			f.write(data.read())         
		return True
	def normalization(adjacency):              
		adjacency += sp.eye(adjacency.shape[0])    
		degree = np.array(adjacency.sum(1)) #此时的度矩阵的对角线的值 为 邻接矩阵 按行求和        
		d_hat = sp.diags(np.power(degree, -0.5).flatten()) #对度矩阵对角线的值取-0.5次方 再转换为对角矩阵        
		return d_hat.dot(adjacency).dot(d_hat).tocoo()

图卷积层定义

class GraphConvolution(nn.Module):    
	def __init__(self, input_dim, output_dim, use_bias=True):
		super(GraphConvolution,self).__init__()        self.input_dim = input_dim        
		self.output_dim = output_dim        self.use_bias = use_bias        
		self.weight = nn.Parameter(torch.Tensor(input_dim,output_dim))        
		if self.use_bias:
			self.bias = nn.Parameter(torch.Tensor(output_dim))
		else:
			self.register_parameter('bias',None)
		self.reset_parameters()
	def reset_parameters(self):        
		init.kaiming_uniform_(self.weight)        
		if self.use_bias:            
			init.zeros_(self.bias)
	def forward(self,adjacency,input_feature):
		support = torch.mm(input_feature,self.weight)
		output = torch.sparse.mm(adjacency,support)
		if self.use_bias:            
			output += self.bias        
		return output
	def __repr__(self):        
		return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.output_dim) + ')'

模型定义

class GcnNet(nn.Module):
    """        定义一个包含两层GraphConvolution的模型    """
        def __init__(self,input_dim = 1433):        
        	super(GcnNet,self).__init__()        
        	self.gcn1 = GraphConvolution(input_dim, 16)        
        	self.gcn2 = GraphConvolution(16,7)     
        def forwaer(self,adjacency,feature):        
        	h = F.relu(self.gcn1(adjacency,feature))        
        	logits = self.gcn2(adjacency,h)        
        	return logits

模型训练

learning_rate = 0.1
weight_decay = 5e-4#权重衰减
8epochs = 200
device = "cuda" if torch.cuda.is_available() else "cpu"
model = GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(),lr = learning_rate,weight_decay = weight_decay)
dataset = CoraData().datax = dataset.x / dataset.x.sum(1,keepdims = True)# 归一化数据,使得每一行和为1,数据归一化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。,sum(1),1是按行,0是按列
tensor_x = torch.from_numpy(x).to(device)、
tensor_y = torch.from_numpy(dataset.y).to(device)
tensor_train_mask = torch.from_numpy(dataset.train_mask).to(device)
tensor_val_mask = torch.from_numpy(dataset.val_mask).to(device)
tensor_test_mask = torch.from_numpy(dataset.test_mask).to(device)
normalize_adjacency = CoraData.normalization(dataset.adjacency)# 规范化邻接矩阵
indices = torch.from_numpy(np.asarray([normalize_adjacency.row,normalize_adjacency.col]).astype('int64')).long
values = torch.from_numpy(normalize_adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices,values,(2708,2708)).to(device)
def train():
    loss_history = []    
    val_acc_history = []    
    model.train()    
    train_y = tensor_y[tensor_train_mask]    
    for epoch in range(epochs):
    	logits = model(tensor_adjacency, tensor_x)  # 前向传播        
    	train_mask_logits = logits[tensor_train_mask]   # 只选择训练节点进行监督        
    	loss = criterion(train_mask_logits,train_y)      
    	optimizer.zero_grad()        
    	loss.backward()# 反向传播计算参数的梯度        
    	optimizer.step()    # 使用优化方法进行梯度更新,更新学习率        
    	train_acc, _, _ = tt(tensor_train_mask) # 计算当前模型训练集上的准确率        
    	val_acc,_,_ = tt(tensor_val_mask)        # 记录训练过程中损失值和准确率的变化,用于画图        
    	loss_history.append(loss.item())        
    	val_acc_history.append(val_acc.item())
    	print("Epoch {:03d}: Loss {:.4f}, TrainAcc {:.4}, ValAcc {:.4f}".format(epoch, loss.item(), train_acc.item(), val_acc.item()))

def tt(mask):	
	model.eval()   
	with torch.no_grad():
		logits = model(tensor_adjacency,tensor_x)       
		test_mask_logits = logits[mask]        
           	predict_y = test_mask_logits.max(1)[1]
            	accuarcy = torch.eq(predict_y,tensor_y[mask]).float.mean()
            
	return accuarcy,test_mask_logits.cpu().numpy(),tensor_y[mask].cpu().numpy()
loss,val_acc = train()
print(tensor_test_mask)
test_acc,test_logits,test_label = tt(tensor_test_mask)
print("Test accuarcy: ", test_acc.item())

实验结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值