[rl] Add trainer entropy metric#3848
Conversation
529a2bb to
b4ae6ec
Compare
| logits = _gather_vocab_logits(logits) | ||
| logprobs = _logprobs_from_full_vocab(logits, labels) | ||
| with torch.no_grad(): | ||
| logits_fp32 = logits.float() |
There was a problem hiding this comment.
This way you'd have two .float() conversion, one in logprobs, one for entropy. Have you thought about that?
There was a problem hiding this comment.
good point, we shouldn't keep two copies of float32 logits, we can share one.
| ).reshape(B, L) | ||
|
|
||
|
|
||
| def compute_logprobs( |
There was a problem hiding this comment.
Tow much value is it to keep this standalone? There are two other options so that we don't have to introduce many subfunctions:
- remove this, and only use the combined function
- having a flag e.g.
return_entropyto if-else
curious what's the tradeoff in your mind?
There was a problem hiding this comment.
compute_logprobs is only used in some test scripts that we have under experimental/rl. I think combining these two functions(second option) looks better.
| """Compute per-position logprobs from logits and labels. | ||
| *, | ||
| return_entropy: bool = False, | ||
| ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]: |
There was a problem hiding this comment.
The other option is to return logprobs, None when return_entropy=False. Not sure which is better.
Update: OK claude prefers o/w, so no need to change
|
@claude review this PR, including my comments |
|
Claude finished @tianyu-l's task in 2m 45s —— View job PR Review: [rl] Add trainer entropy metricOverall this is a clean, well-scoped change. The entropy math is correct, the single-upcast refactor preserves logprob numerics bit-for-bit, and the Responses to the inline comments1. Two 2. Standalone helper vs. combined function — the 3.
Switching to an always-tuple return would silently break those ( FindingsA. The unit test claimed in the test plan isn't in the diff. B. (minor, optional) Extra full-size allocation in the entropy path. with torch.no_grad():
logp = torch.log_softmax(logits, dim=-1)
entropy = -(logp.exp() * logp).sum(dim=-1)Not blocking — the current form is correct and readable. Things I confirmed are correct
Nothing blocking beyond adding the unit test (A). Nice change. |
Summary
Add a trainer-policy entropy metric to watch for entropy -> reward collapse during RL (motivated by checking whether MoE / Qwen3-30B-A3B runs collapse), and surface the existing train/inference KL signal.
Why
Entropy collapse is the leading indicator of reward collapse in GRPO/DAPO: the policy sharpens to near-deterministic -> within-group reward variance -> 0 -> zero advantage -> no gradient. Entropy + KL on the dashboard catch this early.
What
compute_logprobs(..., return_entropy=True)returns per-token entropy alongside logprobs from a single vocab gather + single fp32 upcast (no extra copy); default call is unchanged (returns logprobs).trainer/entropy/mean(meanH(p)over response tokens); documentsbit_wise/logprob_diff/meanas the k1 estimate of-KL(q||p)(-1xverl'sppo_kl).train/*->trainer/*(grad_norm,lr,policy_version); add entropy to the console keys.Test plan
rl_grpo_qwen3_1_7bsearch_r1 + alphabet_sort);trainer/entropy/meanlogs to W&B with the expected sharpen-then-plateau.