@@ -352,10 +352,15 @@ def forward(self, x: Tensor, *, paddings: Optional[Tensor] = None) -> Tensor:
352352
353353
354354class RMSNormStateless (BaseNormalizationLayer ):
355- """Stateless version of https://github.com/bzhangGo/rmsnorm."""
355+ """Stateless version of https://github.com/bzhangGo/rmsnorm.
356+
357+ Paper: https://arxiv.org/abs/1910.07467
358+ """
356359
357360 @config_class
358361 class Config (BaseNormalizationLayer .Config ):
362+ """RMSNormStateless.Config."""
363+
359364 # The epsilon.
360365 eps : float = 1e-8
361366 # Cast input to this dtype for the 'forward' call. If None, do not cast.
@@ -364,14 +369,27 @@ class Config(BaseNormalizationLayer.Config):
364369 input_partition_spec : Optional [tuple [Optional [str ]]] = None
365370 # If not None, how to partition output activation values.
366371 output_partition_spec : Optional [tuple [Optional [str ]]] = None
372+ # If True, center the input by subtracting the mean before RMS normalization.
373+ # This variant is used in Gemma and Qwen3-Next and provides better numerical stability.
374+ zero_centered : Optional [bool ] = None
367375
368376 def _forward (self , x : Tensor ) -> Tensor :
377+ """Applies RMS normalization to the input.
378+
379+ Args:
380+ x: Input tensor to normalize.
381+
382+ Returns:
383+ Normalized tensor with the same shape as input.
384+ """
369385 cfg = self .config
370386 x = maybe_shard (x , cfg .input_partition_spec )
371387 x_dtype = x .dtype
372388 if cfg .forward_dtype is not None :
373389 x = x .astype (cfg .forward_dtype )
374390 moment2 = (x * x ).mean (axis = - 1 , keepdims = True )
391+ if cfg .zero_centered :
392+ x = x - x .mean (axis = - 1 , keepdims = True )
375393 x = x * jax .lax .rsqrt (moment2 + cfg .eps )
376394 x = x .astype (x_dtype )
377395 return x
0 commit comments