tolivert's picture
deploy: financial_rag streamlit app
4e316d6
Raw
History Blame Contribute Delete
1.6 kB
"""
LayerNorm (Layer Normalization).
The original normalization technique for transformers (Ba et al., 2016).
LayerNorm normalises each token's hidden state by subtracting the mean and
dividing by the standard deviation, then applies a learnable affine
transform: output = scale * (x - mean) / sqrt(var + eps) + shift.
Compared to RMSNorm (which skips mean-centering and the shift parameter),
LayerNorm has slightly higher compute cost due to the extra mean subtraction
and additive bias. In practice, modern LLMs (LLaMA, Qwen3, Gemma) prefer
RMSNorm because the mean-centering provides negligible benefit in pre-norm
architectures, while the savings add up across dozens of layers.
Included here for educational comparison with RMSNorm.
Computation is done in float32 regardless of input dtype to avoid
numerical instability in half-precision, then cast back.
"""
import torch
import torch.nn as nn
class LayerNorm(nn.Module):
def __init__(self, emb_dim: int, eps: float = 1e-5, dtype=None):
super().__init__()
self.eps = eps
self.scale = nn.Parameter(torch.ones(emb_dim, dtype=dtype))
self.shift = nn.Parameter(torch.zeros(emb_dim, dtype=dtype))
def forward(self, x):
input_dtype = x.dtype
# Force float32 for numerical stability (matches RMSNorm convention)
x = x.to(torch.float32)
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return (self.scale.float() * norm_x + self.shift.float()).to(input_dtype)