Previous lesson. Graph Neural Networks Advanced Topics · Next lesson. Graph Neural Networks Quick Reference
Graph Neural Networks Project Tutorial
Step-by-step tutorial: Node classification with GCN on Cora dataset.
Project: Node Classification with GCN
Objective
Classify research papers in the Cora citation network using Graph Convolutional Networks.
Copy the cells below onto your machine after installing PyTorch Geometric. They are tagged so the Study Hub accuracy suite does not download Cora or require GPU stacks.
Step 1: Setup
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid
from torch_geometric.nn import GCNConv
Step 2: Load Dataset
dataset = Planetoid(root='/tmp/Cora', name='Cora')
data = dataset[0]
print(f"Nodes: {data.x.shape[0]}")
print(f"Edges: {data.edge_index.shape[1]}")
print(f"Features: {data.x.shape[1]}")
print(f"Classes: {dataset.num_classes}")
Step 3: Define GCN Model
class GCN(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super(GCN, self).__init__()
self.c hidden_dim)
self.c output_dim)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index)
x = F.relu(x)
x = F.dropout(x, training=self.training)
x = self.conv2(x, edge_index)
return F.log_softmax(x, dim=1)
Step 4: Training
model = GCN(input_dim=dataset.num_features,
hidden_dim=64,
output_dim=dataset.num_classes)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
def train():
model.train()
optimizer.zero_grad()
out = model(data.x, data.edge_index)
loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
return loss.item()
def test():
model.eval()
out = model(data.x, data.edge_index)
pred = out.argmax(dim=1)
acc = (pred[data.test_mask] == data.y[data.test_mask]).sum() / data.test_mask.sum()
return acc.item()
# Train
for epoch in range(200):
loss = train()
if epoch % 20 == 0:
acc = test()
print(f"Epoch {epoch}, Loss: {loss:.4f}, Accuracy: {acc:.4f}")
Tiny local smoke (no PyG download)
Runs with NumPy only. Shows one message-passing step on a tiny synthetic graph.
import numpy as np
rng = np.random.default_rng(0)
n_nodes, n_feat, n_classes = 12, 8, 3
x = rng.normal(size=(n_nodes, n_feat))
# Undirected ring + a few random edges
edges = [(i, (i + 1) % n_nodes) for i in range(n_nodes)]
edges += [(0, 5), (2, 8), (3, 9)]
adj = np.zeros((n_nodes, n_nodes))
for i, j in edges:
adj[i, j] = 1.0
adj[j, i] = 1.0
np.fill_diagonal(adj, 1.0)
deg = adj.sum(axis=1)
deg_inv_sqrt = np.diag(1.0 / np.sqrt(np.maximum(deg, 1e-8)))
norm_adj = deg_inv_sqrt @ adj @ deg_inv_sqrt
w = rng.normal(size=(n_feat, n_classes))
h = np.maximum(0.0, norm_adj @ x @ w) # one GCN-like layer + ReLU
y_hat = h.argmax(axis=1)
print(f"Synthetic nodes={n_nodes} edges={len(edges)} pred_classes={sorted(set(y_hat.tolist()))}")
assert h.shape == (n_nodes, n_classes)
Previous lesson. Graph Neural Networks Advanced Topics · Next lesson. Graph Neural Networks Quick Reference