Skip to main content

pytorch-geometric

Graph Neural Networks (GNN) for learning on graph-structured data. PyTorch Geometric (PyG) extends PyTorch with the MessagePassing framework — the core abstraction for all GNN layers — and provides standard convolutions (GCNConv, GATConv, GraphSAGEConv, GINConv), graph pooling, batching of variable-size graphs, and datasets. Use when: performing node classification (e.g., predicting labels on a citation network), graph classification (e.g., predicting molecular properties), link prediction (e.g., recommending new connections), learning representations on any graph-structured data (social networks, molecules, knowledge graphs, protein structures), implementing custom GNN architectures via the MessagePassing base class, working with heterogeneous graphs (multiple node/edge types), or any task where data has explicit relational structure that CNNs/RNNs cannot capture. Complements networkx (classical graph algorithms) and rdkit (molecular graphs) — PyG adds the deep learning layer on top.

Datos de origen

Repositorio
tondevrel/scientific-agent-skills
Última actividad en el origen
1 de febrero de 2026 a las 04:41
Idioma detectado de SKILL.md
inglés
Estrellas
22
Forks
2

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
pytorch-geometric
description
Graph Neural Networks (GNN) for learning on graph-structured data. PyTorch Geometric (PyG) extends PyTorch with the MessagePassing framework — the core abstraction for all GNN layers — and provides standard convolutions (GCNConv, GATConv, GraphSAGEConv, GINConv), graph pooling, batching of variable-size graphs, and datasets. Use when: performing node classification (e.g., predicting labels on a citation network), graph classification (e.g., predicting molecular properties), link prediction (e.g., recommending new connections), learning representations on any graph-structured data (social networks, molecules, knowledge graphs, protein structures), implementing custom GNN architectures via the MessagePassing base class, working with heterogeneous graphs (multiple node/edge types), or any task where data has explicit relational structure that CNNs/RNNs cannot capture. Complements networkx (classical graph algorithms) and rdkit (molecular graphs) — PyG adds the deep learning layer on top.
# PyTorch Geometric — Graph Neural Networks PyTorch Geometric (PyG) is the standard library for deep learning on graphs. Where networkx handles graph algorithms (shortest path, centrality, community detection), PyG handles **learning on graphs**: training neural networks that operate directly on graph structure. The core insight: a GNN layer aggregates information from a node's neighbors, learns which neighbors matter, and produces new node representations — all differentiable, all trainable. ## Core Mental Model ``` A GRAPH has: • Nodes (vertices) — each has a feature vector • Edges (connections) — each optionally has attributes • Structure — which nodes connect to which A GNN LAYER does (per node): 1. GATHER messages from neighbors 2. AGGREGATE messages (sum / mean / max) 3. UPDATE own representation using aggregated + self Node v: h_v ← UPDATE( h_v, AGGREGATE( MESSAGE(h_u, e_uv) for u ∈ N(v) ) ) After k layers: each node "sees" its k-hop neighborhood. This is how local structure becomes global representation. PyG's DATA OBJECT: x → node feature matrix [num_nodes, num_features] edge_index → edge list (COO format) [2, num_edges] edge_attr → edge feature matrix [num_edges, num_edge_features] (optional) y → labels [num_nodes] or [num_graphs] (optional) ``` ### edge_index — The Key Format ``` Graph: 0 → 1, 0 → 2, 1 → 2 edge_index = tensor([[0, 0, 1], ← source nodes [1, 2, 2]]) ← target nodes Column i describes edge i: source = edge_index[0, i], target = edge_index[1, i] ⚠️ UNDIRECTED graph: store BOTH directions! 0 — 1 becomes 0→1 AND 1→0 → edge_index has 2× the edges Messages flow: source → target (default in MessagePassing) ``` ## Reference Documentation **PyG docs**: https://pytorch-geometric.readthedocs.io/en/latest/ **PyG tutorials**: https://pytorch-geometric.readthedocs.io/en/latest/tutorials.html **GitHub**: https://github.com/pyg-team/pytorch_geometric **Search patterns**: `Data`, `MessagePassing`, `GCNConv`, `global_mean_pool`, `Batch` ## Quick Reference ### Installation ```bash # Install PyTorch first (match your CUDA version) pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # Then PyG — use the install finder at https://pytorch-geometric.readthedocs.io/en/latest/install.html pip install torch-geometric # Core extensions (required for many features): pip install torch-scatter torch-sparse torch-cluster torch-spline-conv ``` ### Standard Imports ```python import torch import torch.nn.functional as F from torch_geometric.data import Data, DataLoader, Batch from torch_geometric.nn import GCNConv, GATConv, GraphSAGEConv, GINConv from torch_geometric.nn import global_mean_pool, global_add_pool from torch_geometric.datasets import Planetoid, TUDataset ``` ### Basic Pattern — Build a Graph, Define a GNN, Train ```python import torch import torch.nn.functional as F from torch_geometric.data import Data from torch_geometric.nn import GCNConv # ─── 1. Build a graph ─── x = torch.tensor([[1.0, 0.0], # Node 0 features [0.0, 1.0], # Node 1 features [1.0, 1.0], # Node 2 features [0.0, 0.0]], # Node 3 features dtype=torch.float) # Edges: 0-1, 1-2, 2-3 (undirected → both directions) edge_index = torch.tensor([[0, 1, 1, 2, 2, 3], [1, 0, 2, 1, 3, 2]], dtype=torch.long) y = torch.tensor([0, 0, 1, 1]) # Node labels (2 classes) data = Data(x=x, edge_index=edge_index, y=y) print(data) # Data(x=[4, 2], edge_index=[2, 6], y=[4]) # ─── 2. Define GNN model ─── class SimpleGCN(torch.nn.Module): def __init__(self, in_features, hidden, out_classes): super().__init__() self.conv1 = GCNConv(in_features, hidden) self.conv2 = GCNConv(hidden, out_classes) def forward(self, x, edge_index): x = F.relu(self.conv1(x, edge_index)) x = F.dropout(x, p=0.5, training=self.training) x = self.conv2(x, edge_index) return x # [num_nodes, out_classes] — logits per node model = SimpleGCN(in_features=2, hidden=16, out_classes=2) out = model(data.x, data.edge_index) # Shape: [4, 2] ``` ## Critical Rules ### ✅ DO - **Make undirected graphs bidirectional in edge_index** — If edge 0→1 exists, include 1→0 too. Use `torch_geometric.utils.to_undirected()` to do this automatically. - **Keep edge_index as `torch.long` (int64)** — Always. Node feature tensors are float, edge_index must be long. - **Use `data.to(device)` to move entire graph** — Moves x, edge_index, edge_attr, y all at once. Don't move tensors individually. - **Use train_mask/val_mask/test_mask for node classification** — Standard transductive split. Masks are boolean tensors of shape [num_nodes]. - **Use DataLoader for graph classification** — It batches multiple graphs into one Batch object. Don't manually concatenate. - **Use `global_mean_pool` or `global_add_pool` before the final classifier in graph-level tasks** — Converts variable-size node matrices to fixed-size graph vectors. - **Add self-loops before GCN layers** — `GCNConv` adds them by default (`add_self_loops=True`). If you disabled them, node features don't propagate to themselves. - **Use `batch` argument in pooling** — `global_mean_pool(x, batch)` — `batch` tells the pooling which nodes belong to which graph in a Batch. ### ❌ DON'T - **Don't confuse edge_index shape** — It's `[2, E]`, NOT `[E, 2]`. Row 0 = sources, row 1 = targets. This is the #1 bug in PyG code. - **Don't use GCN on heterogeneous graphs** — GCNConv assumes homogeneous graphs (one node type, one edge type). Use `HeteroConv` or type-specific layers. - **Don't forget `model.eval()` and `torch.no_grad()` during inference** — Dropout and batch norm behave differently. - **Don't assume edge_index is sorted** — PyG doesn't guarantee edge ordering. Don't index into edge_attr assuming a specific edge order. - **Don't use standard PyTorch DataLoader** — Use `torch_geometric.data.DataLoader` which knows how to batch graphs. - **Don't stack node features across graphs manually** — `Batch.from_data_list()` handles this with correct edge_index offsetting. ## Anti-Patterns (NEVER) ```python import torch from torch_geometric.data import Data # ❌ BAD: edge_index transposed — [E, 2] instead of [2, E] edges = [(0,1), (1,2), (2,3)] edge_index = torch.tensor(edges, dtype=torch.long) # Shape: [3, 2] ← WRONG # GCNConv will silently produce garbage or crash. # ✅ GOOD: Transpose to [2, E] edge_index = torch.tensor(edges, dtype=torch.long).t().contiguous() # Shape: [2, 3] ✓ # ───────────────────────────────────────────────────────────── # ❌ BAD: Directed edges for an undirected graph — messages flow one way only edge_index = torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.long) # Node 3 receives from 2, but node 0 never receives from 1. # GCN on this graph: nodes at the "end" of chains have rich representations, # nodes at the "start" stay at initialization. # ✅ GOOD: Add reverse edges from torch_geometric.utils import to_undirected edge_index = to_undirected(edge_index) # Now: [[0,1,1,2,2,3], [1,0,2,1,3,2]] # ───────────────────────────────────────────────────────────── # ❌ BAD: Moving tensors to different devices separately x = x.to('cuda') edge_index = edge_index.to('cuda') y = y.to('cuda') # Easy to forget one → runtime error # ✅ GOOD: Move the whole Data object data = data.to('cuda') # All attributes moved atomically # ───────────────────────────────────────────────────────────── # ❌ BAD: Standard DataLoader for graph datasets from torch.utils.data import DataLoader as TorchDataLoader loader = TorchDataLoader(dataset, batch_size=32) # Can't batch graphs! # ✅ GOOD: PyG DataLoader from torch_geometric.data import DataLoader loader = DataLoader(dataset, batch_size=32, shuffle=True) # Returns Batch objects — concatenated graphs with correct edge_index offsets ``` ## The Data Object ```python import torch from torch_geometric.data import Data from torch_geometric.utils import to_undirected # ─── Construct from scratch ─── data = Data( x=torch.randn(5, 16), # 5 nodes, 16 features each edge_index=torch.tensor([[0,1,2,3], [1,2,3,4]], dtype=torch.long), edge_attr=torch.randn(4, 8), # 4 edges, 8 features each y=torch.tensor([0, 1, 0, 1, 0]), # Node labels pos=torch.randn(5, 2), # Node positions (optional) ) # ─── Inspect ─── print(data) # Data(x=[5,16], edge_index=[2,4], ...) print(data.num_nodes) # 5 print(data.num_edges) # 4 print(data.num_node_features) # 16 print(data.is_undirected()) # True/False # ─── Make undirected ─── data.edge_index = to_undirected(data.edge_index) # edge_attr must also be duplicated if present: # data.edge_attr = torch.cat([data.edge_attr, data.edge_attr], dim=0) # ─── Build from edge list (e.g., from NetworkX or CSV) ─── import pandas as pd # Edge list: src, dst, weight edges_df = pd.DataFrame({ 'src': [0, 1, 2, 3], 'dst': [1, 2, 3, 0], 'weight': [0.5, 1.0, 0.3, 0.8] }) edge_index = torch.tensor([edges_df['src'].values, edges_df['dst'].values], dtype=torch.long) edge_index = to_undirected(edge_index) edge_attr = torch.tensor(edges_df['weight'].values, dtype=torch.float).unsqueeze(1) edge_attr = torch.cat([edge_attr, edge_attr], dim=0) # Mirror for undirected num_nodes = max(edges_df['src'].max(), edges_df['dst'].max()) + 1 x = torch.eye(num_nodes) # One-hot identity features if no attributes data = Data(x=x, edge_index=edge_index, edge_attr=edge_attr) # ─── Add custom attributes ─── data.graph_label = torch.tensor([1]) # Graph-level label data.node_id = torch.arange(data.num_nodes) # Arbitrary metadata data.split = 'train' # String attributes fine too ``` ## MessagePassing Framework MessagePassing is the base class for ALL GNN layers in PyG. Understanding it = understanding how GNNs work. ```python import torch from torch_geometric.nn import MessagePassing import torch.nn.functional as F class CustomConv(MessagePassing): """ Custom GNN layer via MessagePassing. The propagate() call triggers this sequence: 1. message() — compute message for each edge (source → target) 2. aggregate() — combine messages arriving at each target node 3. update() — update each node's representation propagate(edge_index, x=x) routes: • x_j → source node features (j = source index) • x_i → target node features (i = target index) Subscript _i = target, _j = source. Always. """ def __init__(self, in_channels, out_channels): super().__init__(aggr='add') # Aggregation: 'add', 'mean', 'max' self.lin = torch.nn.Linear(in_channels, out_channels) def forward(self, x, edge_index): # Transform features BEFORE propagation (more efficient) x = self.lin(x) # Propagate: runs message() → aggregate() → update() return self.propagate(edge_index, x=x) def message(self, x_j): """ x_j: source node features for each edge. Shape: [num_edges, out_channels] Return: message to send along each edge. """ return x_j # Simplest: just pass source features through def update(self, aggr_out): """ aggr_out: aggregated messages per target node. Shape: [num_nodes, out_channels] Return: updated node representation. """ return aggr_out # Simplest: use aggregation directly # ─── Attention-weighted custom layer ─── class AttentionConv(MessagePassing): """Messages weighted by learned attention scores (simplified GAT).""" def __init__(self, in_channels, out_channels): super().__init__(aggr='add') self.lin = torch.nn.Linear(in_channels, out_channels) self.att = torch.nn.Parameter(torch.Tensor(1, out_channels)) torch.nn.init.xavier_uniform_(self.att.unsqueeze(0)) def forward(self, x, edge_index): x = self.lin(x) return self.propagate(edge_index, x=x) def message(self, x_i, x_j): # x_i = target features, x_j = source features # Attention score: how much should target i attend to source j? alpha = (x_i * self.att).sum(dim=-1) + (x_j * self.att).sum(dim=-1) alpha = F.leaky_relu(alpha, 0.2) # Note: full GAT uses softmax over neighbors — see GATConv for production version return x_j * alpha.unsqueeze(-1) # ─── Usage ─── # conv = CustomConv(16, 32) # out = conv(data.x, data.edge_index) # [num_nodes, 32] ``` ## Standard Layers — When to Use Which ```python from torch_geometric.nn import GCNConv, GATConv, GraphSAGEConv, GINConv import torch.nn as nn # ─── GCNConv: Graph Convolutional Network (Kipf & Welling, 2017) ─── # Averages neighbor features (with degree normalization). # Fast, simple baseline. No attention, no edge features. # USE: citation networks, social networks, when speed matters. conv_gcn = GCNConv(in_channels=16, out_channels=32) # out = conv_gcn(x, edge_index) # ─── GATConv: Graph Attention Network (Veličković et al., 2018) ─── # Learns attention weights — some neighbors matter more than others. # Slower than GCN but usually better accuracy. # USE: when neighbor importance varies, heterogeneous neighborhood structure.
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub