AI Knowledge Tree

Superposition and polysemanticity

Model why a network with fewer neurons than features stores multiple features per neuron in nearly-orthogonal directions (superposition); show toy models that exhibit the phase transition.

Mechanistic interpretability hopes to find one feature per neuron: "neuron 73 detects banana-ness." In practice, individual neurons are polysemantic: they activate for multiple unrelated concepts (curve detectors, sports, capitalized words, ...).

Superposition (Elhage et al., 2022) provides a principled explanation: a network with dd neurons can represent more than dd features by storing them in nearly-orthogonal directions, accepting small interference costs in exchange for larger representational capacity[1].

The toy model

Consider a network trying to represent n>dn > d sparse features using dd dimensions. Feature ii has value fi≥0f_i \ge 0, occurring with probability pp (sparsity: most features are 0 most of the time).

The model maps features f∈Rn\mathbf{f} \in \mathbb{R}^n to activations h=Wf\mathbf{h} = W \mathbf{f} (encode) and reconstructs as f^=ReLU(W⊤h)\hat{\mathbf{f}} = \text{ReLU}(W^\top \mathbf{h}) (decode), where W∈Rd×nW \in \mathbb{R}^{d \times n}.

The reconstruction loss (with importance weights IiI_i):

L=∑iIiE ⁣[(fi−f^i)2].\mathcal{L} = \sum_i I_i \mathbb{E}\!\left[(f_i - \hat{f}_i)^2\right].

When does superposition occur? Training reveals a phase transition:

  • No superposition (features < neurons): each feature gets its own dimension. Zero interference.
  • Superposition (features > neurons): features are stored in nearly-orthogonal directions. Each feature has some interference with others, but the benefit of storing more features outweighs the cost when features are sparse.

The critical variable is 1−S1 - S where SS is sparsity (probability of being nonzero). Dense features (high 1−S1-S) compete for dimensions; sparse features (low 1−S1-S) happily share directions because they rarely co-occur.

Implementing the toy model

python · numpy
import numpy as np

def relu(x):
    return np.maximum(0, x)

def train_superposition_model(n_features, n_hidden, sparsity, importance_decay=0.9,
                               n_steps=5000, lr=0.01, seed=0):
    """
    n_features: number of features to represent (> n_hidden for superposition regime)
    n_hidden:   dimension of the hidden layer
    sparsity:   probability that each feature is 0
    """
    rng   = np.random.default_rng(seed)
    W     = rng.normal(0, 0.1, (n_hidden, n_features))
    # Feature importances: I_i = importance_decay^i (most important feature = i=0)
    I     = importance_decay ** np.arange(n_features)
    losses = []

    for step in range(n_steps):
        # Sample sparse feature batch (batch_size=256)
        B = 256
        f = rng.uniform(0, 1, (B, n_features))
        mask = rng.random((B, n_features)) > sparsity   # 1 with prob (1-sparsity)
        f *= mask   # sparse features

        # Forward pass
        h    = f @ W.T              # (B, n_hidden): encode
        f_hat = relu(h @ W)         # (B, n_features): decode

        # Loss
        diff  = f - f_hat
        loss  = (I[None, :] * diff ** 2).mean()

        # Backward (manual gradient for W)
        # dL/d(f_hat) = -2 * I * diff / (B * n_features)
        df_hat = -2 * I[None, :] * diff / (B * n_features)
        # f_hat = relu(h @ W), gradient only where h @ W > 0
        mask_relu = (h @ W) > 0
        df_hat_masked = df_hat * mask_relu
        # dL/dW += h.T @ df_hat_masked (contribution from decode)
        # dL/dW += (df_hat_masked @ W.T).T @ f (contribution from encode, chain rule)
        grad_W  = h.T @ df_hat_masked   # (n_hidden, n_features)
        grad_W += (f.T @ (df_hat_masked @ W.T)).T   # encode contribution

        W -= lr * grad_W
        losses.append(loss)

    return W, losses

