最近,尝试使用Pytorch和Pytorch Geometric(PyG),构建图神经网络架构,根据论文《The Emerging Field of Signal Processing on Graphs》中的算法,用PyG重现实验,出现错误及程序源代码如下,请求指导,谢谢!(已解决)
RuntimeError Traceback (most recent call last)
<ipython-input-14-14adeb5271eb> in <module>
2 best_val_acc = test_acc = 0
3 for epoch in range(1, 201):
----> 4 train()
5 train_acc, val_acc, tmp_test_acc = test()
6 if val_acc > best_val_acc:
<ipython-input-12-f9f6ce4bd81a> in train()
5 optimizer.zero_grad()
6 # 将误差反向传播
----> 7 F.nll_loss(model()[data.train_mask], data.y[data.train_mask]).backward()
8 # 更新参数
9 optimizer.step()
D:\Anaconda3\lib\site-packages\torch\nn\modules\module.py in __call__(self, *input, **kwargs)
539 result = self._slow_forward(*input, **kwargs)
540 else:
--> 541 result = self.forward(*input, **kwargs)
542 for hook in self._forward_hooks.values():
543 hook_result = hook(self, input, result)
<ipython-input-10-a5f12f83ff5c> in forward(self)
10 x, edge_index = data.x, data.edge_index
11
---> 12 x = self.conv1(x, edge_index)
13 x = F.relu(x)
14 x = F.dropout(x, training=self.training)
D:\Anaconda3\lib\site-packages\torch\nn\modules\module.py in __call__(self, *input, **kwargs)
539 result = self._slow_forward(*input, **kwargs)
540 else:
--> 541 result = self.forward(*input, **kwargs)
542 for hook in self._forward_hooks.values():
543 hook_result = hook(self, input, result)
<ipython-input-9-e745cda6afb6> in forward(self, x, edge_index)
33
34 # Step 3-5: Start propagating messages.
---> 35 return self.propagate(edge_index, size=(x.size(0), x.size(0)), x=x)
36
37 def message(self, x_j, edge_index, size):
D:\Anaconda3\lib\site-packages\torch_geometric\nn\conv\message_passing.py in propagate(self, edge_index, size, dim, **kwargs)
124 update_args = [kwargs[arg] for arg in self.__update_args__]
125
--> 126 out = self.message(*message_args)
127 out = scatter_(self.aggr, out, edge_index[i], dim, dim_size=size[i])
128 out = self.update(out, *update_args)
<ipython-input-9-e745cda6afb6> in message(self, x_j, edge_index, size)
82 #print("norm.shape=",norm.shape)
83
---> 84 return norm.view(-1, 1) * x_j
85
86 def update(self, aggr_out):
RuntimeError: expected device cpu but got device cuda:0
import torch
from torch_geometric.nn import MessagePassing
from torch_geometric.utils import add_self_loops, degree
import torch.nn.functional as F
import scipy.sparse as sp
import numpy as np
import scipy.sparse
import networkx as nx
import math
##############################处理数据集#############################
import os
import os.path as osp
from torch_geometric.datasets import Planetoid
import torch_geometric.transforms as T
dataset = 'Cora'
path = osp.join(osp.dirname(osp.realpath('__file__')), 'data', dataset)
##加载数据集
dataset = Planetoid(path, dataset, T.NormalizeFeatures())
data = dataset[0]
##############################定义超参数#############################
#threshold小波基的阈值,小于阈值置为:0
threshold = 1e-4
#小波尺度:s
s=1.0
##############################由邻接W计算正则化的拉普拉斯矩阵#############################
def laplacian(W):
"""Return the Laplacian of the weight matrix."""
# Degree matrix.
d = W.sum(axis=0)
# Laplacian matrix.
# d += np.spacing(np.array(0, W.dtype))
d = d.pow(-0.5)
##squeeze()去掉一个维度
D = torch.diag(d, 0)
I = torch.eye(2708)
L = I -torch.mm(torch.mm(D,W),D)
return L
##############################处特征分解#############################
def fourier(L):
"""Return the Fourier basis, i.e. the EVD of the Laplacian."""
# print "eigen decomposition:"
#torch.symeig函数:计算特征值和特征向量(eigenvectors = True)
lamb, U= torch.symeig(L,eigenvectors = True,upper = True)
return lamb, U
##############################堆叠小波变换基和逆小波变换基#############################
def wavelet_basis(adj,s=1.0,threshold=1e-4):
###laplacian来自文件weighting_func.py
L = laplacian(adj)
#得到特征值和特征向量矩阵
lamb, U = fourier(L)
#小波变换UG-sUT
Weight = weight_wavelet(s,lamb,U)
#小波逆变换UGsUT
inverse_Weight = weight_wavelet_inverse(s,lamb,U)
del U,lamb
#将小变换换和你小波变换基中的值小于阈值的置为:0
Weight[Weight < threshold] = 0.0
inverse_Weight[inverse_Weight < threshold] = 0.0
# print len(np.nonzero(Weight)[0])
t_k = [inverse_Weight,Weight]
return t_k####sparse_to_tuple(t_k)
##############################小波变换基#############################
def weight_wavelet(s,lamb,U):
s = s
lamb = torch.exp(-lamb*s)
#UG-sUT
Weight = torch.mm(torch.mm(U,torch.diag(lamb)),torch.t(U))
return Weight
##############################小波变换基#############################
def weight_wavelet_inverse(s,lamb,U):
s = s
lamb = torch.exp(lamb*s)
#UGsUT
Weight = torch.mm(torch.mm(U,torch.diag(lamb)),torch.t(U))
return Weight
################################定义谱图小波卷积层############################
class WaveletConv(MessagePassing):
def __init__(self, in_channels, out_channels):
super(WaveletConv, self).__init__(aggr='add') # "Add" aggregation.
self.lin = torch.nn.Linear(in_channels, out_channels)
#设置可学习参数
self.kernel = torch.nn.Parameter(torch.Tensor(2708), requires_grad=True)
def forward(self, x, edge_index):
# x has shape [N, in_channels=1433]
# edge_index has shape [2, E]
# Step 1: Add self-loops to the adjacency matrix.
## x.size(0)=2708
edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))
##print("x = self.lin(x)***:",x,x.shape)
##2708*1433
# Step 2: Linearly transform node feature matrix.2708*1433
x = self.lin(x)
##2708*16
##print("x = self.lin(x):",x,x.shape)
# Step 3-5: Start propagating messages.
return self.propagate(edge_index, size=(x.size(0), x.size(0)), x=x)
def message(self, x_j, edge_index, size):
# x_j has shape [E, out_channels]
# Step 3: (归一化节点特征)Normalize node features.
row, col = edge_index
#计算每个结点的度
#deg = degree(row, size[0], dtype=x_j.dtype)
#deg_inv_sqrt = deg.pow(-0.5)
#norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]
###############################################
#修改此部分完成谱图小波网络
adj =torch.zeros((2708,2708),dtype=torch.float)
#print(data.edge_index[0].shape)
#torch.Size([10556])
adj[edge_index[0],edge_index[1]]=1
#计算的带小波变换基support[0]和小波逆变换基support[1]
support = wavelet_basis(adj)
self.kernel.data = torch.ones(2708,dtype=torch.float)##(np.ones(2708))
supports = torch.mm(support[0],torch.diag(self.kernel))
supports = torch.mm(supports,support[1])
norm = supports[row, col]
###############################################
#norm:中存放了每条边的归一化权重
##norm:1*10556 * 10556*out_channels = 1*out_channels
#其中参数-1表示剩下的值的个数一起构成一个维度。
#第一个参数1将第一个维度的大小设定成1,
#后一个-1就是说第二个维度的大小=元素总数目/第一个维度的大小
#norm将normal变为10556*1列
#print("message:row=",row.shape)
#print("message:col=",col.shape)
#print("type(norm)=",type(norm))
#print("norm.shape=",norm.shape)
return norm.view(-1, 1) * x_j
def update(self, aggr_out):
# aggr_out has shape [N=2708, out_channels]
# Step 5: Return new node embeddings.
return aggr_out
##############################定义网络模型#############################
class Net(torch.nn.Module):
#torch.nn.Module 是所有神经网络单元的基类
def __init__(self):
super(Net, self).__init__()###复制并使用Net的父类的初始化方法,即先运行nn.Module的初始化函数
self.conv1 = WaveletConv(dataset.num_node_features, 16)
self.conv2 = WaveletConv(16, dataset.num_classes)
def forward(self):
x, edge_index = data.x, data.edge_index
x = self.conv1(x, edge_index)
x = F.relu(x)
x = F.dropout(x, training=self.training)
x = self.conv2(x, edge_index)
return F.log_softmax(x, dim=1)
##############################设置GPU 定义优化器#############################
#device = torch.device('cpu')
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model, data = Net().to(device), data.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
##############################定义训练函数#############################
def train():
model.train()
# 在反向传播之前,先将梯度归0
optimizer.zero_grad()
# 将误差反向传播
F.nll_loss(model()[data.train_mask], data.y[data.train_mask]).backward()
# 更新参数
optimizer.step()
##############################定义测试函数#############################
def test():
model.eval()
logits, accs = model(), []
for _, mask in data('train_mask', 'val_mask', 'test_mask'):
pred = logits[mask].max(1)[1]
acc = pred.eq(data.y[mask]).sum().item() / mask.sum().item()
accs.append(acc)
return accs
##############################训练并测试函数#############################
best_val_acc = test_acc = 0
for epoch in range(1, 201):
train()
train_acc, val_acc, tmp_test_acc = test()
if val_acc > best_val_acc:
best_val_acc = val_acc
test_acc = tmp_test_acc
#打印有哪些参与训练的参数
#for name, param in model.named_parameters():
#if param.requires_grad:
#print(name)
log = 'Epoch: {:03d}, Train: {:.4f}, Val: {:.4f}, Test: {:.4f}'
print(log.format(epoch, train_acc, best_val_acc, test_acc))
补充:程序已正常运行,修改了如下三处,主要原因在于自定义的tensor没有移到GPU上,修改的代码为:
42行: I = torch.eye(2708).cuda()
163行:adj =torch.zeros((2708,2708),dtype=torch.float).cuda()
147行:self.kernel.data = torch.ones(2708,dtype=torch.float).cuda()
还需要继续修改,训练结果不够理想,和论文中给出的结果还有不一致