-
Notifications
You must be signed in to change notification settings - Fork 663
feat(native): add param-free pre-norm (LNPre/RMSPre) and fold_ln support #1580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,11 +66,59 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.weight * normalized | ||
|
|
||
|
|
||
| class NativeRMSNormPre(nn.Module): | ||
| """Param-free RMSNorm — normalization only, no learnable scale.""" | ||
|
|
||
| def __init__(self, eps: float = 1e-5): | ||
| super().__init__() | ||
| self.eps = eps | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| input_dtype = x.dtype | ||
| x_fp32 = x.to(torch.float32) | ||
| rms_inv = torch.rsqrt(x_fp32.pow(2).mean(dim=-1, keepdim=True) + self.eps) | ||
| return (x_fp32 * rms_inv).to(input_dtype) | ||
|
|
||
|
|
||
| class NativeLayerNormPre(nn.Module): | ||
| """Param-free LayerNorm — center + normalize only, no learnable scale/bias. | ||
|
|
||
| Computes in fp32 for numerical stability, matching NativeRMSNormPre and | ||
| HookedTransformer's LayerNormPre. | ||
| """ | ||
|
|
||
| def __init__(self, eps: float = 1e-5): | ||
| super().__init__() | ||
| self.eps = eps | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| input_dtype = x.dtype | ||
| x_fp32 = x.to(torch.float32) | ||
| x_fp32 = x_fp32 - x_fp32.mean(dim=-1, keepdim=True) | ||
| scale = (x_fp32.pow(2).mean(dim=-1, keepdim=True) + self.eps).sqrt() | ||
| return (x_fp32 / scale).to(input_dtype) | ||
|
|
||
|
|
||
| def _is_param_free_norm(cfg: TransformerBridgeConfig) -> bool: | ||
| """Check if the config specifies a param-free normalization type.""" | ||
| return _normalization_type(cfg) in ("RMSPRE", "LNPRE") | ||
|
|
||
|
|
||
| def _make_norm(cfg: TransformerBridgeConfig, *, force_rms: bool = False) -> nn.Module: | ||
| if force_rms or _uses_rms_norm(cfg): | ||
| norm_type = _normalization_type(cfg) | ||
| is_param_free = _is_param_free_norm(cfg) | ||
|
|
||
| if force_rms or norm_type in ("RMS", "RMSPRE"): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With |
||
| if is_param_free or norm_type == "RMSPRE": | ||
| return NativeRMSNormPre(eps=cfg.eps) | ||
| return NativeRMSNorm(cfg.d_model, eps=cfg.eps) | ||
|
|
||
| if norm_type == "LNPRE": | ||
| return NativeLayerNormPre(eps=cfg.eps) | ||
|
|
||
| if _uses_no_norm(cfg): | ||
| return nn.Identity() | ||
|
|
||
| return nn.LayerNorm(cfg.d_model, eps=cfg.eps) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test cannot fail.
boot_nativeinitializes norms to identity, making the scale-fold a no-op, and the 0.01 loss tolerance hides the logit error the current fold introduces. Can we randomize the norm parameters and compare at the logit level, similar to howtests/unit/test_weight_processing.py:838-877works? It would also be nice to add a LNPre-config case, since that workflow currently crashes.