Graph Neural Networks verstehen: Konzepte, Anwendungsfelder und Umsetzung mit PyTorch

Graph Neural Networks (GNNs) haben sich als leistungsstarker Deep-Learning-Ansatz etabliert, um komplexe Daten mit vielen Verknüpfungen zu analysieren. Im Unterschied zu klassischen neuronalen Netzen, die mit Eingaben fester Größe wie Bildern oder Sequenzen arbeiten, sind GNNs darauf ausgelegt, Beziehungen abzubilden, die zeigen, wie Entitäten in einem Graphen miteinander verbunden sind, sich gegenseitig beeinflussen und interagieren. Dadurch sind sie besonders nützlich in Bereichen, in denen Struktur entscheidend ist, etwa in sozialen Netzwerken, der Molekülchemie, Empfehlungssystemen oder der Betrugserkennung.

In diesem Beitrag erläutern wir die grundlegenden Prinzipien hinter GNNs, betrachten ihre Entwicklung und zeigen, welche praktischen Probleme sich damit lösen lassen. Außerdem sehen Sie anhand eines praxisnahen Beispiels mit der PyTorch-Bibliothek, wie sich GNNs konkret implementieren lassen.

Wichtige Erkenntnisse

  • Graph Neural Networks (GNNs) eignen sich hervorragend dazu, Beziehungen in vernetzten Daten zu modellieren, und sind daher ideal für Aufgaben, bei denen Struktur und Abhängigkeiten eine zentrale Rolle spielen.
  • GNNs erweitern klassische Deep-Learning-Verfahren auf graphbasierte Daten und ermöglichen das Lernen aus Knoten, Kanten und deren Verbindungen.
  • Verbreitete GNN-Architekturen wie Graph Convolutional Networks (GCNs), Graph Attention Networks (GATs) und GraphSAGE nutzen unterschiedliche Verfahren, um Informationen zwischen Knoten zu aggregieren und weiterzugeben.
  • Typische Anwendungsbereiche von GNNs reichen von der Analyse sozialer Netzwerke über die Vorhersage molekularer Eigenschaften bis hin zu Empfehlungssystemen, Betrugserkennung und Wissensgraphen.
  • Die Entwicklung von GNNs ist mit modernen Frameworks wie PyTorch Geometric gut zugänglich, da diese die Verarbeitung von Graphdaten, Message Passing und den Modellaufbau deutlich vereinfachen.
  • Eine praktische Implementierung veranschaulicht den vollständigen GNN-Workflow, von der Datenvorbereitung über die Modelldefinition bis hin zu Training und Auswertung.
  • GNNs entwickeln sich derzeit sehr schnell weiter, wobei Fortschritte in Skalierbarkeit, Effizienz und Leistung die Verarbeitung immer größerer und komplexerer Graphen verbessern.

Was sind Graph Neural Networks?

Graph Neural Networks, meist kurz GNNs genannt, gehören zu einer neueren Klasse neuronaler Netze, die speziell für Daten entwickelt wurden, die in Form von Graphen vorliegen. Ein Graph besteht aus Objekten, die als Knoten dargestellt werden, sowie aus Beziehungen zwischen diesen Objekten, die durch Kanten repräsentiert werden. GNNs können sowohl gerichtete Graphen verarbeiten, bei denen Kanten eine Richtung besitzen, als auch ungerichtete Graphen, bei denen Verbindungen keine feste Richtung haben. Zusätzlich können sich solche Graphen stark in Größe und Struktur unterscheiden.

Ein GNN besteht aus mehreren Schichten, wobei jede Ebene auf den Informationen der vorherigen aufbaut. Das Netzwerk erhält einen Graphen als Eingabe, einschließlich seiner Knoten, Kanten und der dazugehörigen Merkmale. Als Ergebnis entsteht eine Menge von Node Embeddings, also Vektorrepräsentationen für jeden Knoten des Eingabegraphen. Diese Embeddings bilden die vom Modell gelernten Eigenschaften der Knoten ab. Anders als herkömmliche neuronale Netze, die hauptsächlich mit Vektoren, Matrizen oder Tensoren arbeiten, können GNNs direkt vollständige Graphstrukturen verarbeiten. Genau diese Flexibilität macht sie so wertvoll für vernetzte Daten wie soziale Netzwerke, Molekülstrukturen oder Verkehrsnetze. Auch wenn die zugrunde liegende Mathematik anspruchsvoll sein kann, bleibt die Grundidee einfach: GNNs leiten wiederholt Informationen zwischen verbundenen Knoten weiter, um aussagekräftige Repräsentationen zu lernen.

