我根据自己的数据集上的在线教程开发了一个 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]]
我不知道为什么。有任何想法吗?