@@ -100,27 +100,29 @@ class _Gemma4ScaleFreeRMSNorm(nn.Module):
100100 Used for V norms in the vision encoder, the vision projector pre-norm,
101101 and the audio pre-projection norm.
102102
103- Implemented as manual ``x / sqrt(mean(x²) + ε)`` rather than
104- ``op.RMSNormalization`` to prevent ORT's graph optimizer from fusing
105- an upstream ``Add(bias)`` into ``SkipSimplifiedLayerNormalization``,
106- which requires the skip tensor to have the same shape as the input
107- (failing when the bias is 1D or has mismatched temporal dimension).
103+ Uses ``op.RMSNormalization`` with ``stash_type=1`` (float32 accumulation)
104+ to handle FP16 overflow: values > 256 squared exceed the FP16 max (65504).
108105 """
109106
110107 def __init__ (self , dim : int , eps : float = 1e-6 ):
111108 super ().__init__ ()
112109 self .dim = dim
113110 self .eps = eps
111+ # Constant all-ones scale (not a learnable parameter from HF).
112+ self .weight = nn .Parameter ([dim ], data = ir .Tensor (np .ones (dim , dtype = np .float32 )))
114113
115114 def forward (self , op : builder .OpBuilder , hidden_states : ir .Value ) -> ir .Value :
116- # Manual RMSNorm: x / sqrt(mean(x²) + ε), scale = 1.0 (scale-free).
117- # Using primitive ops avoids ORT's SkipLayerNorm fusion pattern which
118- # would corrupt the skip shape when an upstream Add uses a 1D bias.
119- square = op .Mul (hidden_states , hidden_states )
120- mean_sq = op .ReduceMean (square , op .Constant (value_ints = [- 1 ]), keepdims = 1 )
121- eps = op .CastLike (op .Constant (value_float = self .eps ), mean_sq )
122- rms = op .Sqrt (op .Add (mean_sq , eps ))
123- return op .Div (hidden_states , rms )
115+ # stash_type=1 means accumulate variance in float32, avoiding
116+ # FP16 overflow when squaring large values.
117+ # CastLike ensures the weight matches the input dtype.
118+ scale = op .CastLike (self .weight , hidden_states )
119+ return op .RMSNormalization (
120+ hidden_states ,
121+ scale ,
122+ axis = - 1 ,
123+ epsilon = self .eps ,
124+ stash_type = 1 ,
125+ )
124126
125127
126128# ---------------------------------------------------------------------------
@@ -773,16 +775,18 @@ def forward(
773775 value_raw = key_raw
774776 else :
775777 value_raw = self .v_proj (op , hidden_states )
776- # Parameterless per-head V normalisation
778+ # Parameterless per-head V normalisation (FP32 accumulation to
779+ # prevent FP16 overflow when squaring values > 256).
777780 value_states = op .Reshape (
778781 value_raw ,
779782 op .Constant (value_ints = [0 , 0 , self .num_key_value_heads , self .head_dim ]),
780783 )
781- sq = op .Mul (value_states , value_states )
784+ v_f32 = op .Cast (value_states , to = ir .DataType .FLOAT )
785+ sq = op .Mul (v_f32 , v_f32 )
782786 mean_sq = op .ReduceMean (sq , [- 1 ], keepdims = 1 )
783787 eps = op .Constant (value_floats = [self ._v_norm_eps ])
784- rms = op .Sqrt (op .Add (mean_sq , op . CastLike ( eps , mean_sq ) ))
785- value_states = op .Div (value_states , rms )
788+ rms = op .Sqrt (op .Add (mean_sq , eps ))
789+ value_states = op .CastLike ( op . Div (v_f32 , rms ), value_states )
786790 value_states = op .Reshape (value_states , [0 , 0 , - 1 ])
787791
788792 # Build GQA attributes
@@ -846,19 +850,21 @@ def forward(
846850 value_raw = key_raw
847851 else :
848852 value_raw = self .v_proj (op , hidden_states )
849- # Parameterless per-head V normalisation
853+ # Parameterless per-head V normalisation (FP32 accumulation to
854+ # prevent FP16 overflow when squaring values > 256).
850855 value_states = op .Reshape (
851856 value_raw ,
852857 op .Constant (value_ints = [0 , 0 , self .num_key_value_heads , self .head_dim ]),
853858 )
854- sq = op .Mul (value_states , value_states )
859+ v_f32 = op .Cast (value_states , to = ir .DataType .FLOAT )
860+ sq = op .Mul (v_f32 , v_f32 )
855861 mean_sq = op .ReduceMean (sq , [- 1 ], keepdims = 1 )
856862 # Use op.Constant to create a 1D tensor node (not a scalar initializer).
857863 # Scalar Python floats use a type-keyed cache that can fail when upstream
858864 # type information is missing (e.g., after custom ops like com.microsoft.MoE).
859865 eps = op .Constant (value_floats = [self ._v_norm_eps ])
860- rms = op .Sqrt (op .Add (mean_sq , op . CastLike ( eps , mean_sq ) ))
861- value_states = op .Div (value_states , rms )
866+ rms = op .Sqrt (op .Add (mean_sq , eps ))
867+ value_states = op .CastLike ( op . Div (v_f32 , rms ), value_states )
862868 value_states = op .Reshape (value_states , [0 , 0 , - 1 ])
863869
864870 attn_output , present_key , present_value = _apply_attention (
0 commit comments