You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When top_p < 1, top_k > 0 or min_p is set, GRPO's vLLM importance-sampling correction compares two log-probabilities computed over different distributions, so the ratio is biased. vLLM returns log-probs renormalized over the truncated (nucleus) distribution, while the trainer recomputes them over the full vocabulary.
Two other RL frameworks have now shipped a training-side fix for exactly this, and the native vLLM fix is merged but not yet in a release TRL supports.
Where the mismatch is
Generation side asks vLLM for post-processor log-probs, in both colocate and server mode:
processed_logprobs means "after every logits processor", which includes top-p/top-k truncation followed by renormalization over the surviving tokens. Temperature is handled symmetrically (line 1532 mirrors it, and that symmetry was the fix for #4159); truncation is not. For a sampled token that survives the nucleus, vLLM's log-prob is therefore higher than the trainer's by -log(sum of surviving probability mass), which is a per-token, systematically negative bias in log π_θ - log π_behaviour.
importance_sampling_ratio multiplies the per-token loss directly:
so the effect is a silent, token-dependent down-weighting of the policy gradient rather than a crash.
Scope
Defaults are safe: top_p=1.0, top_k=0, min_p=None, so nothing is truncated and the two distributions agree.
AsyncGRPOTrainer is affected more severely. Server launch passes --logprobs-mode processed_logprobs, and those generator log-probs are not an optional correction there: they are the denominator of the policy ratio itself, with no flag guarding them.
AsyncGRPOConfig exposes top_p/top_k/min_p like GRPO, and it logs no sampling_logp_difference equivalent, so there is nothing to notice the drift with.
Affected as soon as a user sets any of them, which top_p=0.9/top_k=50 recipes sometimes do.
Only active with vllm_importance_sampling_correction=True.
Same logprobs_mode="processed_logprobs" is set in trl/scripts/vllm_serve.py#L361, so server mode is affected identically.
It is already observable
The existing metric spikes under truncation, since it measures precisely this gap:
sampling/sampling_logp_difference/mean and .../max (grpo_trainer.py, _generate_and_score_completions)
A quick check: run GRPO+vLLM with top_p=1.0 and then top_p=0.8, and compare that metric. If it jumps with no other change, the bias is real. Worth doing before we pick a fix.
Prior art
vLLM #49577 "Mask Replay" is merged (2026-08-13) but landed after v0.27.1 (2026-08-11), so it is not in any release covered by TRL's vllm>=0.18.0,<=0.27.1 pin. We cannot rely on it yet.
When
top_p < 1,top_k > 0ormin_pis set, GRPO's vLLM importance-sampling correction compares two log-probabilities computed over different distributions, so the ratio is biased. vLLM returns log-probs renormalized over the truncated (nucleus) distribution, while the trainer recomputes them over the full vocabulary.Two other RL frameworks have now shipped a training-side fix for exactly this, and the native vLLM fix is merged but not yet in a release TRL supports.
Where the mismatch is
Generation side asks vLLM for post-processor log-probs, in both colocate and server mode:
https://github.com/huggingface/trl/blob/main/trl/generation/vllm_generation.py#L365
Training side divides by temperature and takes a full-vocab log-softmax:
https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py#L1530-L1534
processed_logprobsmeans "after every logits processor", which includes top-p/top-k truncation followed by renormalization over the surviving tokens. Temperature is handled symmetrically (line 1532 mirrors it, and that symmetry was the fix for #4159); truncation is not. For a sampled token that survives the nucleus, vLLM's log-prob is therefore higher than the trainer's by-log(sum of surviving probability mass), which is a per-token, systematically negative bias inlog π_θ - log π_behaviour.importance_sampling_ratiomultiplies the per-token loss directly:https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py#L3208-L3209
so the effect is a silent, token-dependent down-weighting of the policy gradient rather than a crash.
Scope
Defaults are safe:
top_p=1.0,top_k=0,min_p=None, so nothing is truncated and the two distributions agree.AsyncGRPOTraineris affected more severely. Server launch passes--logprobs-mode processed_logprobs, and those generator log-probs are not an optional correction there: they are the denominator of the policy ratio itself, with no flag guarding them.AsyncGRPOConfigexposestop_p/top_k/min_plike GRPO, and it logs nosampling_logp_differenceequivalent, so there is nothing to notice the drift with.Affected as soon as a user sets any of them, which
top_p=0.9/top_k=50recipes sometimes do.Only active with
vllm_importance_sampling_correction=True.Same
logprobs_mode="processed_logprobs"is set intrl/scripts/vllm_serve.py#L361, so server mode is affected identically.It is already observable
The existing metric spikes under truncation, since it measures precisely this gap:
sampling/sampling_logp_difference/meanand.../max(grpo_trainer.py,_generate_and_score_completions)A quick check: run GRPO+vLLM with
top_p=1.0and thentop_p=0.8, and compare that metric. If it jumps with no other change, the bias is real. Worth doing before we pick a fix.Prior art
vllm>=0.18.0,<=0.27.1pin. We cannot rely on it yet.Possible directions
vllm_importance_sampling_correction=Trueis combined with truncation, so the bias is at least not silent.I'd like a repro of the metric jump before committing to one.
Possibly related