Was macht PyTorch Geometric (PyG)?
PyG ist die Standardbibliothek für Graph Neural Networks, die auf PyTorch aufbaut. Es bietet Datenstrukturen für Graphen, über 60 GNN-Layer-Implementierungen, skalierbares Mini-Batch-Training und Unterstützung für heterogene Graphen.
Installation
Getestet gegen torch-geometric 2.7.x (Oktober 2025). Erfordert Python 3.10+ und PyTorch 2.6+.
# 1. Zuerst PyTorch installieren (an CUDA/CPU-Setup anpassen – siehe https://pytorch.org/get-started/locally/)
uv pip install torch
# 2. Kern-PyG (es sind keine Erweiterungs-Wheels für die grundlegende Nutzung erforderlich)
uv pip install torch_geometric
Optionale beschleunigte Operationen (pyg-lib, torch-scatter, torch-sparse, torch-cluster) sind nicht erforderlich für die grundlegende PyG-Nutzung (seit PyG 2.3). Installieren Sie versionierte Wheels aus dem PyG-Wheel-Index, nachdem Sie Ihre PyTorch- und CUDA-Versionen überprüft haben:
python -c "import torch; print(torch.__version__, torch.version.cuda)"
# Dann Wheels für Ihre torch+CUDA-Kombination installieren, z. B.:
uv pip install pyg-lib torch-scatter torch-sparse torch-cluster \
-f https://data.pyg.org/whl/torch-2.8.0+cu128.html
Überprüfen Sie Ihre Version:
import torch_geometric
print(torch_geometric.__version__)
Conda: Der pyg-Conda-Channel wird für PyTorch >2.5 nicht mehr gepflegt – verwenden Sie stattdessen uv pip install und den obigen Wheel-Index.
Hinweise zu PyG 2.7
PyG 2.7 hat die Unterstützung für Python 3.9 und PyTorch ≤2.5 eingestellt. Siehe die 2.7.0-Versionshinweise für Kompatibilitätstabellen mit PyTorch 2.6–2.8. torch_geometric.distributed ist veraltet – verwenden Sie stattdessen das standardmäßige torch.distributed DDP (siehe references/scaling.md).
Kernkonzepte
Graph-Daten: Data und HeteroData
Ein Graph lebt in einem Data-Objekt. Die wichtigsten Attribute:
from torch_geometric.data import Data
data = Data(
x=node_features, # [num_nodes, num_node_features]
edge_index=edge_index, # [2, num_edges] – COO-Format, dtype=torch.long
edge_attr=edge_features, # [num_edges, num_edge_features]
y=labels, # Knotenebene [num_nodes, *] oder Graphebene [1, *]
pos=positions, # [num_nodes, num_dimensions] (für Punktwolken/Räumliches)
)
Das edge_index-Format ist entscheidend: Es ist ein [2, num_edges]-Tensor, wobei edge_index[0] = Quellknoten, edge_index[1] = Zielknoten. Es ist KEINE Liste von Tupeln. Wenn Sie Kantenpaare als Zeilen haben, transponieren Sie und rufen Sie .contiguous() auf:
# Wenn Kanten [[src1, dst1], [src2, dst2], ...] sind – zuerst transponieren:
edge_index = edge_pairs.t().contiguous()
Für ungerichtete Graphen fügen Sie beide Richtungen ein: Kante (0,1) benötigt sowohl [0,1] als auch [1,0] in edge_index.
Für heterogene Graphen verwenden Sie HeteroData – siehe den Abschnitt Heterogene Graphen unten.
Datensätze
PyG bündelt viele Standarddatensätze, die automatisch heruntergeladen und vorverarbeitet werden:
from torch_geometric.datasets import Planetoid, TUDataset
# Knotenklassifikation mit einem einzelnen Graph (Cora, Citeseer, Pubmed)
dataset = Planetoid(root='./data', name='Cora')
data = dataset[0] # einzelner Graph mit Train-/Val-/Test-Masken
# Graphklassifikation mit mehreren Graphen (ENZYMES, MUTAG, IMDB-BINARY, usw.)
dataset = TUDataset(root='./data', name='ENZYMES')
# dataset[0], dataset[1], ... sind einzelne Graphen
Häufige Datensätze nach Aufgabe:
- Knotenklassifikation: Planetoid (Cora/Citeseer/Pubmed), OGB (ogbn-arxiv, ogbn-products, ogbn-mag)
- Graphklassifikation: TUDataset (MUTAG, ENZYMES, PROTEINS, IMDB-BINARY), OGB (ogbg-molhiv)
- Linkvorhersage: OGB (ogbl-collab, ogbl-citation2)
- Molekular: QM7, QM9, MoleculeNet
- Punktwolke/Mesh: ShapeNet, ModelNet10/40, FAUST
Transformationen
Transformationen verarbeiten Graphen vor oder erweitern sie, analog zu torchvision-Transformationen:
import torch_geometric.transforms as T
# Häufige Transformationen
T.NormalizeFeatures() # Zeilennormierung der Knotenmerkmale auf Summe = 1
T.ToUndirected() # Rückwärtskanten hinzufügen, um den Graph ungerichtet zu machen
T.AddSelfLoops() # Selbstschleifenkanten hinzufügen
T.KNNGraph(k=6) # k-NN-Graph aus Punktwolkenpositionen erstellen
T.RandomJitter(0.01) # Zufällige Rauschverstärkung bei Positionen
T.Compose([...]) # Mehrere Transformationen verketten
# Anwenden als pre_transform (einmal, auf Festplatte gespeichert) oder transform (bei jedem Zugriff)
dataset = ShapeNet(root='./data', pre_transform=T.KNNGraph(k=6),
transform=T.RandomJitter(0.01))
GNN-Modelle erstellen
Schnellstart: Verwendung integrierter Layer
Der schnellste Weg, ein GNN zu erstellen – stapeln Sie Conv-Layer aus torch_geometric.nn:
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
class GCN(torch.nn.Module):
def __init__(self, in_channels, hidden_channels, out_channels):
super().__init__()
self.conv1 = GCNConv(in_channels, hidden_channels)
self.conv2 = GCNConv(hidden_channels, out_channels)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index).relu()
x = F.dropout(x, p=0.5, training=self.training)
x = self.conv2(x, edge_index)
return x
Wichtig: PyG-Conv-Layer enthalten KEINE Aktivierungsfunktionen – wenden Sie diese selbst nach jedem Layer an. Dies ist aus Flexibilitätsgründen so konzipiert.
Einen Conv-Layer auswählen
Wählen Sie basierend auf Ihrer Aufgabe und Graphstruktur:
| Layer | Am besten geeignet für | Schlüsselidee |
|---|---|---|
GCNConv | Homogene, semi-supervised Knotenklassifikation | Spektral inspirierte, gradnormalisierte Aggregation |
GATConv / GATv2Conv | Wenn die Nachbarschaftswichtigkeit variiert | Aufmerksamkeitsgewichtete Nachrichten |
SAGEConv | Große Graphen, induktive Umgebungen | Sampling-freundlich, lernbare Aggregation |
GINConv | Graphklassifikation, Maximierung der Ausdrucksstärke | So leistungsfähig wie der WL-Test |
TransformerConv | Reichhaltige Kantenmerkmale, komplexe Interaktionen | Multi-Head-Aufmerksamkeit mit Kantenmerkmalen |
EdgeConv | Punktwolken, dynamische Graphen | MLP auf Kantenmerkmalen (x_i, x_j - x_i) |
RGCNConv | Heterogene Graphen mit vielen Relationstypen | Relationsspezifische Gewichtsmatrizen |
HGTConv | Heterogene Graphen | Typspezifische Aufmerksamkeit |
Alle Conv-Layer akzeptieren mindestens (x, edge_index). Viele akzeptieren auch edge_attr für Kantenmerkmale.
Lazy-Initialisierung
Verwenden Sie -1 für Eingabekanäle, damit PyG die Dimensionen automatisch ableitet – besonders nützlich für heterogene Modelle:
conv = SAGEConv((-1, -1), 64) # Eingabedimensionen werden beim ersten Vorwärtsdurchlauf abgeleitet
# Lazy-Module initialisieren:
with torch.no_grad():
out = model(data.x, data.edge_index)
Hochrangige Modell-APIs
Für gängige Architekturen bietet PyG vorgefertigte Modellklassen:
from torch_geometric.nn import GraphSAGE, GCN, GAT, GIN
model = GraphSAGE(
in_channels=dataset.num_features,
hidden_channels=64,
out_channels=dataset.num_classes,
num_layers=2,
)
Benutzerdefinierte Layer über MessagePassing
Um einen neuartigen GNN-Layer zu implementieren, subklassieren Sie MessagePassing. Das Framework ist:
propagate()orchestriert das Message Passingmessage()definiert, welche Informationen entlang jeder Kante fließen (die Phi-Funktion)aggregate()kombiniert Nachrichten an jedem Knoten (Summe/Mittelwert/Maximum)update()transformiert das aggregierte Ergebnis (die Gamma-Funktion)
from torch_geometric.nn import MessagePassing
from torch_geometric.utils import add_self_loops, degree
class MyConv(MessagePassing):
def __init__(self, in_channels, out_channels):
super().__init__(aggr='add') # "add", "mean" oder "max"
self.lin = torch.nn.Linear(in_channels, out_channels)
def forward(self, x, edge_index):
# Vorverarbeitung vor dem Message Passing
x = self.lin(x)
# Message Passing starten
return self.propagate(edge_index, x=x)
def message(self, x_j):
# x_j: Merkmale der Quellknoten für jede Kante [num_edges, features]
# Das _j-Suffix indexiert automatisch Quellknoten, _i indexiert Zielknoten
return x_j
Die _i/_j-Konvention: Jeder Tensor, der an propagate() übergeben wird, kann automatisch indexiert werden, indem _i (Ziel-/Zentralknoten) oder _j (Quell-/Nachbarknoten) in der message()-Signatur angehängt wird. Wenn Sie also x=... an propagate übergeben, können Sie in message() auf x_i und x_j zugreifen.
Lesen Sie references/message_passing.md für die vollständigen GCN- und EdgeConv-Implementierungsbeispiele.
Aufgabenspezifische Muster
Knotenklassifikation
# Full-Batch-Training auf einem einzelnen Graphen (z. B. Cora)
model.train()
for epoch in range(200):
optimizer.zero_grad()
out = model(data.x, data.edge_index)
loss = F.cross_entropy(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
# Auswertung – train(False) versetzt das Modell in den Inferenzmodus (deaktiviert Dropout/BN)
model.train(False)
pred = model(data.x, data.edge_index).argmax(dim=1)
acc = (pred[data.test_mask] == data.y[data.test_mask]).float().mean()
Graphklassifikation
Mehrere Graphen – verwenden Sie DataLoader für Mini-Batching und global Pooling, um Graphebenen-Repräsentationen zu erhalten:
from torch_geometric.loader import DataLoader
from torch_geometric.nn import GCNConv, global_mean_pool
loader = DataLoader(dataset, batch_size=32, shuffle=True)
class GraphClassifier(torch.nn.Module):
def __init__(self, in_ch, hidden_ch, out_ch):
super().__init__()
self.conv1 = GCNConv(in_ch, hidden_ch)
self.conv2 = GCNConv(hidden_ch, hidden_ch)
self.lin = torch.nn.Linear(hidden_ch, out_ch)
def forward(self, x, edge_index, batch):
x = self.conv1(x, edge_index).relu()
x = self.conv2(x, edge_index).relu()
x = global_mean_pool(x, batch) # [num_graphs_in_batch, hidden_ch]
return self.lin(x)
# Trainingsschleife
for data in loader:
out = model(data.x, data.edge_index, data.batch)
loss = F.cross_entropy(out, data.y)
PyGs DataLoader bündelt mehrere Graphen, indem es blockdiagonale Adjazenzmatrizen erstellt. Der batch-Tensor ordnet jeden Knoten seinem Graphindex zu. Pooling-Operationen (global_mean_pool, global_max_pool, global_add_pool) verwenden dies, um pro Graph zu aggregieren.
Linkvorhersage
Teilen Sie Kanten in Trainings-/Validierungs-/Testsets auf und verwenden Sie Negative Sampling:
from torch_geometric.transforms import RandomLinkSplit
transform = RandomLinkSplit(
num_val=0.1,
num_test=0.1,
is_undirected=True,
add_negative_train_samples=False,
)
train_data, val_data, test_data = transform(data)
# Knoten codieren, dann Kanten bewerten
z = model.encode(train_data.x, train_data.edge_index)
# Positive Kanten
pos_score = (z[train_data.edge_label_index[0]] * z[train_data.edge_label_index[1]]).sum(dim=1)
Lesen Sie references/link_prediction.md für die vollständige Anleitung zur Linkvorhersage: GAE/VGAE-Autoencoder, vollständige Trainingsschleifen, LinkNeighborLoader für große Graphen, heterogene Linkvorhersage und Bewertungsmetriken.
Skalierung auf große Graphen
Für Graphen, die nicht in den GPU-Speicher passen, verwenden Sie Nachbarschafts-Sampling mit NeighborLoader:
from torch_geometric.loader import NeighborLoader
train_loader = NeighborLoader(
data,
num_neighbors=[15, 10], # 15 Nachbarn in Hop 1, 10 in Hop 2 sampeln
batch_size=128, # Anzahl der Startknoten pro Batch
input_nodes=data.train_mask, # Welche Knoten zum Sampeln verwendet werden
shuffle=True,
)
for batch in train_loader:
batch = batch.to(device)
out = model(batch.x, batch.edge_index)
# Nur die ersten batch_size Knoten für den Verlust verwenden (dies sind die Startknoten)
loss = F.cross_entropy(out[:batch.batch_size], batch.y[:batch.batch_size])
Wichtige Punkte zu NeighborLoader:
- Die Länge der
num_neighbors-Liste sollte der GNN-Tiefe (Anzahl der Message-Passing-Layer) entsprechen - Startknoten sind immer die ersten
batch.batch_sizeKnoten in der Ausgabe batch.n_idordnet umbenannte Indizes wieder den ursprünglichen Knoten-IDs zu- Funktioniert sowohl für
Dataals auch fürHeteroData - Für Linkvorhersage verwenden Sie stattdessen
LinkNeighborLoader - Das Samplen von mehr als 2-3 Hops ist im Allgemeinen nicht durchführbar (exponentielles Wachstum)
Andere Skalierungsoptionen: ClusterLoader (ClusterGCN), GraphSAINTSampler, ShaDowKHopSampler. Für Multi-GPU-Training, DDP, PyTorch-Lightning-Integration und torch.compile-Unterstützung lesen Sie references/scaling.md.
Heterogene Graphen
Für Graphen mit mehreren Knoten- und Kantentypen (soziale Netzwerke, Wissensgraphen, Empfehlungen):
from torch_geometric.data import HeteroData
data = HeteroData()
# Knotenmerkmale – indiziert durch Knotentyps-String
data['user'].x = torch.randn(1000, 64)
data['movie'].x = torch.randn(500, 128)
# Kantenindizes – indiziert durch (src_type, edge_type, dst_type) Triplett
data['user', 'rates', 'movie'].edge_index = torch.randint(0, 500, (2, 3000))
data['user', 'follows', 'user'].edge_index = torch.randint(0, 1000, (2, 5000))
# Zugriffs-Convenience-Dictionaries
data.x_dict # {'user': Tensor, 'movie': Tensor}
data.edge_index_dict # {('user','rates','movie'): Tensor, ...}
data.metadata() # ([node_types], [edge_types])
Drei Möglichkeiten, heterogene GNNs zu erstellen
1. Automatische Konvertierung mit to_hetero() – schreiben Sie ein homogenes Modell und konvertieren Sie es automatisch:
from torch_geometric.nn import SAGEConv, to_hetero
class GNN(torch.nn.Module):
def __init__(self, hidden_channels, out_channels):
super().__init__()
self.conv1 = SAGEConv((-1, -1), hidden_channels)
self.conv2 = SAGEConv((-1, -1), out_channels)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index).relu()
x = self.conv2(x, edge_index)
return x
model = GNN(64, dataset.num_classes)
model = to_hetero(model, data.metadata(), aggr='sum')
# Jetzt akzeptiert es Dictionaries:
out = model(data.x_dict, data.edge_index_dict)
Verwenden Sie (-1, -1) für bipartite Eingabekanäle (Quelle und Ziel können sich unterscheiden). Die Lazy-Initialisierung erledigt den Rest.
2. HeteroConv-Wrapper – unterschiedlicher Conv pro Kantentyp:
from torch_geometric.nn import HeteroConv, GCNConv, SAGEConv, GATConv
conv = HeteroConv({
('paper', 'cites', 'paper'): GCNConv(-1, 64),
('author', 'writes', 'paper'): SAGEConv((-1,
…
[truncated for translation]