Pre-Norm vs Post-Norm › Learning Rate Sensitivity

Michael BrenndoerferUpdated June 10, 20255 min read
Language AI HandbookAll chapters
Part 15 of 67Transformer Blocks
Chapter 106Pre-Norm vs Post-Norm › Learning Rate Sensitivity
Reading section 11 of 18 · Learning Rate Sensitivity · 5 minSection 11 of 18 · 5 min

Book contents

Section 11 of 18 · 5 min read

Pre-Norm vs Post-Norm › Learning Rate SensitivityOne section · Sections in this chapter11 / 18 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Post-norm architectures are notoriously sensitive to learning rate choices. Large learning rates can cause training to diverge, necessitating careful warmup schedules.

Pre-norm is more forgiving because the direct gradient path through residual connections prevents gradient explosion. This allows training with larger learning rates and simpler schedules.

Out[23]:
Visualization
Line plot showing post-norm training loss with and without warmup, where no warmup causes immediate divergence.
Post-norm training requires warmup. Without warmup (orange), training diverges immediately at the target learning rate. With warmup (blue), the gradual learning rate increase allows the model to find a stable optimization trajectory.
Line plot showing pre-norm training loss with and without warmup, where both converge successfully.
Pre-norm handles aggressive learning rates. Both with warmup (blue) and without (orange), training proceeds stably. The gradient highway ensures that even large initial updates don't cause divergence.

The simulated training curves illustrate the practical difference. Post-norm without warmup experiences immediate instability: the gradient explosion causes the loss to spike and training to fail. With warmup, the gradual learning rate increase gives the optimizer time to find a stable path. Pre-norm, by contrast, handles both scenarios gracefully because the gradient highway prevents the cascading instabilities that plague post-norm.

In[24]:
Code
def simulate_lr_sensitivity(norm_type, learning_rates, n_steps=200):
    """
    Simulate training stability across learning rates using loss dynamics.
    Returns a stability score: 1.0 = fully stable, 0.0 = diverged.
    Post-norm is sensitive to large LRs because the LN Jacobian gates all
    gradient paths; pre-norm's residual highway tolerates larger updates.
    """
    stability_scores = []

    # Post-norm instability threshold: large initial gradients destabilize
    # the LN Jacobian chain. The effective sensitivity scales with LR * n_layers.
    post_instability_threshold = (
        5e-3  # LRs above this cause post-norm instability
    )

    for lr in learning_rates:
        rng = np.random.default_rng(42)
        loss = 4.0
        losses = []
        diverged = False

        for step in range(n_steps):
            # Post-norm: large LR causes gradient explosion through LN Jacobian chain
            if norm_type == "post" and lr > post_instability_threshold:
                # Instability grows with LR; higher LR means faster divergence
                instability_factor = lr / post_instability_threshold
                noise = rng.standard_normal() * lr * 500 * instability_factor
                loss = loss + abs(noise) * 0.3
                if loss > 15.0:
                    diverged = True
                    break
            else:
                # Stable convergence for pre-norm (all LRs) and
                # post-norm at low-to-moderate LRs
                decay = 1.0 - lr * 5
                decay = max(0.990, min(0.999, decay))
                loss = max(0.3, loss * decay + rng.standard_normal() * 0.02)

            losses.append(loss)

        if diverged or len(losses) == 0:
            stability_scores.append(0.0)
        else:
            final_loss = (
                np.mean(losses[-20:]) if len(losses) >= 20 else np.mean(losses)
            )
            stability_scores.append(1.0 / (1.0 + final_loss))

    return stability_scores


learning_rates = [1e-5, 1e-4, 1e-3, 5e-3, 1e-2, 5e-2]
pre_stability = simulate_lr_sensitivity("pre", learning_rates)
post_stability = simulate_lr_sensitivity("post", learning_rates)
Out[25]:
Visualization
Bar chart comparing stability scores for pre-norm and post-norm across learning rates from 1e-5 to 5e-2.
Learning rate sensitivity comparison. Pre-norm (blue) maintains stability across a wider range of learning rates, while post-norm (orange) becomes unstable at higher learning rates, requiring more careful hyperparameter tuning.

The stability comparison shows that pre-norm tolerates higher learning rates. This translates to faster convergence in practice, as larger learning rates allow taking bigger optimization steps.