Wie funktionieren Graph Neural Networks?

Graph Neural Networks, kurz GNNs, konzentrieren sich darauf, Muster und Abhängigkeiten zwischen Knoten innerhalb eines Netzwerks zu lernen. Das zentrale Prinzip dabei ist, dass jeder Knoten Nachrichten an seine Nachbarknoten sendet und dabei Informationen über sich selbst weitergibt.

  • Innerhalb eines GNN tauscht jeder Knoten Informationen mit den Knoten aus, mit denen er verbunden ist. Dadurch kann das Modell schrittweise die gesamte Graphstruktur erfassen.
  • Jeder Knoten erzeugt eine „Nachricht“, die auf seinen eigenen Merkmalen sowie auf den Eigenschaften der benachbarten Knoten basiert.
  • Gleichzeitig erstellen auch die umliegenden Knoten ihre eigenen Nachrichten und senden diese zurück.
  • Wenn ein Knoten diese Nachrichten empfängt, aktualisiert er seine interne Repräsentation, indem er alle eingehenden Informationen zusammenführt.
  • Dieser wiederholte Message-Passing-Prozess sorgt dafür, dass sich Wissen durch den gesamten Graphen ausbreitet und Knoten auch Informationen über weiter entfernte Bereiche erhalten.
  • Werden mehrere solcher Schichten übereinandergestapelt, können GNNs tiefere und komplexere Zusammenhänge erfassen.
  • Mit jeder zusätzlichen Schicht entstehen reichhaltigere und aussagekräftigere Merkmalsdarstellungen des Graphen.

Ein Graph Neural Network in PyTorch implementieren

Cora-Datensatz

Der Cora-Datensatz ist ein bekannter Benchmark, der häufig in der Forschung zum Graph Representation Learning eingesetzt wird. Er enthält eine Sammlung wissenschaftlicher Veröffentlichungen, die in sieben Kategorien unterteilt sind, darunter „Case Based“, „Genetic Algorithms“, „Neural Networks“, „Probabilistic Methods“, „Reinforcement Learning“ und „Rule Learning“. Cora wird seit vielen Jahren genutzt und gilt weiterhin als Standardgrundlage für zahlreiche Projekte in diesem Bereich. Mit diesem Datensatz lässt sich prüfen, wie gut ein Modell sowohl den textuellen Inhalt von Dokumenten als auch die Zitationsbeziehungen zwischen ihnen erfassen kann. Viele einflussreiche Arbeiten zu Graph Neural Networks haben Cora verwendet, um ihre Leistung bei genau diesen kombinierten Aufgaben zu bewerten. In diesem Graphen werden die Publikationen als Knoten dargestellt, während die Zitate zwischen ihnen die Kanten bilden. Jedes Dokument besitzt zusätzlich einen Feature-Vektor, der seinen Inhalt beschreibt. Ziel ist es, ein Modell zu entwickeln, das den Zitationsgraphen, die Inhaltsmerkmale und die Beziehungen zwischen ihnen nutzt, um vorherzusagen, zu welcher der sieben Klassen eine Publikation gehört.

Datenvorverarbeitung

Wir installieren die Bibliothek PyTorch Geometric mit dem folgenden Befehl:

pip install torch_geometric

Anschließend können wir PyTorch Geometric verwenden, um den Datensatz zu laden und vorzubereiten.

from torch_geometric.datasets import Planetoid
import torch_geometric.transforms as T

dataset = Planetoid(root='data/Cora', name='Cora', transform=T.NormalizeFeatures())

