RMSNorm › RMSNorm in Modern Architectures

Michael BrenndoerferUpdated June 7, 20254 min read
Language AI HandbookAll chapters
Part 15 of 67Transformer Blocks
Chapter 105RMSNorm › RMSNorm in Modern Architectures
Reading section 10 of 15 · RMSNorm in Modern Architectures · 4 minSection 10 of 15 · 4 min

Book contents

Section 10 of 15 · 4 min read

RMSNorm › RMSNorm in Modern ArchitecturesOne section · Sections in this chapter10 / 15 ↓
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

RMSNorm has become the normalization of choice for most modern large language models. Understanding how and where it is used in real production architectures helps you read and understand model code, make informed design decisions for new projects, and understand why certain implementation details matter.

The adoption of RMSNorm followed a clear pattern. Early LLMs like GPT-2 and BERT inherited the LayerNorm convention from earlier transformers. As the field moved toward larger models and more efficient training, practitioners began searching for every possible source of speedup. RMSNorm emerged as a clean win: a principled simplification with no quality cost and measurable efficiency gains. Meta's decision to use RMSNorm in LLaMA was highly influential, as the open-source release of LLaMA made RMSNorm the default for a large portion of the research and practitioner community.

Modern transformer architectures place RMSNorm in a pre-normalization configuration, applying it before each sub-layer rather than after. This pre-norm placement is itself a significant architectural choice, separate from the choice between RMS and standard normalization. Pre-norm (normalizing the residual stream before processing) has been shown to provide better gradient flow than post-norm (normalizing after the residual addition), especially for very deep models.

LLaMA Architecture

The LLaMA family of models uses RMSNorm with a specific configuration that handles an important practical concern: numerical precision in mixed-precision training. When a model trains with float16 or bfloat16, the reduced precision can cause problems for normalization computations, especially when inputs have small magnitudes. The solution is to perform the normalization computation in float32 and then cast the result back to the working precision.

In[26]:
Code
class LLaMAStyleRMSNorm(nn.Module):
    """
    RMSNorm as used in LLaMA models.

    Key differences from basic implementation:
    - Uses float32 for normalization even with mixed precision
    - Specific epsilon value
    """

    def __init__(self, dim, eps=1e-5):
        super().__init__()
        self.eps = eps
        self.weight = nn.Parameter(torch.ones(dim))

    def forward(self, x):
        # Store original dtype for mixed precision training
        input_dtype = x.dtype

        # Compute in float32 for numerical stability
        x = x.float()

        # Compute RMS
        rms = torch.sqrt(torch.mean(x**2, dim=-1, keepdim=True) + self.eps)

        # Normalize and scale
        x = x / rms

        # Apply weight and cast back to original dtype
        return (self.weight * x).to(input_dtype)

The key insight here is computing normalization in float32 even when the model uses float16 or bfloat16 for other operations. The reason this matters: RMS values can be small (close to ϵ\epsilon) for some activations, especially early in training or for low-magnitude inputs. In float16, the smallest representable positive number is approximately 6×10−56 \times 10^{-5}, and arithmetic near this boundary introduces significant errors. Computing in float32 avoids this problem while adding only a small overhead.

Pre-Norm Placement

Modern transformers use RMSNorm in a pre-normalization configuration, applying it before each sub-layer rather than after. In the original transformer paper (Vaswani et al. 2017), normalization was applied after the residual connection (post-norm). Subsequent research demonstrated that applying normalization before the sub-layer (pre-norm) leads to more stable gradients and makes the residual stream easier to reason about.

With pre-norm, the forward pass can be written as:

x = x + attention(RMSNorm(x)) x = x + FFN(RMSNorm(x))

The input x\mathbf{x} is normalized before being fed to each sub-layer, but the residual connection bypasses the normalization. This means the residual stream (the running value of x\mathbf{x}) is never itself forced into a normalized distribution. Instead, each sub-layer receives a normalized version of the current residual stream as input, processes it, and adds the result back to the unnormalized residual. This architecture gives the network more flexibility to store information in the residual stream without it being overwritten by normalization.

In[27]:
Code
class ModernTransformerBlock(nn.Module):
    """
    Transformer block with pre-RMSNorm architecture.

    This is the standard configuration for LLaMA, Mistral, etc.
    """

    def __init__(self, dim, n_heads, ffn_mult=4):
        super().__init__()

        # Pre-normalization for attention
        self.attention_norm = LLaMAStyleRMSNorm(dim)

        # Self-attention (simplified)
        self.attention = nn.MultiheadAttention(dim, n_heads, batch_first=True)

        # Pre-normalization for FFN
        self.ffn_norm = LLaMAStyleRMSNorm(dim)

        # Feed-forward network with SwiGLU (simplified to GELU)
        hidden_dim = int(dim * ffn_mult)
        self.ffn = nn.Sequential(
            nn.Linear(dim, hidden_dim),
            nn.GELU(),
            nn.Linear(hidden_dim, dim),
        )

    def forward(self, x):
        # Attention with pre-norm
        normed = self.attention_norm(x)
        attn_out, _ = self.attention(normed, normed, normed)
        x = x + attn_out

        # FFN with pre-norm
        normed = self.ffn_norm(x)
        x = x + self.ffn(normed)

        return x

The pre-norm placement ensures that inputs to each sub-layer are normalized, stabilizing gradients throughout the network. This has become standard practice after research showed it improves training stability, especially for very deep models. Notice that each transformer block has exactly two RMSNorm layers: one before the attention sub-layer and one before the feed-forward sub-layer. There is also typically a final RMSNorm applied to the output of the last transformer block before the language model head.