[fix][metrics] Reduce policy_entropy and logprob-diff metrics via sum/count - #1969
[fix][metrics] Reduce policy_entropy and logprob-diff metrics via sum/count#1969ayucpp wants to merge 2 commits into
Conversation
…/count The `policy_entropy` and `minibatch_rollout_logprobs_abs_diff_*` metrics were reduced as mean-of-means: workers emitted per-micro-batch masked means, which were then averaged across micro-batches, DP ranks, and mini-batches. With token-based batching (or any imbalanced sharding) that over-weights under-sampled shards and no longer equals the metric computed on the global mini-batch. Emit the masked per-token sums (`_sum`, `_sq_sum`) and their counts (`_nnz`) instead. `reduce_metrics` / `all_reduce_metrics` always sum these keys, and the mean/std are derived once at the final step: - policy_entropy = policy_entropy_sum / loss_mask_nnz - logprob-diff mean = sum/nnz, std = sqrt(E[x^2] - E[x]^2) Workers keep emitting a final `policy_entropy` after the DP all-reduce so existing consumers (worker-level tests, Tinker `_extract_metrics`) work unchanged; the raw keys survive so the trainer re-derives the exact global value after mini-batch aggregation. Megatron packed entropy helpers now return the global sum/count (all-reduced across CP), keeping CP invariance. Signed-off-by: ayushflux <ayushflux@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request refactors the calculation of policy entropy and rollout logprob difference metrics to prevent "mean-of-means" inaccuracies when dealing with imbalanced shards. Instead of averaging means at the worker level, the code now tracks and reduces raw masked sums and non-zero counts (nnz) across micro-batches, DP ranks, and mini-batches, performing a single final division at the trainer level. A critical issue was identified in megatron_model_wrapper.py where entropy_sum and entropy_nnz are returned as PyTorch tensors in packed sequence branches but as Python floats in non-packed branches. This type mismatch can cause the metrics to be silently skipped during the reduction step, so calling .item() on the tensors is recommended to ensure they are properly logged.
| POLICY_ENTROPY_SUM_KEY: entropy_sum, | ||
| LOSS_MASK_NNZ_KEY: entropy_nnz, |
There was a problem hiding this comment.
In the packed sequence branches (e.g., when calling vocab_parallel_entropy_packed_sequences or from_parallel_hidden_to_entropy_packed_sequences), entropy_sum and entropy_nnz are returned as torch.Tensors on GPU. However, in the non-packed branches (using compute_masked_sum_and_count), they are returned as Python floats.\n\nIf they are torch.Tensors, they will be added to the metrics dictionary as tensors. Later, when reduce_metrics is called, it checks isinstance(x, (int, float)) for each metric value. Since torch.Tensor is not an instance of int or float, these metrics will be silently skipped and omitted from the reduced metrics, preventing policy_entropy from being finalized and logged.\n\nTo fix this, we should call .item() on entropy_sum and entropy_nnz if they are PyTorch tensors.
POLICY_ENTROPY_SUM_KEY: entropy_sum.item() if torch.is_tensor(entropy_sum) else entropy_sum,\n LOSS_MASK_NNZ_KEY: entropy_nnz.item() if torch.is_tensor(entropy_nnz) else entropy_nnz,There was a problem hiding this comment.
Fixed in ac1318c — I normalized to Python floats right after the branch chain (entropy_sum = float(entropy_sum); entropy_nnz = float(entropy_nnz)), which covers both the packed branches (tensors) and compute_masked_sum_and_count (already floats) in one place. float(tensor) on a scalar tensor is equivalent to .item(), so the metrics now survive the isinstance(x, (int, float)) check in reduce_metrics. Full CPU suite: 1393 passed, no regressions.
The packed entropy helpers return detached GPU tensors while compute_masked_sum_and_count returns Python floats. reduce_metrics skips any value that is not an int/float, so tensor-valued policy_entropy_sum / loss_mask_nnz were silently dropped and policy_entropy was never finalized for packed megatron runs. Signed-off-by: ayushflux <ayushflux@gmail.com>
Fixes #1822
What does this PR do?
Updates metric aggregation for
policy_entropyandminibatch_rollout_logprobs_abs_diff_*to compute the exact global mean instead of a mean-of-means.Why
Workers reported per-micro-batch masked means, averaged across micro-batches, DP ranks, and mini-batches — over-weighting shards with few unmasked tokens.
Example: Shard A = 1 token (entropy 1.0), Shard B = 100 tokens (entropy 2.0):
Token-based batching (or DP workers getting mostly failed trajectories) makes shards imbalanced, so the old value wasn't the global-batch metric.
What changes
policy_entropy_sum,minibatch_rollout_logprobs_abs_diff_sum/_sq_sum) and counts (loss_mask_nnz,..._nnz);reduce_metrics/all_reduce_metricsalways sum these.policy_entropyafter the DP all-reduce, so Tinker_extract_metricsand worker-level GPU assertions are unchanged.Testing
test_worker_utils.pyandtest_skip_fwd_logprobs.py.-m "not vllm").