GCN: Graph Convolutional Networks

5 minute read

Published:

TL;DR: GCN's layer formula is \(H' = \sigma(\hat{A} H W)\), where \(\hat{A} = \tilde{D}^{-1/2} \tilde{A} \tilde{D}^{-1/2}\) is the symmetrically normalised adjacency built from \(\tilde{A} = A + I\) (the adjacency with self-loops) and its degree matrix \(\tilde{D}\). Each layer averages neighbour representations and applies a learned linear transformation. Simple. Powerful. The foundation everything else builds on.
Graph Convolutional Network
GCN layer-wise propagation rule and architecture (Kipf & Welling, 2017)

From Spectral Theory to a Practical Layer

As explained in the Graph Laplacian post, spectral graph convolution filters signals using the Laplacian’s eigenvectors. But computing eigenvectors is O(N³) — completely infeasible for large graphs.

Kipf & Welling (2016) made two elegant simplifications:

  1. Localise: approximate the spectral filter as a first-order polynomial in the Laplacian \(L\) — only immediate neighbours matter.
  2. Normalise: use symmetric normalisation to prevent large-degree nodes from dominating.

The result is a layer that can be computed with simple sparse matrix multiplication.

The GCN Formula

\[ H^{(l+1)} = \sigma\!\left( \hat{A} \, H^{(l)} \, W^{(l)} \right), \qquad \hat{A} \;=\; \tilde{D}^{-1/2} \, \tilde{A} \, \tilde{D}^{-1/2} \]

Where:

  • \(H^{(l)} \in \mathbb{R}^{N \times F_l}\) — node features at layer \(l\), one row per node; \(H^{(0)} = X\) is the input feature matrix.
  • \(A \in \{0,1\}^{N \times N}\) — the plain adjacency matrix of the graph.
  • \(\tilde{A} = A + I\) — the adjacency with self-loops added.
  • \(\tilde{D}\) — the diagonal degree matrix of \(\tilde{A}\), i.e. \(\tilde{D}_{ii} = \sum_j \tilde{A}_{ij}\).
  • \(\hat{A} = \tilde{D}^{-1/2} \tilde{A} \tilde{D}^{-1/2}\) — the symmetrically normalised propagation matrix. This is the object the layer actually multiplies by; \(\tilde{A}\) alone is not normalised.
  • \(W^{(l)} \in \mathbb{R}^{F_l \times F_{l+1}}\) — the learned weight matrix for layer \(l\).
  • \(\sigma\) — an elementwise activation function (ReLU).

Throughout this post \(\tilde{A}\) always means \(A + I\) and \(\hat{A}\) always means the normalised propagation matrix — they are two different matrices.

Breaking It Down Step by Step

Let’s trace what happens for a single node \(v\) with neighbours labelled A, B and C (plain labels — not to be confused with the adjacency matrix \(A\)):

Step 1: Add self-loops. \(\tilde{A} = A + I\) ensures \(v\) attends to itself too, not just its neighbours. Without this, a node’s own features would be excluded from the aggregation.

Step 2: Compute normalisation weights. The weight on the edge \(u \to v\) is

\[ \hat{A}_{vu} = \frac{1}{\sqrt{\tilde{d}_u \, \tilde{d}_v}}, \qquad \tilde{d}_u = \tilde{D}_{uu} = \deg(u) + 1 . \]

This is the symmetric normalisation. It down-weights edges touching high-degree “hub” nodes (which would otherwise dominate).

Step 3: Aggregate. Row \(v\) of \(\hat{A} H^{(l)}\) is a weighted sum of the features of \(v\) and all its neighbours:

\[ \big(\hat{A} H^{(l)}\big)_v = \sum_{u \in \mathcal{N}(v) \cup \{v\}} \frac{1}{\sqrt{\tilde{d}_u \, \tilde{d}_v}} \, h_u^{(l)} \]

Step 4: Transform. Multiply by \(W^{(l)}\): a linear projection to a new feature space.

Step 5: Activate. Apply ReLU (or another non-linearity).

Graph (degrees include the self-loop) A d̃=2 v d̃=4 B d̃=3 C d̃=2 E 1/√(2×4)≈0.35 1/√(4×3)≈0.29 1/√(2×4)≈0.35 self-loop 1/√(4×4)=0.25 GCN aggregation at v: h_v' = σ( W · [ 0.35 · h_A + 0.25 · h_v (self-loop) + 0.29 · h_B + 0.35 · h_C ] ) Hub v (d̃=4) down-weights its own edges: self-loop 0.25 < low-degree neighbour A at 0.35
Figure 1: GCN aggregation at node v. Every weight is \(1/\sqrt{\tilde{d}_u \tilde{d}_v}\), where \(\tilde{d}\) is the degree in \(\tilde{A} = A + I\) (so it counts the self-loop). The normalisation shrinks every edge touching a high-degree node — here the hub v — so no single neighbour dominates, and the self-loop guarantees v's own features are included. Node E is drawn only so that B genuinely has \(\tilde{d}_B = 3\).

