meshgraph-gat-lab / model.py
ARotting's picture
Publish Interactive enterprise neighbor-attention inspector
c3425d3 verified
Raw
History Blame Contribute Delete
2.33 kB
from __future__ import annotations
import torch
from torch import nn
from torch.nn import functional as F
class DenseGraphAttentionLayer(nn.Module):
def __init__(self, input_dimensions: int, output_dimensions: int, heads: int) -> None:
super().__init__()
self.output_dimensions = output_dimensions
self.heads = heads
self.weight = nn.Parameter(
torch.randn(heads, input_dimensions, output_dimensions) * 0.1
)
self.source_attention = nn.Parameter(torch.randn(heads, output_dimensions) * 0.1)
self.target_attention = nn.Parameter(torch.randn(heads, output_dimensions) * 0.1)
def forward(
self,
features: torch.Tensor,
adjacency: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
projected = torch.einsum("ni,hio->hno", features, self.weight)
source = torch.einsum("hno,ho->hn", projected, self.source_attention)
target = torch.einsum("hno,ho->hn", projected, self.target_attention)
scores = F.leaky_relu(source[:, :, None] + target[:, None, :], 0.2)
mask = adjacency.bool() | torch.eye(len(adjacency), device=adjacency.device).bool()
scores = scores.masked_fill(~mask[None], -torch.inf)
attention = torch.softmax(scores, dim=2)
output = torch.einsum("hij,hjo->hio", attention, projected)
return output.permute(1, 0, 2).reshape(len(features), -1), attention
class MeshGraphGAT(nn.Module):
def __init__(self, input_dimensions: int = 8) -> None:
super().__init__()
self.first = DenseGraphAttentionLayer(input_dimensions, 16, heads=4)
self.second = DenseGraphAttentionLayer(64, 2, heads=1)
def forward(
self,
features: torch.Tensor,
adjacency: torch.Tensor,
*,
return_attention: bool = False,
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
hidden, first_attention = self.first(features, adjacency)
hidden = F.elu(hidden)
hidden = F.dropout(hidden, p=0.15, training=self.training)
logits, _ = self.second(hidden, adjacency)
if return_attention:
return logits, first_attention
return logits
def parameter_count(model: nn.Module) -> int:
return sum(parameter.numel() for parameter in model.parameters())