@@ -189,5 +189,55 @@ def loss_fn(*, model_params, inputs) -> ForwardOutputs:
189189 )
190190
191191
192+ class TestMinibatchSummaryMerge (test_utils .TestCase ):
193+ """Test that `with_minibatch_steps` correctly merges Summary vs plain scalar summaries."""
194+
195+ @parameterized .named_parameters (
196+ ("two_steps" , 2 ),
197+ ("four_steps" , 4 ),
198+ )
199+ def test_summary_merge_with_plain_scalars (self , steps ):
200+ """Tests that Summary instances use accumulator values while plain scalars use averaging."""
201+ params = dict (w = jnp .asarray ([0.0 , 2.0 , 2.0 , - 3.0 ]))
202+
203+ def loss_fn (* , model_params , inputs ) -> ForwardOutputs :
204+ loss = - jax .nn .log_softmax (model_params ["w" ])[1 ]
205+ output_collection = new_output_collection ()
206+ # A Summary instance — should be accumulated via MetricAccumulator.
207+ output_collection .summaries ["weighted_metric" ] = WeightedSummary (loss , jnp .array (1.0 ))
208+ # A plain scalar — should be accumulated via sum/divide-by-steps.
209+ output_collection .summaries ["plain_scalar" ] = loss
210+ del inputs
211+ return ForwardOutputs (loss = loss , aux = {}, output_collection = output_collection )
212+
213+ forward_key , param_noise_key = jax .random .split (jax .random .PRNGKey (0 ), 2 )
214+ inputs = dict (
215+ input_batch = jnp .ones ((steps * 4 , 8 )),
216+ forward_key = forward_key ,
217+ param_noise_key = param_noise_key ,
218+ )
219+
220+ # Without minibatch (baseline).
221+ out_baseline = loss_fn (model_params = params , inputs = inputs )
222+
223+ # With minibatch.
224+ loss_fn_mb = gradient_accumulation .with_minibatch_steps (
225+ steps = steps , metric_accumulator = MetricAccumulator .default_config ()
226+ )(loss_fn )
227+ out_mb = loss_fn_mb (model_params = params , inputs = inputs )
228+
229+ # The plain scalar should match (sum of identical values / steps == original value).
230+ test_utils .assert_allclose (
231+ out_baseline .output_collection .summaries ["plain_scalar" ],
232+ out_mb .output_collection .summaries ["plain_scalar" ],
233+ )
234+ # The WeightedSummary should be properly accumulated (mean stays the same,
235+ # weight == steps since each minibatch contributes weight=1).
236+ ws = out_mb .output_collection .summaries ["weighted_metric" ]
237+ self .assertIsInstance (ws , WeightedSummary )
238+ test_utils .assert_allclose (ws .mean , out_baseline .loss )
239+ test_utils .assert_allclose (ws .weight , jnp .array (float (steps )))
240+
241+
192242if __name__ == "__main__" :
193243 absltest .main ()
0 commit comments