Вопрос или проблема
Я попытался создать класс GNN, который может использовать мои признаки узлов вместе с признаками ребер графа. Я реализовал NNConv для использования признаков ребер, но не могу понять, что идет не так. В целом, набор данных имеет много графов с разным количеством узлов и ребер. Но одни и те же размеры атрибутов узлов (5) и атрибутов ребер (3). Однако размер edge_index варьируется, так как количество ребер меняется в разных графах. В моем коде происходят следующие 2 случая ошибки. Может кто-нибудь предложить нужные изменения.
Случай(a)
x torch.Size([39, 5])
edge_attr torch.Size([44, 3])
edge_index torch.Size([2, 88])
batch_index torch.Size([39])
Для пакета: DataBatch(x=[39, 5], edge_index=[2, 88], edge_attr=[44, 3], y=[1], smiles=[1], batch=[39], ptr=[2])
Код:
model = GNN(test[0].x.shape[1], test[0].edge_attr.shape[1], test[0].edge_index)
model = model.to(device)
pred = model(batch.x.float(), batch.edge_attr.float(), batch.edge_index, batch.batch)
Сообщение об ошибке:
44 x = self.conv1(x, edge_index, edge_attr)
1501 return forward_call(*args, **kwargs)
102 out = self.propagate(edge_index, x=x, edge_attr=edge_attr, size=size)
469 out = self.message(**msg_kwargs)
115 weight = weight.view(-1, self.in_channels_l, self.out_channels)
Ошибка:
1. RuntimeError: shape ‘[-1, 5, 32]’ is invalid for input of size 1408
Случай(b)
x torch.Size([24, 5])
edge_attr torch.Size([25, 3])
edge_index torch.Size([2, 50])
batch_index torch.Size([24])
Для пакета я настроил только для 1 графа; таким образом, мой пакет: DataBatch(x=[24, 5], edge_index=[2, 50], edge_attr=[25, 3], y=[1], smiles=[1], batch=[24], ptr=[2])
Ошибка возникает, когда я запускаю следующую строку кода:
Код:
model = GNN(test[0].x.shape[1], test[0].edge_attr.shape[1], test[0].edge_index)
model = model.to(device)
pred = model(batch.x.float(), batch.edge_attr.float(), batch.edge_index, batch.batch)
Ошибка:
RuntimeError: The size of tensor a (50) must match the size of tensor b (5) at non-singleton dimension 0
Код класса GNN:
class GNN(torch.nn.Module):
def __init__(self, feature_size,edge_feat,edge_index):
super(GNN, self).__init__()
num_classes = 2
embedding_size = 32
n_out = 16
nn1 = nn.Sequential(nn.Linear(edge_feat,32), nn.ReLU())
nn2 = nn.Sequential(nn.Linear(edge_feat,32), nn.ReLU())
# Уровни GNN
self.conv1 = NNConv(feature_size, 32, nn1, aggr="mean")
self.pool1 = TopKPooling(32, ratio=0.8)
self.conv2 = NNConv(32, 64, nn2, aggr="mean")
self.pool2 = TopKPooling(32, ratio=0.8)
# Линейные уровни
self.linear1 = Linear(32, 16)
self.linear2 = Linear(16, num_classes)
def forward(self, x, edge_attr, edge_index, batch_index):
# Первый блок
x = self.conv1(x, edge_index, edge_attr)
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)
# Второй блок
x = self.conv2(x, edge_index)
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)
# Конкатенация объединенных векторов
x = x1 + x2
# Блок вывода
x = self.linear1(x).relu()
x = F.dropout(x, p=0.5, training=self.training)
x = self.linear2(x)
return x
Поскольку этот пост появилось 10 месяцев назад, я предполагаю, что вы уже решили эту проблему. Однако для других читателей, я думаю, что проблема кроется в ваших nn1 и nn2, выход этих слоев должен быть в соответствии с количеством входных и выходных каналов согласно реализации NNConv в torch_geometric
NNConv.
Ответ или решение
Чтобы корректно реализовать NNConv в PyTorch, важно учитывать несколько ключевых аспектов, связанных с архитектурой графовой нейронной сети (GNN) и спецификой библиотеки PyTorch Geometric. Ниже будет представлена детализированная инструкция по исправлению проблем в вашем коде и правильной реализации NNConv.
Основные ошибки и их решение
1. Неверная форма выходных данных nn1 и nn2
В процессе использования NNConv в PyTorch Geometric критически важно, чтобы выходные данные ваших последовательностей nn1
и nn2
соответствовали ожиданиям NNConv. Согласно документации от PyTorch Geometric, выходные данные nn1
и nn2
должны иметь форму (num_edges, in_channels * out_channels)
.
В вашем коде вы определили сети nn1
и nn2
следующим образом:
nn1 = nn.Sequential(nn.Linear(edge_feat, 32), nn.ReLU())
nn2 = nn.Sequential(nn.Linear(edge_feat, 32), nn.ReLU())
Однако их выходы не соответствуют требованиям NNConv. Чтобы исправить это, измените последний слой каждой из последовательностей. Например, если вы хотите использовать 32 входных канала и 32 выходных канала, вы можете сделать следующее:
nn1 = nn.Sequential(nn.Linear(edge_feat, 32), nn.ReLU(), nn.Linear(32, 32*32))
nn2 = nn.Sequential(nn.Linear(edge_feat, 32), nn.ReLU(), nn.Linear(32, 32*64))
Это обеспечит, что выходные данные будут соответствовать размерности (num_edges, in_channels * out_channels)
.
2. Ошибки при передаче размеров тензоров
У вас возникают ошибки, связанные с несовпадением размеров тензоров. Например, ошибка RuntimeError: shape ‘[-1, 5, 32]’ is invalid for input of size 1408
говорит о том, что в процессе выполнения операции reshaping ожидаются данные другой формы.
Убедитесь, что во всех шагах передачи данных в нейронные сети используется правильная размерность. Проверьте, что входы x
и edge_attr
правильно передаются через все слои и при необходимости пересчитывайте их размерности.
3. Корректная обработка пакетов графов
Работа с батчами графов требует аккуратной обработки индексов. Ваша функция forward
должна правильно обрабатывать batch_index
, чтобы делающие обработку значения из разных графов не перепутались.
Пример исправленного кода
Ниже приведён пример исправленного кода для класса GNN с учетом вышеизложенных рекомендаций:
class GNN(torch.nn.Module):
def __init__(self, feature_size, edge_feat):
super(GNN, self).__init__()
num_classes = 2
self.embedding_size1 = 32
self.embedding_size2 = 64
nn1 = nn.Sequential(nn.Linear(edge_feat, 32), nn.ReLU(), nn.Linear(32, self.embedding_size1 * self.embedding_size2))
nn2 = nn.Sequential(nn.Linear(edge_feat, 32), nn.ReLU(), nn.Linear(32, self.embedding_size2 * num_classes))
self.conv1 = NNConv(feature_size, self.embedding_size1, nn1, aggr="mean")
self.pool1 = TopKPooling(self.embedding_size1, ratio=0.8)
self.conv2 = NNConv(self.embedding_size1, self.embedding_size2, nn2, aggr="mean")
self.pool2 = TopKPooling(self.embedding_size2, ratio=0.8)
self.linear1 = Linear(self.embedding_size1 + self.embedding_size2, 16)
self.linear2 = Linear(16, num_classes)
def forward(self, x, edge_attr, edge_index, batch_index):
x = self.conv1(x, edge_index, edge_attr)
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)
x = self.conv2(x, edge_index)
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)
x = torch.cat([x1, x2], dim=1) # Конкатенация pooled векторов
x = self.linear1(x).relu()
x = F.dropout(x, p=0.5, training=self.training)
x = self.linear2(x)
return x
Заключение
Эти корректировки должны помочь устранить ошибки при выполнении и позволить вам успешно использовать NNConv в построении графовых нейронных сетей. Следуйте указаниям выше, чтобы адаптировать вашу архитектуру и учитывать размерности тензоров в PyTorch. Успехов в разработке вашего проекта!