0

我根据自己的数据集上的在线教程开发了一个 GCN 模型,以进行图级预测。我的数据集中有 293 个图,这是数据集中第一个图的示例:

Data(x=[75, 4], edge_index=[2, 346], edge_attr=[346], y=[1], pos=[75, 2])

只有两个标签,1或0。模型定义如下:

import torch
import torch.nn.functional as F
from torch.nn import Sequential, Linear, BatchNorm1d, ReLU
from torch_geometric.nn import TransformerConv, GATConv, TopKPooling, BatchNorm
from torch_geometric.nn import global_mean_pool as gap, global_max_pool as gmp
from torch_geometric.nn.conv.x_conv import XConv
torch.manual_seed(12345)


class GNN(torch.nn.Module):
    def __init__(self, feature_size):
        super(GNN, self).__init__()
        num_classes = 2
        embedding_size = 256
        
        # GNN layers
        self.conv1 = GATConv(feature_size, embedding_size, heads = 3, dropout =0.3)
        self.head_transform1 = Linear(embedding_size*3, embedding_size)
        self.pool1 = TopKPooling(embedding_size, ratio=0.8)
        self.conv2 = GATConv(embedding_size, embedding_size, heads = 3, dropout =0.3)
        self.head_transform2 = Linear(embedding_size*3, embedding_size)
        self.pool2 = TopKPooling(embedding_size, ratio=0.5)
        self.conv3 = GATConv(embedding_size, embedding_size, heads = 3, dropout =0.3)
        self.head_transform3 = Linear(embedding_size*3, embedding_size)
        self.pool3 = TopKPooling(embedding_size, ratio=0.2)
        

        # Linear layers
        self.linear1 = Linear(embedding_size*2, 256)
        self.linear2 = Linear(256, num_classes)

    def forward(self, x, edge_attr, edge_index, batch_index):
        
        # 1. Obtain node embeddings 
        x = self.conv1(x, edge_index)
        x = self.head_transform1(x)

        x, edge_index, edge_attr, batch_index, _, _=self.pool1(x,
                                                               edge_index,
                                                               None,
                                                               batch_index)
        x1 = torch.cat([gmp(x, batch_index), gap(x, batch_index)], dim=1)

        # second block
        x = self.conv2(x, edge_index)
        x = self.head_transform2(x)
        x, edge_index, edge_attr, batch_index, _, _=self.pool2(x,
                                                               edge_index,
                                                               None,
                                                               batch_index)
        x2 = torch.cat([gmp(x, batch_index), gap(x, batch_index)], dim=1)

        # third block
        x = self.conv3(x, edge_index)
        x = self.head_transform3(x)
        x, edge_index, edge_attr, batch_index, _, _=self.pool3(x,
                                                               edge_index,
                                                               None,
                                                               batch_index)
        x3 = torch.cat([gmp(x, batch_index), gap(x, batch_index)], dim=1)


        # Concat pooled vectors
        x = x1 + x2 + x3

        # Output block
        x = self.linear1(x).relu()
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.linear2(x)

        return(x)
        

#model = GCN(hidden_channels=2)
#print(model)

这就是我训练和测试模型的方式

from tqdm import tqdm
from sklearn.metrics import confusion_matrix, f1_score, accuracy_score, \
     precision_score, recall_score, roc_auc_score

from torch_geometric.loader import DataLoader

device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")

def count_parameters(model):
  return sum(p.numel() for p in model.parameters() if p.requires_grad)

torch.manual_seed(12345)
dataset.shuffle()
train_dataset = dataset[:210]
test_dataset = dataset[210:]

#%% loading the model
model = GNN(feature_size=train_dataset[0].x.shape[1])
model = model.to(device)
print(f"number of parameters: {count_parameters(model)}")
print(model)

#%% Loss and Optimizer
weights = torch.tensor([1,1], dtype=torch.float32).to(device)
loss_fn = torch.nn.CrossEntropyLoss(weight=weights)
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)

#%% Prepare training
NUM_GRAPHS_PER_BATCH = 16
train_loader = DataLoader(train_dataset, batch_size=NUM_GRAPHS_PER_BATCH, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=NUM_GRAPHS_PER_BATCH, shuffle=True)

def train(epoch):

  all_preds = []
  all_labels = []

  for _, batch in enumerate(tqdm(train_loader)):
    # USE GPU
    batch.to(device)
    optimizer.zero_grad()
    pred = model(batch.x.float(),
                 batch.edge_attr.float(),
                 batch.edge_index,
                 batch.batch)
    # Calculating the loss and gradients
    loss = torch.sqrt(loss_fn(pred, batch.y))
    loss.backward()
    optimizer.step()

    all_preds.append(np.argmax(pred.cpu().detach().numpy(), axis=1))
    all_labels.append(batch.y.cpu().detach().numpy())
  all_preds = np.concatenate(all_preds).ravel()
  all_labels = np.concatenate(all_labels).ravel()
  calculate_metrics(all_preds, all_labels, epoch, "train")
  return loss

def test(epoch):
  all_preds = []
  all_labels = []

  for batch in test_loader:
    # USE GPU
    batch.to(device)
    pred = model(batch.x.float(),
                 batch.edge_attr.float(),
                 batch.edge_index,
                 batch.batch)
    # Calculating the loss and gradients
    loss = torch.sqrt(loss_fn(pred, batch.y))
    all_preds.append(np.argmax(pred.cpu().detach().numpy(), axis=1))
    all_labels.append(batch.y.cpu().detach().numpy())

  all_preds = np.concatenate(all_preds).ravel()
  all_labels = np.concatenate(all_labels).ravel()
  calculate_metrics(all_preds, all_labels, epoch, "test")
  return loss

def calculate_metrics(y_pred, y_true, epoch, type):
  print(f"\n Confusion matrix: \n {confusion_matrix(y_pred, y_true)}")
  print(f"F1 score: {f1_score(y_pred, y_true)}")
  print(f"Accuracy: {accuracy_score(y_pred, y_true)}")
  print(f"Precision: {precision_score(y_pred, y_true)}")
  print(f"Recall: {recall_score(y_pred, y_true)}")

  try:
    roc = roc_auc_score(y_pred, y_true)
    print(f"ROC AUC: {roc}")
    mlflow.log_metric(key=f"ROC - AUC -{type}", value=float(roc), step=epoch)
  except:
    mlflow.log_metric(key=f"ROC - AUC -{type}", value=float(0), step=epoch)
    print(f"ROC AUC: notdefined")

这就是我运行模型的方式:

import mlflow.pytorch

with mlflow.start_run() as run:
  for epoch in range(500):

    # Training
    model.train()
    loss = train(epoch=epoch)
    print(f"Epoch {epoch} | Train Loss {loss}")
    mlflow.log_metric(key="Train loss", value=float(loss), step=epoch)

    # Testing
    model.eval()
    if epoch % 5 == 0:
      loss = test(epoch=epoch)
      loss = loss.detach().cpu().numpy()
      print(f"Epoch {epoch} | Test loss {loss}")
      mlflow.log_metric(key="Test loss", value=float(loss), step=epoch)

    scheduler.step()
  print("Done.")

这些模型可以正常工作而没有错误,但是,问题在于混淆矩阵表明该模型仅预测一个标签。对于每个时代,我得到完全相同的混淆矩阵:

[[ 86 124]
 [  0   0]]

我不知道为什么。有任何想法吗?

4

0 回答 0