Deep GNNs & Limitations

Oversmoothing, over-squashing, expressiveness limits, and the fixes that let you go deeper.

1. Oversmoothing

Stack many GNN layers and all node embeddings become nearly identical - the model cannot distinguish nodes.

Math intuition: Repeated normalized adjacency multiplication is like repeated low-pass filtering. The Dirichlet energy $\mathbf{h}^T\mathbf{L}\mathbf{h}$ shrinks toward zero - embeddings become smooth (constant) across the graph.

Intuition: Imagine everyone in a network averaging their opinion with neighbors every day. Eventually everyone says the same thing. That is oversmoothing.

When it hurts: node classification needing local detail, heterophilous graphs. When it helps: semi-supervised learning with very few labels (smoothness prior).

2. Over-Squashing

Information from distant nodes must pass through narrow bottlenecks (low-degree edges). Messages get compressed into fixed-size vectors - exponential information loss.

Intuition: A rumor passing through one person who only speaks in 10-word sentences loses detail fast. Bridge nodes with few connections are those bottlenecks.

Fixes: graph rewiring (add edges), virtual nodes, graph transformers (global attention), subgraph GNNs.

3. Expressiveness and 1-WL

Standard MPNNs are at most as powerful as the 1-Weisfeiler-Lehman graph isomorphism test. They cannot distinguish some non-isomorphic graphs.

GIN achieves this upper bound. To go beyond: subgraph GNNs (NGNN, ESAN), higher-order WL, simplicial complexes.

4. Fixes: JK, Residual, Normalization

from torch_geometric.nn import JumpingKnowledge

class DeepGNN(torch.nn.Module):
    def __init__(self, num_layers, hidden, out_dim):
        super().__init__()
        self.convs = torch.nn.ModuleList([GCNConv(hidden, hidden) for _ in range(num_layers)])
        self.jk = JumpingKnowledge(mode='cat')
        self.lin = torch.nn.Linear(num_layers * hidden, out_dim)

    def forward(self, x, edge_index):
        hs = []
        for conv in self.convs:
            x = conv(x, edge_index).relu()
            hs.append(x)
        x = self.jk(hs)
        return self.lin(x)

5. Heterophily Architectures

When neighbors have different labels, blind aggregation fails. Solutions:

6. How Many Layers?

Graph typeTypical depthReason
Cora-scale citation2Small diameter, homophily
Molecules3–5Need multi-bond receptive field
Large social2–3 + samplingOversmoothing + scale
Long-range reasoningTransformer / rewiringOver-squashing in MPNN