Dear all, i intend to replicated the example by using GCN model for IMDB-Binary. however, i am not able to print the accuracy and epoch loop at the end, some guidance pls
Loading data:
import argparse
import torch
import torch.nn.functional as F
from torch.nn import Linear as Lin
from torch_geometric.nn import XConv, fps, global_mean_pool
from points.datasets import get_dataset
from points.train_eval import run
parser = argparse.ArgumentParser()
parser.add_argument('--epochs', type=int, default=200)
parser.add_argument('--batch_size', type=int, default=32)
parser.add_argument('--lr', type=float, default=0.001)
parser.add_argument('--lr_decay_factor', type=float, default=0.5)
parser.add_argument('--lr_decay_step_size', type=int, default=50)
parser.add_argument('--weight_decay', type=float, default=0)
args = parser.parse_args()
class Net(torch.nn.Module):
def __init__(self, num_classes):
super(Net, self).__init__()
self.conv1 = XConv(0, 48, dim=3, kernel_size=8, hidden_channels=32)
self.conv2 = XConv(
48, 96, dim=3, kernel_size=12, hidden_channels=64, dilation=2)
self.conv3 = XConv(
96, 192, dim=3, kernel_size=16, hidden_channels=128, dilation=2)
self.conv4 = XConv(
192, 384, dim=3, kernel_size=16, hidden_channels=256, dilation=2)
self.lin1 = Lin(384, 256)
self.lin2 = Lin(256, 128)
self.lin3 = Lin(128, num_classes)
def forward(self, pos, batch):
x = F.relu(self.conv1(None, pos, batch))
idx = fps(pos, batch, ratio=0.375)
x, pos, batch = x[idx], pos[idx], batch[idx]
x = F.relu(self.conv2(x, pos, batch))
idx = fps(pos, batch, ratio=0.334)
x, pos, batch = x[idx], pos[idx], batch[idx]
x = F.relu(self.conv3(x, pos, batch))
x = F.relu(self.conv4(x, pos, batch))
x = global_mean_pool(x, batch)
x = F.relu(self.lin1(x))
x = F.relu(self.lin2(x))
x = F.dropout(x, p=0.5, training=self.training)
x = self.lin3(x)
return F.log_softmax(x, dim=-1)
train_dataset, test_dataset = get_dataset(num_points=1024)
model = Net(train_dataset.num_classes)
run(train_dataset, test_dataset, model, args.epochs, args.batch_size, args.lr,
args.lr_decay_factor, args.lr_decay_step_size, args.weight_decay)
GCN:
import torch
import torch.nn.functional as F
from torch.nn import Linear
from torch_geometric.nn import GCNConv, global_mean_pool, JumpingKnowledge
class GCN(torch.nn.Module):
def __init__(self, dataset, num_layers, hidden):
super(GCN, self).__init__()
self.conv1 = GCNConv(dataset.num_features, hidden)
self.convs = torch.nn.ModuleList()
for i in range(num_layers - 1):
self.convs.append(GCNConv(hidden, hidden))
self.lin1 = Linear(hidden, hidden)
self.lin2 = Linear(hidden, dataset.num_classes)
def reset_parameters(self):
self.conv1.reset_parameters()
for conv in self.convs:
conv.reset_parameters()
self.lin1.reset_parameters()
self.lin2.reset_parameters()
def forward(self, data):
x, edge_index, batch = data.x, data.edge_index, data.batch
x = F.relu(self.conv1(x, edge_index))
for conv in self.convs:
x = F.relu(conv(x, edge_index))
x = global_mean_pool(x, batch)
x = F.relu(self.lin1(x))
x = F.dropout(x, p=0.5, training=self.training)
x = self.lin2(x)
return F.log_softmax(x, dim=-1)
def __repr__(self):
return self.__class__.__name__
class GCNWithJK(torch.nn.Module):
def __init__(self, dataset, num_layers, hidden, mode='cat'):
super(GCNWithJK, self).__init__()
self.conv1 = GCNConv(dataset.num_features, hidden)
self.convs = torch.nn.ModuleList()
for i in range(num_layers - 1):
self.convs.append(GCNConv(hidden, hidden))
self.jump = JumpingKnowledge(mode)
if mode == 'cat':
self.lin1 = Linear(num_layers * hidden, hidden)
else:
self.lin1 = Linear(hidden, hidden)
self.lin2 = Linear(hidden, dataset.num_classes)
def reset_parameters(self):
self.conv1.reset_parameters()
for conv in self.convs:
conv.reset_parameters()
self.jump.reset_parameters()
self.lin1.reset_parameters()
self.lin2.reset_parameters()
def forward(self, data):
x, edge_index, batch = data.x, data.edge_index, data.batch
x = F.relu(self.conv1(x, edge_index))
xs = [x]
for conv in self.convs:
x = F.relu(conv(x, edge_index))
xs += [x]
x = self.jump(xs)
x = global_mean_pool(x, batch)
x = F.relu(self.lin1(x))
x = F.dropout(x, p=0.5, training=self.training)
x = self.lin2(x)
return F.log_softmax(x, dim=-1)
def __repr__(self):
return self.__class__.__name__
Validation:
import time
import torch
import torch.nn.functional as F
from torch import tensor
from torch.optim import Adam
from sklearn.model_selection import StratifiedKFold
from torch_geometric.data import DataLoader, DenseDataLoader as DenseLoader
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
def cross_validation_with_val_set(dataset, model, folds, epochs, batch_size,
lr, lr_decay_factor, lr_decay_step_size,
weight_decay, logger=None):
val_losses, accs, durations = [], [], []
for fold, (train_idx, test_idx,
val_idx) in enumerate(zip(*k_fold(dataset, folds))):
train_dataset = dataset[train_idx]
test_dataset = dataset[test_idx]
val_dataset = dataset[val_idx]
if 'adj' in train_dataset[0]:
train_loader = DenseLoader(train_dataset, batch_size, shuffle=True)
val_loader = DenseLoader(val_dataset, batch_size, shuffle=False)
test_loader = DenseLoader(test_dataset, batch_size, shuffle=False)
else:
train_loader = DataLoader(train_dataset, batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size, shuffle=False)
test_loader = DataLoader(test_dataset, batch_size, shuffle=False)
model.to(device).reset_parameters()
optimizer = Adam(model.parameters(), lr=lr, weight_decay=weight_decay)
if torch.cuda.is_available():
torch.cuda.synchronize()
t_start = time.perf_counter()
for epoch in range(1, epochs + 1):
train_loss = train(model, optimizer, train_loader)
val_losses.append(eval_loss(model, val_loader))
accs.append(eval_acc(model, test_loader))
eval_info = {
'fold': fold,
'epoch': epoch,
'train_loss': train_loss,
'val_loss': val_losses[-1],
'test_acc': accs[-1],
}
if logger is not None:
logger(eval_info)
if epoch % lr_decay_step_size == 0:
for param_group in optimizer.param_groups:
param_group['lr'] = lr_decay_factor * param_group['lr']
if torch.cuda.is_available():
torch.cuda.synchronize()
t_end = time.perf_counter()
durations.append(t_end - t_start)
loss, acc, duration = tensor(val_losses), tensor(accs), tensor(durations)
loss, acc = loss.view(folds, epochs), acc.view(folds, epochs)
loss, argmin = loss.min(dim=1)
acc = acc[torch.arange(folds, dtype=torch.long), argmin]
loss_mean = loss.mean().item()
acc_mean = acc.mean().item()
acc_std = acc.std().item()
duration_mean = duration.mean().item()
print('Val Loss: {:.4f}, Test Accuracy: {:.3f} ± {:.3f}, Duration: {:.3f}'.
format(loss_mean, acc_mean, acc_std, duration_mean))
return loss_mean, acc_mean, acc_std
def k_fold(dataset, folds):
skf = StratifiedKFold(folds, shuffle=True, random_state=12345)
test_indices, train_indices = [], []
for _, idx in skf.split(torch.zeros(len(dataset)), dataset.data.y):
test_indices.append(torch.from_numpy(idx))
val_indices = [test_indices[i - 1] for i in range(folds)]
for i in range(folds):
train_mask = torch.ones(len(dataset), dtype=torch.bool)
train_mask[test_indices[i]] = 0
train_mask[val_indices[i]] = 0
train_indices.append(train_mask.nonzero().view(-1))
return train_indices, test_indices, val_indices
def num_graphs(data):
if data.batch is not None:
return data.num_graphs
else:
return data.x.size(0)
def train(model, optimizer, loader):
model.train()
total_loss = 0
for data in loader:
optimizer.zero_grad()
data = data.to(device)
out = model(data)
loss = F.nll_loss(out, data.y.view(-1))
loss.backward()
total_loss += loss.item() * num_graphs(data)
optimizer.step()
return total_loss / len(loader.dataset)
def eval_acc(model, loader):
model.eval()
correct = 0
for data in loader:
data = data.to(device)
with torch.no_grad():
pred = model(data).max(1)[1]
correct += pred.eq(data.y.view(-1)).sum().item()
return correct / len(loader.dataset)
def eval_loss(model, loader):
model.eval()
loss = 0
for data in loader:
data = data.to(device)
with torch.no_grad():
out = model(data)
loss += F.nll_loss(out, data.y.view(-1), reduction='sum').item()
return loss / len(loader.dataset)