# Superposition regime: 10 features in 5 dimensions
W, losses = train_superposition_model(n_features=10, n_hidden=5, sparsity=0.7)
print(f"Final loss: {losses[-1]:.5f}")

# Measure interference: ideally columns of W are nearly orthogonal
col_norms = np.linalg.norm(W, axis=0)
W_norm = W / (col_norms[None, :] + 1e-9)
gram = W_norm.T @ W_norm   # (n_features, n_features)
off_diag = gram - np.eye(gram.shape[0])
print(f"Max off-diagonal Gram entry: {np.abs(off_diag).max():.4f}")
print(f"Mean absolute interference: {np.abs(off_diag).mean():.4f}")
print("(Lower = more orthogonal = better superposition)")

The phase transition

The key prediction of the superposition hypothesis: as sparsity increases (features become rarer), a phase transition occurs from the "no superposition" regime to the "superposition" regime. At the phase boundary, the model abruptly starts encoding more features than it has dimensions.

Implications for interpretability:

  • Neurons are not the right unit of analysis (they are polysemantic).
  • Features are directions in activation space, not individual neurons.
  • Finding features requires decomposing the activation space into interpretable directions (which sparse autoencoders do, as the next lesson shows).
Superposition lets a model represent more features than it has dimensions by storing them as spread-out, non-orthogonal directions: five features pack into 2D as a pentagon 72° apart, tolerating the interference because each fires only rarely.
Superposition lets a model represent more features than it has dimensions by storing them as spread-out, non-orthogonal directions: five features pack into 2D as a pentagon 72° apart, tolerating the interference because each fires only rarely.

The geometry of superposition

In the superposition regime, features are stored in directions that form a regular polytope structure:

  • n=d+1n = d + 1 features fit in dd dimensions as vertices of a simplex (all pairwise cosine similarities equal −1/d-1/d).
  • n=2dn = 2d features fit as an antipodal pair arrangement.
  • More features require denser packings.

This geometric structure predicts which features will "collapse" (merge into one direction) when the network must compress more features into fewer dimensions under limited sparsity.

Exercises

Exercise 1. Suppose n=d+1n = d + 1 features are stored as the vertices of a regular simplex in dd dimensions, so all pairwise cosine similarities are equal. Using the fact that the unit vectors sum to zero, derive that the shared cosine similarity is exactly −1d-\tfrac{1}{d}, and evaluate it for d=4d = 4.

Solution

Step 1. Let v1,…,vd+1\mathbf{v}_1, \dots, \mathbf{v}_{d+1} be the unit feature directions of a regular simplex. By symmetry they are centered, so their sum is the zero vector: ∑ivi=0\sum_i \mathbf{v}_i = \mathbf{0}.

Step 2. Take the squared norm of that sum and expand it:

0=∥∑ivi∥2=∑i∥vi∥2+∑i≠jvi⋅vj.0 = \left\|\sum_i \mathbf{v}_i\right\|^2 = \sum_i \|\mathbf{v}_i\|^2 + \sum_{i \ne j} \mathbf{v}_i \cdot \mathbf{v}_j.

Step 3. Each ∥vi∥2=1\|\mathbf{v}_i\|^2 = 1, giving ∑i∥vi∥2=d+1\sum_i \|\mathbf{v}_i\|^2 = d + 1. By symmetry every off-diagonal dot product equals the same value cc, and there are (d+1)d(d+1)d ordered off-diagonal pairs. So

0=(d+1)+(d+1) d c⟹c=−1d.0 = (d + 1) + (d + 1)\,d\,c \quad\Longrightarrow\quad c = -\frac{1}{d}.

Since the vectors are unit length, this dot product is the cosine similarity.

Step 4. For d=4d = 4, the shared cosine similarity is −14=−0.25-\tfrac{1}{4} = -0.25. The simplex spreads the d+1d + 1 directions as far apart as a fixed dimension allows, which is why it is the minimal-interference packing.

