❓ Questions & Help
Hello, thanks for this useful tool.
I am working on a binary graph classification problem with 4 raw files.
I created my dataset like the tu-dataset:
AGGF1_A.txt:
1,2
2,1
1,3
3,1
4,5
5,4
...
AGGF1_graph_indicator.txt:
1
1
1
1
2
2
...
AGGF1_graph_labels.txt:
0
1
0
1
0
0
0
0
...
AGGF1_node_attributes.txt:
1 0 0 0 1.0 0.0000 0.0000 0.0000 0.0000 0 1 0.000 -1.128
0 0 1 0 1.0 0.0000 0.0000 0.0000 0.0000 0 1 0.003 -0.018
0 0 0 1 0.6461 0.3539 0.0000 0.0000 0.0000 0 1 0.008 0.262
0 1 0 0 0.8007 0.1993 0.0000 0.0000 0.0000 0 1 0.012 0.555
0 1 0 0 0.8431 0.1523 0.0019 0.0012 0.0015 0 1 0.003 -0.393
0 0 0 1 0.8395 0.1491 0.0088 0.0010 0.0016 0 1 0.002 -0.442
...
Here is the code to create the dataset:
import os, glob
import os.path as osp
import numpy as np
import torch
import torch.nn.functional as F
from torch_sparse import coalesce
from torch_geometric.data import InMemoryDataset
from torch_geometric.io import read_txt_array
from torch_geometric.utils import remove_self_loops
from torch_geometric.data import Data
def cat(seq):
seq = [item for item in seq if item is not None]
seq = [item.unsqueeze(-1) if item.dim() == 1 else item for item in seq]
return torch.cat(seq, dim=-1) if len(seq) > 0 else None
def read_file(folder, prefix, name, dtype=None):
path = osp.join(folder, '{}_{}.txt'.format(prefix, name))
return read_txt_array(path, sep='\t', dtype=dtype)
def split(data, batch):
node_slice = torch.cumsum(torch.from_numpy(np.bincount(batch)), 0)
node_slice = torch.cat([torch.tensor([0]), node_slice])
row, _ = data.edge_index
edge_slice = torch.cumsum(torch.from_numpy(np.bincount(batch[row])), 0)
edge_slice = torch.cat([torch.tensor([0]), edge_slice])
data.edge_index -= node_slice[batch[row]].unsqueeze(0)
data.__num_nodes__ = torch.bincount(batch).tolist()
slices = {'edge_index': edge_slice}
if data.x is not None:
slices['x'] = node_slice
if data.edge_attr is not None:
slices['edge_attr'] = edge_slice
if data.y is not None:
if data.y.size(0) == batch.size(0):
slices['y'] = node_slice
else:
slices['y'] = torch.arange(0, batch[-1] + 2, dtype=torch.long)
return data, slices
def read_tu_data(folder, prefix):
files = glob.glob(osp.join(folder, '{}_*.txt'.format(prefix)))
names = [f.split(os.sep)[-1][len(prefix) + 1:-4] for f in files]
edge_index = read_file(folder, prefix, 'A', torch.long).t() - 1
batch = read_file(folder, prefix, 'graph_indicator', torch.long) - 1
node_attributes = node_labels = None
if 'node_attributes' in names:
node_attributes = read_file(folder, prefix, 'node_attributes')
if 'node_labels' in names:
node_labels = read_file(folder, prefix, 'node_labels', torch.long)
if node_labels.dim() == 1:
node_labels = node_labels.unsqueeze(-1)
node_labels = node_labels - node_labels.min(dim=0)[0]
node_labels = node_labels.unbind(dim=-1)
node_labels = [F.one_hot(x, num_classes=-1) for x in node_labels]
node_labels = torch.cat(node_labels, dim=-1).to(torch.float)
x = cat([node_attributes, node_labels])
edge_attributes, edge_labels = None, None
if 'edge_attributes' in names:
edge_attributes = read_file(folder, prefix, 'edge_attributes')
if 'edge_labels' in names:
edge_labels = read_file(folder, prefix, 'edge_labels', torch.long)
if edge_labels.dim() == 1:
edge_labels = edge_labels.unsqueeze(-1)
edge_labels = edge_labels - edge_labels.min(dim=0)[0]
edge_labels = edge_labels.unbind(dim=-1)
edge_labels = [F.one_hot(e, num_classes=-1) for e in edge_labels]
edge_labels = torch.cat(edge_labels, dim=-1).to(torch.float)
edge_attr = cat([edge_attributes, edge_labels])
y = None
if 'graph_attributes' in names: # Regression problem.
y = read_file(folder, prefix, 'graph_attributes')
elif 'graph_labels' in names: # Classification problem.
y = read_file(folder, prefix, 'graph_labels', torch.long)
_, y = y.unique(sorted=True, return_inverse=True)
num_nodes = edge_index.max().item() + 1 if x is None else x.size(0)
edge_index, edge_attr = remove_self_loops(edge_index, edge_attr)
edge_index, edge_attr = coalesce(edge_index, edge_attr, num_nodes,
num_nodes)
data = Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y)
data, slices = split(data, batch)
return data, slices
class MYDataset(InMemoryDataset):
def __init__(self, root, name, transform=None, pre_transform=None,
pre_filter=None, use_node_attr=False, use_edge_attr=False,
cleaned=False):
self.name = name
self.cleaned = cleaned
super(MYDataset, self).__init__(root, transform, pre_transform,
pre_filter)
self.data, self.slices = torch.load(self.processed_paths[0])
if self.data.x is not None and not use_node_attr:
num_node_attributes = self.num_node_attributes
self.data.x = self.data.x[:, num_node_attributes:]
if self.data.edge_attr is not None and not use_edge_attr:
num_edge_attributes = self.num_edge_attributes
self.data.edge_attr = self.data.edge_attr[:, num_edge_attributes:]
@property
def raw_dir(self):
name = 'raw{}'.format('_cleaned' if self.cleaned else '')
return osp.join(self.root, self.name, name)
@property
def processed_dir(self):
name = 'processed{}'.format('_cleaned' if self.cleaned else '')
return osp.join(self.root, self.name, name)
@property
def num_node_labels(self):
if self.data.x is None:
return 0
for i in range(self.data.x.size(1)):
x = self.data.x[:, i:]
if ((x == 0) | (x == 1)).all() and (x.sum(dim=1) == 1).all():
return self.data.x.size(1) - i
return 0
@property
def num_node_attributes(self):
if self.data.x is None:
return 0
return self.data.x.size(1) - self.num_node_labels
@property
def num_edge_labels(self):
if self.data.edge_attr is None:
return 0
for i in range(self.data.edge_attr.size(1)):
if self.data.edge_attr[:, i:].sum() == self.data.edge_attr.size(0):
return self.data.edge_attr.size(1) - i
return 0
@property
def num_edge_attributes(self):
if self.data.edge_attr is None:
return 0
return self.data.edge_attr.size(1) - self.num_edge_labels
@property
def raw_file_names(self):
names = ['A', 'graph_indicator']
return ['{}_{}.txt'.format(self.name, name) for name in names]
@property
def processed_file_names(self):
return 'data.pt'
def process(self):
self.data, self.slices = read_tu_data(self.raw_dir, self.name)
if self.pre_filter is not None:
data_list = [self.get(idx) for idx in range(len(self))]
data_list = [data for data in data_list if self.pre_filter(data)]
self.data, self.slices = self.collate(data_list)
if self.pre_transform is not None:
data_list = [self.get(idx) for idx in range(len(self))]
data_list = [self.pre_transform(data) for data in data_list]
self.data, self.slices = self.collate(data_list)
torch.save((self.data, self.slices), self.processed_paths[0])
def __repr__(self):
return '{}({})'.format(self.name, len(self))
And my model.py:
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
from torch_geometric.nn import global_mean_pool
class Net(torch.nn.Module):
def __init__(self, dataset):
super(Net, self).__init__()
dim = 16
self.conv1 = GCNConv(dataset.num_node_features, dim)
self.conv2 = GCNConv(dim, dim)
self.conv3 = GCNConv(dim, dim)
self.mlp = nn.Linear(dim, dataset.num_classes)
def forward(self, data):
x, edge_index, batch = data.x, data.edge_index, data.batch
x = F.relu(self.conv1(x, edge_index))
x = F.relu(self.conv2(x, edge_index))
x = F.dropout(x, p=0.5, training=self.training)
x = global_mean_pool(x, batch)
x = self.mlp(x)
return F.log_softmax(x, dim=1)
gcn_main.py:
import argparse
import torch
import torch.nn.functional as F
from torch_geometric.data import DataLoader
from dataset import MYDataset
from model import Net
names = [
'A', 'graph_indicator', 'node_labels', 'node_attributes',
'edge_labels', 'edge_attributes', 'graph_labels', 'graph_attributes'
]
def train(model, train_loader, optimizer, device):
model.train()
loss_all, counter = 0, 0
for data in train_loader:
data = data.to(device)
optimizer.zero_grad()
output = model(data)
label = data.y.to(device)
loss = F.nll_loss(output, label)
loss.backward()
loss_all += data.num_graphs * loss.item()
counter += len(data.y)
optimizer.step()
return loss_all / counter
def evaluate(loader, model, device):
model.eval()
correct, counter = 0, 0
for data in loader:
data = data.to(device)
output = model(data).max(dim=1)[1]
correct += output.eq(data.y).sum().item()
counter += len(data.y)
return correct / counter
def main(args):
dataset = MYDataset(root='', name='AGGF1', use_node_attr=True).shuffle()
train_length = int(len(dataset) * 0.9)
train_dataset = dataset[train_length:]
test_dataset = dataset[:train_length]
print("Train Points: %d" % len(train_dataset))
print("Test Points: %d" % len(test_dataset))
train_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=True)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = Net(dataset).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=args.lr, weight_decay=5e-4)
for epoch in range(1, 201):
loss = train(model, train_loader, optimizer, device)
train_acc = evaluate(train_loader, model, device)
test_acc = evaluate(test_loader, model, device)
print(
'Epoch: {:03d}, Loss: {:.5f}, Train Acc: {:.5f}, Test Acc: {:.5f}'.format(epoch, loss, train_acc,
test_acc))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
help='learning rate (default: 0.01)')
args = parser.parse_args()
main(args)
And the output is like:

I have tried different batch_sizes, learning rates, and the layers, but only the convergence rate changes, two accuracies always converge to 1.0000. Even when I only use one fc layer. Does this indicate that the model does not suitable for my data? what are the possible bugs?I need your help, thanks!
❓ Questions & Help
Hello, thanks for this useful tool.
I am working on a binary graph classification problem with 4 raw files.
I created my dataset like the tu-dataset:
Here is the code to create the dataset:
And my model.py:
gcn_main.py:
And the output is like:

I have tried different batch_sizes, learning rates, and the layers, but only the convergence rate changes, two accuracies always converge to 1.0000. Even when I only use one fc layer. Does this indicate that the model does not suitable for my data? what are the possible bugs?I need your help, thanks!