Concrete Numerical Example: One GCN Layer

Consider a 3-node path graph \(1 - 2 - 3\), with node features \(h_1 = [2,0]\), \(h_2 = [0,4]\), \(h_3 = [1,1]\).

Step 1 — Add self-loops: \(\tilde{A} = A + I\), so every node gains a self-edge. The degrees of \(\tilde{A}\) are \(\tilde{d}_1 = 2\), \(\tilde{d}_2 = 3\), \(\tilde{d}_3 = 2\).

Step 2 — Compute the entries of \(\hat{A} = \tilde{D}^{-1/2}\tilde{A}\tilde{D}^{-1/2}\):

  • Edge \((1,2)\): \(1/\sqrt{2 \times 3} \approx 0.408\)
  • Edge \((2,3)\): \(1/\sqrt{3 \times 2} \approx 0.408\)
  • Self-loop at node 1: \(1/\sqrt{2 \times 2} = 0.5\)
  • Self-loop at node 2: \(1/\sqrt{3 \times 3} \approx 0.333\)
  • Self-loop at node 3: \(1/\sqrt{2 \times 2} = 0.5\)

Step 3 — Aggregate for node 2 (whose closed neighbourhood is \(\{1, 2, 3\}\)):

\[ \begin{aligned} \big(\hat{A} H\big)_2 &= 0.408 \cdot [2,0] + 0.333 \cdot [0,4] + 0.408 \cdot [1,1] \\ &= [0.816,\, 0] + [0,\, 1.333] + [0.408,\, 0.408] \\ &= [1.224,\, 1.741] \end{aligned} \]

Step 4 — Apply the weight matrix \(W\) and a ReLU:

\[ h_2' = \operatorname{ReLU}\big( [1.224,\, 1.741] \, W \big) \]

The weight matrix \(W\) is shared across all nodes and learned by backpropagation.

Why symmetric normalisation \(1/\sqrt{\tilde{d}_u \tilde{d}_v}\) instead of simple \(1/\tilde{d}_v\)? With row-normalisation \(\tilde{D}^{-1}\tilde{A}\), the aggregation is asymmetric: a message from a high-degree hub is divided by the target's degree only, so the hub is never penalised for broadcasting to everyone. Symmetric normalisation divides by both endpoints' degrees — a high-degree sender's outgoing messages are down-weighted and a high-degree receiver's incoming messages are down-weighted — which keeps any single neighbour from dominating and, as a bonus, keeps \(\hat{A}\) symmetric with eigenvalues in \((-1, 1]\).

What GCN Is in MPNN Terms

  • MSG: send the neighbour’s features scaled by the normalisation weight, \(\operatorname{MSG}(h_v, h_u) = \hat{A}_{vu} h_u\).
  • AGGREGATE: a weighted sum over the closed neighbourhood \(\mathcal{N}(v) \cup \{v\}\) — exactly what multiplying by \(\hat{A}\) does.
  • UPDATE: apply a single linear layer \(W^{(l)}\) followed by \(\sigma\).

Stacking Layers and the Oversmoothing Problem

With \(k\) GCN layers, each node’s representation captures its \(k\)-hop neighbourhood.

  • \(k = 2\): captures 2-hop information (good for most citation/social graph tasks).
  • \(k \gg 1\): oversmoothing — repeatedly applying \(\hat{A}\) acts like a random walk that converges towards its stationary distribution, so node representations lose their differences and drift towards a common vector, making nodes indistinguishable.

This is GCN’s main limitation. GAT, GPRGNN, and JK-Nets propose various solutions.

✅ Key Takeaways

  • GCN layer: \(H^{(l+1)} = \sigma(\hat{A} H^{(l)} W^{(l)})\), with \(\tilde{A} = A + I\), \(\tilde{D}\) its degree matrix, and \(\hat{A} = \tilde{D}^{-1/2}\tilde{A}\tilde{D}^{-1/2}\) the symmetrically normalised propagation matrix.
  • All neighbours contribute with the same fixed weight \(1/\sqrt{\tilde{d}_u \tilde{d}_v}\) — no attention, no learned weights on edges.
  • Simple and fast: just sparse matrix multiplication + a linear layer.
  • Main weakness: oversmoothing with deep stacks; node embeddings lose their differences as layers are added.