Unsupervised GraphSAGE (Cora)
Author: K3-Node Team
Backend: Multi-Backend
Dataset: Cora (Planetoid)
Description: Learn node embeddings of the Cora graph without labels: GraphSAGE (Hamilton et al., 2017) is trained to predict which pairs of nodes are linked.
Unsupervised GraphSAGE (Cora)
Learn node embeddings of the Cora graph without labels: GraphSAGE (Hamilton et al., 2017) is trained to predict which pairs of nodes are linked. The embeddings are then evaluated by how well a simple classifier predicts the topics of the papers.
Same model as PyG's examples/graph_sage_unsup.py.
Install K3-Node, then choose a backend: "tensorflow", "torch" or "jax"
Load the data
LinkNeighborLoader yields mini-batches of edges together with the sampled neighborhoods of their endpoints.
import keras
from keras import ops
from k3_node.datasets import Planetoid
from k3_node.loader import FullGraphDataset, LinkNeighborLoader
from k3_node.models import GraphSAGE
from k3_node.transforms import NormalizeFeatures
dataset = Planetoid("data/Planetoid", name="Cora", transform=NormalizeFeatures())
data = dataset[0]
train_loader = LinkNeighborLoader(data, batch_size=256, shuffle=True, neg_sampling_ratio=1.0, num_neighbors=[10, 10])
Define the model
class LinkModel(keras.Model):
def __init__(self, in_channels):
super().__init__()
self.sage = GraphSAGE(in_channels, hidden_channels=64, num_layers=2)
def call(self, data, training=False):
h = self.sage(data.x, data.edge_index, training=training)
if getattr(data, "edge_label_index", None) is None:
return h # node embeddings
src, dst = data.edge_label_index[0], data.edge_label_index[1]
return ops.sum(ops.take(h, src, axis=0) * ops.take(h, dst, axis=0), axis=-1)
model = LinkModel(dataset.num_features)
Train
Each batch is a set of 256 edges (with their sampled neighborhoods) plus as many random non-edges; the model learns to score true edges high. No labels are used.
model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss=keras.losses.BinaryCrossentropy(from_logits=True))
model.fit(train_loader, epochs=50, verbose=2)
Evaluate
from sklearn.linear_model import LogisticRegression
z = model.predict(FullGraphDataset(data), verbose=0)
train_mask, val_mask, test_mask, y = (ops.convert_to_numpy(v) for v in (data.train_mask, data.val_mask,
data.test_mask, data.y))
clf = LogisticRegression().fit(z[train_mask], y[train_mask])
print(f"Validation accuracy: {clf.score(z[val_mask], y[val_mask]):.4f}")
print(f"Test accuracy: {clf.score(z[test_mask], y[test_mask]):.4f}")