Skip to content

Commit 3535c92

Browse files
authored
Honor norm_zero_centered_gamma in RMSNormWithInputScale (pytorch#22444)
Summary: rlformers stores gamma offset by -1 when `norm_zero_centered_gamma` is set, so its `RMSNormWithInputScale` computes `rms_norm((weight + 1.0) * x)` (`core/model/transformer.py:208-211`). ET computed `rms_norm(weight * x)` unconditionally, and `norm_zero_centered_gamma` appeared in zero lines of ET — not on `ModelArgs`, not in `model_args_conversion.py`, not in `norm.py`. The flag was silently dropped, so a checkpoint carrying it loaded without error and produced systematically wrong activations from the first block onward. Add `zero_centered_gamma` to `RMSNormWithInputScale` and `RMSNormWithInputScaleCoreML`, add `norm_zero_centered_gamma` to `ModelArgs`, plumb it through `get_et_model_args_from_genai_model_args`, and pass it where `post_ffn_norm` is built. It defaults to False, so no checkpoint without the flag changes behavior. Reviewed By: Zhiwen-Owen-Jiang Differential Revision: D117233461 Pull Request resolved: pytorch#22444
1 parent ade181b commit 3535c92

3 files changed

Lines changed: 34 additions & 6 deletions

File tree

‎examples/models/llama/llama_transformer.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,11 @@ def __init__(
244244
self.post_attn_norm = ScalelessRMSNorm(args.dim, eps=args.norm_eps)
245245

246246
if args.use_ffn_learnable_scales and self.mlp_type != "skip":
247-
self.post_ffn_norm = RMSNormWithInputScale(args.dim, eps=args.norm_eps)
247+
self.post_ffn_norm = RMSNormWithInputScale(
248+
args.dim,
249+
eps=args.norm_eps,
250+
zero_centered_gamma=args.norm_zero_centered_gamma,
251+
)
248252

249253
@classmethod
250254
def from_type(cls, layer_id, args, rope) -> "TransformerBlock":

‎examples/models/llama/model_args.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ class ModelArgs:
187187
use_attn_o_norm: bool = False
188188
use_residual_gate: bool = False
189189
use_ffn_learnable_scales: bool = False
190+
# Zero-centered gamma: the checkpoint stores gamma offset by -1, so the
191+
# effective scale is ``weight + 1``. rlformers applies this in
192+
# RMSNormWithInputScale; ignoring it silently rescales every post-FFN norm.
193+
norm_zero_centered_gamma: bool = False
190194
output_soft_cap_temp: Optional[float] = None
191195
output_linear_intermediate_dim: int = 0
192196

‎examples/models/llama/norm.py‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,28 @@ def forward(self, x):
103103

104104

105105
class RMSNormWithInputScale(torch.nn.Module):
106-
def __init__(self, dim: int, eps: float = 1e-5):
106+
def __init__(self, dim: int, eps: float = 1e-5, zero_centered_gamma: bool = False):
107+
"""RMSNorm with gamma applied to the input: ``rms_norm(gamma * x)``.
108+
109+
Args:
110+
zero_centered_gamma: when True the checkpoint stores gamma offset by
111+
-1, so the effective scale is ``weight + 1``. Mirrors
112+
``RMSNormWithInputScale`` in rlformers' ``core/model/transformer.py``.
113+
"""
107114
super().__init__()
108115
self.eps = eps
109116
self.dim = dim
117+
self.zero_centered_gamma = zero_centered_gamma
110118
self.weight = torch.nn.Parameter(torch.ones(dim))
111119

112120
def forward(self, x):
113-
scaled = self.weight * x
121+
w = self.weight + 1.0 if self.zero_centered_gamma else self.weight
122+
scaled = w * x
114123
return F.rms_norm(scaled, (self.dim,), None, self.eps)
115124

116125

117126
class RMSNormWithInputScaleCoreML(torch.nn.Module):
118-
def __init__(self, dim: int, eps: float = 1e-5):
127+
def __init__(self, dim: int, eps: float = 1e-5, zero_centered_gamma: bool = False):
119128
"""
120129
CoreML-friendly RMSNormWithInputScale.
121130
@@ -139,6 +148,7 @@ def __init__(self, dim: int, eps: float = 1e-5):
139148
)
140149
self.eps = eps
141150
self.dim = dim
151+
self.zero_centered_gamma = zero_centered_gamma
142152
self.weight = torch.nn.Parameter(torch.ones(dim))
143153

144154
def _norm(self, x):
@@ -153,7 +163,8 @@ def _norm(self, x):
153163
)
154164

155165
def forward(self, x):
156-
scaled = self.weight * x
166+
w = self.weight + 1.0 if self.zero_centered_gamma else self.weight
167+
scaled = w * x
157168
return self._norm(scaled)
158169

159170

@@ -197,7 +208,16 @@ def replace_rms_norm_for_coreml_(model: torch.nn.Module) -> torch.nn.Module:
197208
# applies its scale post-norm, which would change the math here).
198209
dim = getattr(mod, "dim", None) or mod.normalized_shape[-1]
199210
eps = getattr(mod, "eps", 1e-6) or 1e-6
200-
new = RMSNormWithInputScaleCoreML(dim, eps=eps)
211+
# Carry `zero_centered_gamma` across. Dropping it here would silently
212+
# downgrade `rms_norm((weight + 1) * x)` to `rms_norm(weight * x)` on the
213+
# CoreML path only -- the same wrong-activation bug this flag exists to
214+
# prevent, and one that would show up as a CoreML-vs-XNNPACK divergence
215+
# rather than as an error.
216+
new = RMSNormWithInputScaleCoreML(
217+
dim,
218+
eps=eps,
219+
zero_centered_gamma=getattr(mod, "zero_centered_gamma", False),
220+
)
201221
new.weight = mod.weight
202222
elif isinstance(mod, (RMSNorm, ScalelessRMSNorm, torch.nn.RMSNorm)):
203223
# All three carry the normalized dim either as `dim` or in `normalized_shape[-1]`.

0 commit comments

Comments
 (0)