Exercise 2. Two features each fire independently with probability pp (so sparsity is 1−p1 - p in the lesson's convention of "probability of being zero" =1−p= 1 - p). They share a single stored direction (collision). Interference costs the model only when both fire at once. Compute the probability of a collision event as a function of pp, and explain why high sparsity (small pp) makes sharing a direction nearly free.

Solution

Step 1. The two features are independent, each on with probability pp. A collision requires both to be on simultaneously, which has probability

P(both on)=p⋅p=p2.P(\text{both on}) = p \cdot p = p^2.

Step 2. When only one of the two fires, the shared direction carries exactly that one feature with no ambiguity, so no interference is paid. The cost is incurred only on the p2p^2 fraction of inputs where both are active and their values superimpose.

Step 3. As sparsity rises (p→0p \to 0), p2p^2 shrinks quadratically, far faster than the linear pp rate at which each feature individually appears. For example at p=0.05p = 0.05 each feature is on 5%5\% of the time, but collisions happen only p2=0.25%p^2 = 0.25\% of the time. The model gains a whole extra feature direction while paying interference on a quarter of one percent of inputs. This quadratic suppression of collisions is exactly why sparse features "happily share directions," and why the superposition regime opens up as sparsity grows.

Exercise 3 (code). Train the lesson's toy model in both regimes and confirm the qualitative prediction: with more features than dimensions and high sparsity, the model packs features into near-orthogonal directions (small off-diagonal Gram entries), whereas with dense features it cannot, and the off-diagonal interference is larger.

Solution
python · numpy
import numpy as np

def relu(x):
    return np.maximum(0, x)

def train(n_features, n_hidden, sparsity, n_steps=4000, lr=0.01, seed=0):
    rng = np.random.default_rng(seed)
    W = rng.normal(0, 0.1, (n_hidden, n_features))
    I = 0.9 ** np.arange(n_features)
    for _ in range(n_steps):
        B = 256
        f = rng.uniform(0, 1, (B, n_features))
        f *= (rng.random((B, n_features)) > sparsity)   # zero with prob = sparsity
        h = f @ W.T
        f_hat = relu(h @ W)
        diff = f - f_hat
        df_hat = -2 * I[None, :] * diff / (B * n_features)
        df_hat *= (h @ W) > 0
        grad_W = h.T @ df_hat + (f.T @ (df_hat @ W.T)).T
        W -= lr * grad_W
    return W

def mean_interference(W):
    norms = np.linalg.norm(W, axis=0)
    Wn = W / (norms[None, :] + 1e-9)
    gram = Wn.T @ Wn
    off = gram - np.eye(gram.shape[0])
    return np.abs(off).mean()

# 10 features, 5 dimensions. Sparse vs dense.
W_sparse = train(n_features=10, n_hidden=5, sparsity=0.9, seed=0)   # rarely co-active
W_dense  = train(n_features=10, n_hidden=5, sparsity=0.1, seed=0)   # usually co-active

intf_sparse = mean_interference(W_sparse)
intf_dense  = mean_interference(W_dense)
print(f"mean |off-diagonal| interference  sparse: {intf_sparse:.4f}")
print(f"mean |off-diagonal| interference  dense:  {intf_dense:.4f}")

# Sparse features pack with less interference than dense ones.
assert intf_sparse < intf_dense

The sparse model achieves a smaller mean off-diagonal Gram entry: because its features rarely co-occur, it can afford to stack ten of them into five dimensions along nearly orthogonal directions. The dense model, whose features collide constantly, pays a higher interference cost and packs less cleanly, the phase distinction the superposition hypothesis predicts.

The next lesson takes the practical consequence of superposition seriously: since features are directions rather than neurons, recovering them calls for a dedicated decomposition tool, the sparse autoencoder.


References

  1. [1]Elhage, N., et al. (2022). "Toy models of superposition". Transformer Circuits Thread. transformer-circuits.pub. Source of the toy model, the phase transition, and the polytope geometry.↩