Die Klasse Planetoid lädt den Cora-Datensatz und normalisiert die Feature-Vektoren. Über das dataset-Objekt erhalten wir ein vorbereitetes Data-Objekt mit den folgenden Attributen:

  • x: eine Matrix mit Knotenmerkmalen in der Form (num_nodes, num_features)
  • edge_index: eine Matrix für die Kantenverbindungen in der Form (2, num_edges)
  • y: ein Vektor mit Knotenlabels in der Form (num_nodes)
  • train_mask, val_mask, test_mask: boolesche Masken, die festlegen, welche Knoten für Training, Validierung und Test verwendet werden.

Modellarchitektur

Bei der Entwicklung eines Graph Neural Networks ist die Wahl der passenden Architektur besonders wichtig. Im Folgenden gehen wir eine einfache Implementierung mit PyTorchs Bibliothek torch_geometric durch. Dabei verwenden wir ein Graph Convolutional Network als praxisnahen Einstieg in Graph-Learning-Aufgaben.

import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv

class GNN(torch.nn.Module):
    def __init__(self, in_channels, hidden_channels, out_channels):
        super(GNN, self).__init__()
        # Define the first graph convolutional layer
        self.conv1 = GCNConv(in_channels, hidden_channels)
        # Define the second graph convolutional layer
        self.conv2 = GCNConv(hidden_channels, out_channels)
        # Define the linear layer
        self.linear = torch.nn.Linear(out_channels, out_channels)

    def forward(self, x, edge_index):
        # Apply the first graph convolutional layer
        x = self.conv1(x, edge_index)
        # Apply the ReLU activation function
        x = F.relu(x)
        # Apply the second graph convolutional layer
        x = self.conv2(x, edge_index)
        # Apply the ReLU activation function
        x = F.relu(x)
        # Apply the linear layer
        x = self.linear(x)
        # Apply the log softmax activation function
        return F.log_softmax(x, dim=1)

Im obigen Code haben wir torch und torch.nn.functional importiert, um auf nützliche Module und Funktionen für neuronale Netze zugreifen zu können. Danach haben wir eine Klasse GNN definiert, die von torch.nn.Module erbt.

Innerhalb der Methode __init__ wurden zwei Faltungsschichten mithilfe des Moduls GCNConv aus PyTorch Geometric definiert. Dadurch lässt sich Graph Convolution unkompliziert umsetzen. Zusätzlich wurde eine einfache lineare Schicht ergänzt.

Im Forward-Pass wird die Eingabe zunächst durch die beiden Convolution-Schichten geleitet, wobei nach jeder Schicht die ReLU-Aktivierungsfunktion angewendet wird. Anschließend folgt die lineare Schicht und zum Schluss eine Log-Softmax-Funktion, um die Ausgaben zu verdichten.

Schon mit wenigen Codezeilen lässt sich so ein kompaktes und dennoch wirkungsvolles Graph Neural Network aufbauen.

Natürlich handelt es sich hier nur um ein einfaches Beispiel, aber es zeigt sehr gut, wie PyTorch und PyTorch Geometric das schnelle Prototyping und die Weiterentwicklung von GNN-Architekturen erleichtern. Insbesondere die GCNConv-Schichten machen es sehr einfach, die Graphstruktur in das Modell einzubinden.

Training

Für das Training verwenden wir einen Cross-Entropy-Loss zusammen mit dem Adam-Optimierer. Mithilfe der Maskenattribute des Data-Objekts können wir die Daten in Trainings-, Validierungs- und Testmengen aufteilen.

# Set the device to CUDA if available, otherwise use CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# Define the GNN model with the specified input, hidden, and output dimensions, and move it to the device
model = GNN(dataset.num_features, 16, dataset.num_classes).to(device)
# Define the Adam optimizer with the specified learning rate and weight decay
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

# Define the training function
def train():
    # Set the model to training mode
    model.train()
    # Zero the gradients of the optimizer
    optimizer.zero_grad()
    # Perform a forward pass of the model on the training nodes
    out = model(dataset.x.to(device), dataset.edge_index.to(device))
    # Compute the negative log-likelihood loss on the training nodes
    loss = F.nll_loss(out[dataset.train_mask], dataset.y[dataset.train_mask])
    # Compute the gradients of the loss with respect to the model parameters
    loss.backward()
    # Update the model parameters using the optimizer
    optimizer.step()
    # Return the loss as a scalar value
    return loss.item()

