Pre-Norm vs Post-Norm › Learning Rate Sensitivity
Language AI HandbookAll chapters 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.


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.
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)
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.