GCN model produces the same prediction for all batches in each epoch

I am using this GCN model to classify a list of graphs into 5 families.

import torch
from torch.nn import Linear
from torch_geometric.nn import GCNConv, global_mean_pool
import torch.nn.functional as F

class GCN(torch.nn.Module):
def init(self, num_node_features, hidden_channels, num_classes):
super(GCN, self).init()
torch.manual_seed(12345)
self.conv1 = GCNConv(num_node_features, hidden_channels)
self.conv2 = GCNConv(hidden_channels, hidden_channels)
self.conv3 = GCNConv(hidden_channels, hidden_channels)
self.lin = Linear(hidden_channels, num_classes)

def forward(self, x, edge_index, batch):
    # 1. Obtain node embeddings 
    x = self.conv1(x, edge_index)
    x = x.relu()
    x = self.conv2(x, edge_index)
    x = x.relu()
    x = self.conv3(x, edge_index)

    # 2. Readout layer
    x = global_mean_pool(x, batch)  # [batch_size, hidden_channels]

    # 3. Apply a final classifier
    x = F.dropout(x, p=0.5, training=self.training)
    x = self.lin(x)
    
    return x

model = GCN(num_node_features=1, hidden_channels=64, num_classes=5)
from collections import Counter
from random import choice

Count the number of instances in each class

counter = Counter([data.y.item() for data in data_list])

Get the maximum number of instances in a single class

max_count = max(counter.values())

Create a dictionary to hold instances for each class

class_dict = {}
for i in range(max(counter.keys()) + 1):
class_dict[i] = [data for data in data_list if data.y.item() == i]

Create a list to hold the oversampled data

oversampled_data_list = []

for class_key, data_instances in class_dict.items():
oversample_count = max_count - len(data_instances)
oversampled_data = data_instances + [choice(data_instances) for _ in range(oversample_count)]
oversampled_data_list.extend(oversampled_data)

Shuffle the oversampled data

random.shuffle(oversampled_data_list)

Import necessary modules

from sklearn.model_selection import train_test_split
from torch_geometric.data import DataLoader
from torch.optim import Adam
from torch.utils.data.sampler import WeightedRandomSampler
import torch
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
#model = GAE(GATEncoder(in_channels, out_channels)).to(device)
model=GCN(hidden_channels=3,out_channels=10)

Create a DataLoader for your data_list

loader = DataLoader(data_list, batch_size=5, shuffle=True)
import torch
from torch.utils.data import random_split

torch.manual_seed(12345)
total_length = len(oversampled_data_list)
train_length = int(total_length*0.8)
test_length = total_length - train_length

train_dataset, test_dataset = random_split(oversampled_data_list, [train_length, test_length])

train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=10, shuffle=False)

optimizer = torch.optim.Adam(model.parameters(), lr=0.5)
criterion = torch.nn.CrossEntropyLoss()

def train():
model.train()
for data in train_loader: # Iterate in batches over the training dataset.
optimizer.zero_grad() # Clear gradients.
out = model(data.x, data.edge_index, data.batch) # Perform a single forward pass.
loss = criterion(out, data.y) # Compute the loss.
loss.backward() # Derive gradients.
optimizer.step() # Update parameters based on gradients.

def test(loader):
model.eval()
correct = 0
with torch.no_grad():
for data in loader: # Iterate in batches over the training/test dataset.
out = model(data.x, data.edge_index, data.batch)
pred = out.argmax(dim=1) # Use the class with highest probability.
print(pred)
correct += int((pred == data.y).sum()) # Check against ground-truth labels.
return correct / len(loader.dataset) # Derive ratio of correct predictions.

for epoch in range(1, 171):
train()
train_acc = test(train_loader)
test_acc = test(test_loader)
print(f’Epoch: {epoch:03d}, Train Acc: {train_acc:.4f}, Test Acc: {test_acc:.4f}')

tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
tensor([1, 1, 1, 1, 1, 1])
Epoch: 001, Train Acc: 0.2054, Test Acc: 0.1786
tensor([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
tensor([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
tensor([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
tensor([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
tensor([3, 3, 3, 3, 3, 3, 3, 3, 3, 3]

It does the same even after 30 epochs, just it changes which family.

How can I make it work? Thanks a lot!