# Define the testing function
@torch.no_grad()
def test():
    # Set the model to evaluation mode
    model.eval()
    # Perform a forward pass of the model on all nodes
    out = model(dataset.x.to(device), dataset.edge_index.to(device))
    # Compute the predicted labels by taking the argmax of the output scores
    pred = out.argmax(dim=1)
    # Compute the training, validation, and testing accuracies
    train_acc = pred[dataset.train_mask].eq(dataset.y[dataset.train_mask]).sum().item() / dataset.train_mask.sum().item()
    val_acc = pred[dataset.val_mask].eq(dataset.y[dataset.val_mask]).sum().item() / dataset.val_mask.sum().item()
    test_acc = pred[dataset.test_mask].eq(dataset.y[dataset.test_mask]).sum().item() / dataset.test_mask.sum().item()
    # Return the accuracies as a tuple
    return train_acc, val_acc, test_acc

# Train the model for 500 epochs
for epoch in range(1, 500):
    # Perform a single training iteration and get the loss
    loss = train()
    # Evaluate the model on the training, validation, and testing sets and get the accuracies
    train_acc, val_acc, test_acc = test()
    # Print the epoch number, loss, and accuracies
    print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}, Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}, Test Acc: {test_acc:.4f}')

Die Funktion train führt einen einzelnen Trainingsdurchlauf aus und gibt den Verlustwert zurück. Die Funktion test misst die Leistung des Modells auf Trainings-, Validierungs- und Testdaten und liefert die jeweilige Genauigkeit zurück. Wir haben das Modell über 500 Epochen trainiert und nach jeder Epoche sowohl den Loss als auch die Accuracy ausgegeben.

Die Genauigkeit des GNN-Modells berechnen

Der folgende Code definiert eine Funktion, die die Genauigkeit des Modells über den gesamten Datensatz hinweg bestimmt. Die Funktion compute_accuracy() versetzt das Modell in den Evaluierungsmodus, führt einen Forward-Pass aus und sagt für jeden Knoten ein Label voraus. Anschließend werden diese Vorhersagen mit den tatsächlichen Labels verglichen, die Anzahl korrekter Vorhersagen gezählt und durch die Gesamtzahl der Knoten dividiert, um die Gesamtgenauigkeit zu erhalten.

@torch.no_grad()
def compute_accuracy():
    model.eval()
    out = model(dataset.x.to(device), dataset.edge_index.to(device))
    pred = out.argmax(dim=1)
    correct = pred.eq(dataset.y.to(device)).sum().item()
    total = dataset.y.shape[0]
    accuracy = correct / total
    return accuracy

accuracy = compute_accuracy()
print(f"Accuracy: {accuracy:.4f}")

In diesem Beispiel erreichte das Modell auf dem Cora-Datensatz eine Genauigkeit von 0.8006. Das bedeutet, dass es in etwa 80 % der Fälle das Klassenlabel korrekt vorhersagen konnte. Das ist ein gutes Ergebnis, auch wenn es nicht perfekt ist. Die Accuracy liefert einen schnellen, allgemeinen Eindruck von der Gesamtleistung des Modells, zeigt aber nicht vollständig, in welchen Bereichen es besonders gut oder weniger gut arbeitet.

Um die tatsächliche Leistungsfähigkeit des Modells besser zu verstehen, ist es sinnvoll, zusätzliche Bewertungsmetriken wie Precision, Recall, F1-Score und die Confusion Matrix einzubeziehen. Diese Kennzahlen geben detailliertere Einblicke in das Verhalten des Modells, etwa bei der korrekten Identifikation positiver und negativer Fälle oder beim Umgang mit unausgeglichenen Datensätzen. Auch wenn eine Genauigkeit von 80 % solide ist, reicht sie allein nicht aus, um das Modell als vollen Erfolg zu bewerten. Sie ist ein guter erster Indikator, aber sie erzählt nicht die ganze Geschichte der Modellleistung.

Auswertung

Wir können die Leistung des GNN mit Kennzahlen wie Accuracy, Precision, Recall und F1-Score beurteilen. Darüber hinaus lassen sich die vom Modell gelernten Node Embeddings mithilfe von t-SNE visualisieren. Diese Methode projiziert hochdimensionale Embeddings in zwei Dimensionen, sodass sie anschaulich dargestellt werden können.

