Key ideas
- Graphs as data. nodes with a feature matrix , edges (possibly with features ) and an adjacency matrix with when and are linked. For an undirected graph is symmetric with non-zeros, and row sums to the degree ; a directed graph has in general. A self-loop is a 1 on the diagonal. counts the walks of length from to , so multiplying features by is one hop of sum aggregation1. Real graphs are sparse, so they are stored as edge lists, not matrices1.
- Tasks1: node-level (a label per node: the topic of each paper in a citation network), edge-level (whether or how two nodes are linked: link prediction), graph-level (one label per graph: is this molecule toxic?).
- Why not an MLP or a CNN? Nodes have no order, so one graph has up to adjacency matrices, and an MLP on the flattened matrix may answer differently for each; it also needs a fixed 1. A kernel keeps one weight per neighbour offset, which works because every interior pixel has exactly 8 neighbours at fixed offsets; graph nodes have a varying number of unordered neighbours1.
- Symmetry. Renumbering nodes with a permutation matrix maps and . Layers with one output per node should be equivariant, ; graph-level outputs should be invariant. GNNs are built to respect these symmetries1.
- Message passing (one layer): compute a message along every edge, aggregate the messages at each node with a permutation-invariant function that takes any number of inputs (sum, mean, max), then update the node from its old state and the aggregate with a learned network. The same functions serve every node1. The sum is not normalised, so it also reflects how many neighbours there are; the mean gives a normalised view (useful when degrees vary a lot); the max highlights the single most salient value1. Neighbours and give sums 6 and 3 but the same mean (1.5) and max (2).
- GCN2: self-loops keep each node's own features in its update; the symmetric normalisation weights edge by , where is the degree counting the self-loop, so outputs do not grow with degree, using a single weight matrix per layer. It replaces (: degrees of ), whose eigenvalues reach 2 and make deep stacks unstable (the renormalisation trick). With a sparse a layer with input and output features costs . The Cora model has two layers, , trained full-batch.
- GraphSAGE3 learns aggregators (mean, max-pooling, or an LSTM fed a random order of the neighbours, since an LSTM is not permutation invariant) and concatenates the node's own state with the aggregate. It samples a fixed number of neighbours per layer, with replacement if needed, so a batch costs : with , , a target node gets 10 sampled neighbours and 250 two-hop samples. Because it learns functions instead of one embedding per node, it is inductive: it embeds nodes and graphs never seen in training.
- GAT4 learns how much each neighbour matters. Attention is masked: scores exist only for in the neighbourhood of (including ). With heads the outputs are concatenated ( features in Cora's first layer); a multi-head output layer averages them instead.
- Readout. For graph classification, a permutation-invariant pooling (sum, mean, max) turns the node embeddings into one vector1.
- Depth. After layers a node depends on its -hop neighbourhood2. More layers are not better: GCN propagation is a form of Laplacian smoothing, and repeating it makes the nodes of a connected component indistinguishable (over-smoothing), even with untrained weights5. Kipf and Welling got their best results with 2–3 layers, and beyond about 7 layers training without residual connections became difficult2.
- Transductive vs inductive. Transductive: the test nodes sit in the training graph with visible features and hidden labels (GCN on Cora: 140 training labels, 500 validation and 1,000 test nodes)2,6. Inductive: test nodes or graphs are unseen (GraphSAGE; GAT on protein graphs)3,4.
- PyTorch Geometric6: a graph is
Data(x, edge_index);edge_indexis a[2, num_edges]long tensor in COO format with source nodes in row 0 and target nodes in row 1, not a list of pairs. An undirected edge is stored in both directions, andnum_edgescounts columns. A mini-batch is one big graph with a block-diagonal adjacency matrix, concatenated node features and abatchvector giving each node's graph.
Formulas
Kipf and Welling write for the whole normalised matrix 2; some texts use , for and its degrees, so check which is meant. Node-wise, with (the degree counting the self-loop): . A layer with inputs and outputs has a weight matrix ( parameters, plus with a bias), whatever and are2.
General message passing, GraphSAGE and GAT1,3,4:
GraphSAGE also rescales to unit length3. In the GAT formulas includes itself; the LeakyReLU has slope 0.2; one head has parameters ( and ), and heads multiply this by 4. Readout: (or the mean or max). Over-smoothing limit on a connected graph: satisfies , so tends to a multiple of (entries proportional to ), while the mean tends to a constant vector5.
Worked example
Star graph: centre 0 with leaves 1, 2, 3; features , , no bias, ReLU.
- Degrees with self-loops: , so the centre weights itself by and each leaf by .
- Node 0: .
- Leaf : , giving , , for nodes 1, 2, 3; node 2 had feature 0 but receives its neighbour's signal.
- Compare: the plain sum and the mean differ from 1.914, and row 0 of sums to , not 1.
In code
import torch
from torch_geometric.data import Data
from torch_geometric.nn import GCNConv
edge_index = torch.tensor([[0, 1, 0, 2, 0, 3], # source nodes
[1, 0, 2, 0, 3, 0]]) # target nodes (both directions)
data = Data(x=torch.tensor([[2.0], [1.0], [0.0], [3.0]]), edge_index=edge_index)
print(data.num_nodes, data.num_edges) # 4 6
conv = GCNConv(1, 1) # adds self-loops, symmetric normalisation, bias
with torch.no_grad():
conv.lin.weight.fill_(1.0)
conv.bias.zero_()
print(conv(data.x, data.edge_index).view(-1)) # tensor([1.9142, 1.2071, 0.7071, 2.2071])
print(sum(p.numel() for p in GCNConv(1433, 16).parameters())) # 22944
Common pitfalls
- An undirected edge is two columns of
edge_indexand two non-zeros of ; has non-zeros. In a directed graph, node aggregates only the columns whose target (row 1) is , its in-degree, not every edge that touches it. - Equivariant (node outputs move with the nodes) is not invariant (one unchanged graph output). Concatenating node vectors in index order is neither a valid aggregator nor a valid readout.
- Self-loops are not there for symmetry or connectivity; they keep the node's own features. The rows of do not sum to 1; only the mean does.
- The parameter count of GCN, GraphSAGE and GAT layers does not depend on the number of nodes or edges.
- Deeper is not better. The GNN-specific danger is over-smoothing (all node representations look alike), which is not the same thing as exploding gradients or overfitting.
- GAT applies the LeakyReLU before the softmax, includes the node itself, and attends only over neighbours, not over all node pairs.
- GraphSAGE is inductive because it learns shared aggregators; the sampling only fixes the cost. Transductive is not unsupervised: labels are used, only the test nodes' labels are hidden.
References
- Sanchez-Lengeling et al. (2021), A Gentle Introduction to Graph Neural Networks, Distill — Images as graphs; What types of problems have graph structured data?; The challenges of using graphs in machine learning; Graph Neural Networks (pooling, passing messages); Into the Weeds (Comparing aggregation operations; matrix multiplications as walks on a graph)
- Kipf & Welling (2017), Semi-Supervised Classification with Graph Convolutional Networks — Sec. 2 (Eq. 2); Sec. 2.2 Layer-Wise Linear Model (Eqs. 7–8, renormalization trick); Sec. 3.1 Example (Eq. 9); Sec. 4.2 Neural Networks on Graphs; Sec. 5–6 (Cora set-up); Appendix B Experiments on Model Depth
- Hamilton, Ying & Leskovec (2017), Inductive Representation Learning on Large Graphs — Abstract; Sec. 3.1 Embedding generation (Algorithm 1, Neighborhood definition); Sec. 3.3 Aggregator Architectures; Appendix A Minibatch pseudocode
- Veličković et al. (2018), Graph Attention Networks — Sec. 2.1 Graph Attentional Layer (Eqs. 1–6); Sec. 2.2 Comparisons to Related Work; Sec. 3.1 Datasets (Inductive learning); Sec. 3.3 Experimental Setup
- Li, Han & Wu (2018), Deeper Insights into Graph Convolutional Networks for Semi-Supervised Learning — Sec. 3 Analysis (Why GCNs Work; When GCNs Fail, i.e. the karate-club experiment, the text before Theorem 1 and its proof)
- PyTorch Geometric documentation: Introduction by Example — Data Handling of Graphs; Common Benchmark Datasets; Mini-batches