# Import the necessary libraries
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt

# Set the model to evaluation mode
model.eval()

# Perform a forward pass of the model on the dataset
out = model(dataset.x.to(device), dataset.edge_index.to(device))

# Apply t-SNE to the output feature matrix to obtain a 2D embedding
emb = TSNE(n_components=2).fit_transform(out.cpu().detach().numpy())

# Create a figure with a specified size
plt.figure(figsize=(10,10))

# Create a scatter plot of the embeddings, color-coded by the true labels
plt.scatter(emb[:,0], emb[:,1], c=dataset.y, cmap='jet')

# Display the plot
plt.show()

Hinweis: Der Leser kann den obigen Code ausführen und ein interpretierbares Streudiagramm anzeigen lassen. Das Skript nutzt t-SNE, um die gelernten Node Embeddings in einer zweidimensionalen Darstellung sichtbar zu machen, was eine effektive Möglichkeit ist, hochdimensionale Daten zu untersuchen. Folgendes passiert in der Visualisierung:

  • Jeder Punkt im Diagramm steht für einen Knoten im Datensatz. Die x- und y-Achse bilden die beiden Dimensionen ab, auf die t-SNE die ursprünglichen Embeddings reduziert hat. Die Farbe eines Punktes entspricht dem tatsächlichen Label des jeweiligen Knotens.
  • Knoten mit ähnlichen Embeddings sollten auch ähnliche Labels besitzen und daher als Cluster im Diagramm erscheinen. Umgekehrt liegen Knoten mit sehr unterschiedlichen Embeddings in der Regel weiter auseinander und gehören oft zu verschiedenen Klassen.
  • Insgesamt vermittelt die Grafik eine anschauliche Zusammenfassung der Beziehungen zwischen den Knoten auf Basis der gelernten Embeddings. Es werden Gruppen sichtbar, die auf gemeinsame Eigenschaften hinweisen. Damit erhält man einen praktischen Einblick darin, wie das Modell Informationen strukturiert und ordnet.

Mögliche Herausforderungen und wichtige Überlegungen

  • Mit 2708 Knoten und 5429 Kanten ist der Cora-Datensatz vergleichsweise klein. Das kann die Effizienz eines GNN einschränken und den Einsatz fortgeschrittener Methoden wie Data Augmentation oder Transfer Learning erforderlich machen.
  • Der Cora-Datensatz enthält nur einen Knotentyp und einen Kantentyp und stellt damit einen homogenen Graphen dar. Das kann die Übertragbarkeit des GNN auf komplexere Netzwerke mit unterschiedlichen Knoten- und Kantentypen begrenzen.
  • Die Wahl geeigneter Hyperparameter, darunter die Anzahl der Hidden Layers, die Zahl der Hidden Units und die Learning Rate, hat erheblichen Einfluss auf die Leistung des Modells und erfordert eine sorgfältige Abstimmung.

FAQ

1. Was ist ein Graph Neural Network (GNN)?

Ein GNN ist ein neuronales Netz, das speziell für graphstrukturierte Daten entwickelt wurde. Es lernt Repräsentationen auf Knoten-, Kanten- oder Graph-Ebene, indem es Informationen benachbarter Knoten zusammenführt.

2. Worin unterscheidet sich ein GNN von einem klassischen neuronalen Netz?

Klassische neuronale Netze eignen sich vor allem für gitterförmige Daten wie Bilder oder Sequenzen, während GNNs für unregelmäßige und vernetzte Strukturen wie soziale Netzwerke oder Molekülgraphen ausgelegt sind.

3. Für welche Problemstellungen werden GNNs eingesetzt?

Typische Einsatzgebiete sind Node Classification, Link Prediction, Graph Classification, Empfehlungssysteme, Betrugserkennung und die Vorhersage molekularer Eigenschaften.

4. Was bedeutet Message Passing in GNNs?

Message Passing ist der zentrale Mechanismus, bei dem Knoten Informationen mit ihren Nachbarn austauschen, ihre Embeddings aktualisieren und kontextbezogene Beziehungen erlernen.

5. Lassen sich GNNs gut auf sehr große Graphen skalieren?

Die Skalierung kann herausfordernd sein, da Speicherbedarf und Nachbarschaftserweiterung schnell wachsen. Verfahren wie Sampling mit GraphSAGE, Mini-Batching und verteiltes Training helfen dabei, dieses Problem zu verringern.

6. Welche Programmbibliotheken eignen sich besonders für die Implementierung von GNNs?

Beliebte Frameworks sind PyTorch Geometric (PyG) und die Deep Graph Library (DGL), die beide fertige Layer und Hilfsfunktionen für den Aufbau von GNN-Modellen bereitstellen.

7. Sind GNNs für Echtzeitanwendungen geeignet?

Ja, abhängig von der Komplexität des Modells. Leichtgewichtige Architekturen und samplingbasierte Ansätze können nahezu Echtzeitfähigkeit ermöglichen.

8. Können GNNs mit dynamischen oder sich verändernden Graphen umgehen?

Ja, dynamische GNN-Varianten können Graphen verarbeiten, die sich im Zeitverlauf verändern. Das ist zum Beispiel für Verkehrsprognosen, zeitbasierte Empfehlungssysteme und Anomalieerkennung nützlich.

9. Welche Datenvorbereitung ist für GNNs erforderlich?

In der Regel müssen Adjazenzinformationen sowie Knoten- und Kantenmerkmale vorbereitet und der Graph korrekt für die gewählte Bibliothek formatiert werden.

10. Sind GNNs interpretierbar?

Die Interpretierbarkeit verbessert sich zunehmend durch Werkzeuge wie Attention-Mechanismen und GNNExplainer, die einflussreiche Knoten und Kanten sichtbar machen können.

Fazit

In diesem Artikel haben wir die zentralen Konzepte hinter Graph Neural Networks (GNNs) vorgestellt und betrachtet, wie sie in unterschiedlichen Bereichen eingesetzt werden können. GNNs eignen sich besonders gut für graphstrukturierte Daten, da sie komplexe Beziehungen in sozialen Netzwerken, Molekülgraphen, Verkehrssystemen und vielen weiteren Anwendungen modellieren können.

Um diese Ideen praktisch zu veranschaulichen, haben wir mit dem weit verbreiteten Cora-Datensatz gearbeitet, einem Standard-Benchmark im Graph Learning. In diesem Datensatz wird jede Veröffentlichung als Knoten dargestellt, während die Zitationsbeziehungen zwischen den Publikationen die Kanten bilden. Unser Ziel bestand darin, sowohl die textuellen Merkmale der einzelnen Dokumente als auch die Zitationsverbindungen zu nutzen, um die jeweilige Kategorie vorherzusagen.

Wir haben die Daten mit der Bibliothek PyTorch Geometric vorbereitet, die Feature-Vektoren normalisiert und den Datensatz in Trainings-, Validierungs- und Testbereiche aufgeteilt. Anschließend haben wir ein einfaches GNN mit Graph-Convolution-Schichten und einem linearen Klassifikator aufgebaut und es mit Cross-Entropy-Loss und dem Adam-Optimierer trainiert. Abschließend wurde die Modellleistung anhand der Genauigkeit ausgewertet.

Auch wenn es noch viele weitere Verfahren und Verbesserungsmöglichkeiten zu entdecken gibt, bietet dieses Projekt einen soliden Einstieg in die Funktionsweise von GNNs und zeigt, wie wirkungsvoll sie beim Lernen aus komplexen relationalen Daten sein können.

Quelle: digitalocean.com

Jetzt 200€ Guthaben sichern

Registrieren Sie sich jetzt in unserer ccloud³ und erhalten Sie 200€ Startguthaben für Ihr Projekt.

Das könnte Sie auch interessieren:

Moderne Hosting Services mit Cloud Server, Managed Server und skalierbarem Cloud Hosting für professionelle IT-Infrastrukturen

LangMem SDK für KI-Agenten: Langzeitgedächtnis Guide

AI/ML, Tutorial
Vijona8 Juni um 13:37 Uhr LangMem SDK für KI-Agenten: Langzeitgedächtnis, Architektur, Integration, Leistung und Alternativen KI-Agenten, die auf großen Sprachmodellen basieren, waren lange mit einer wesentlichen Einschränkung konfrontiert: Ihr Gedächtnis ist…