fix: sanitize non-finite logprobs in vllm async worker - #2962
fix: sanitize non-finite logprobs in vllm async worker#2962yuchenwang3 wants to merge 2 commits into
Conversation
9b450c8 to
eb836d6
Compare
eb836d6 to
922710d
Compare
|
This has been waiting on the copy-pr-bot gate since Jun 26 — could a maintainer |
922710d to
7065be6
Compare
|
@yuki-97 sorry for the direct ping — you're the most recent committer on |
There was a problem hiding this comment.
thanks @yuchenwang3 , fix LGTM.
but @yfw @ananthsub could you help confirm whether this will affect something?
it looks like:
- before: traj with nan logprobs will return http 500 to gym.
- after: will return traj, and replace logprobs nan with 0.
|
Thanks @yuki-97! Unit test added in 3e688cb — covers the float / nested-dict / nested-list branches plus ±inf and non-float passthrough, per your sketch. On the behavior question for @yfw @ananthsub: the intent is that a single non-finite logprob no longer 500s the whole request (which aborts GRPO rollout collection through the Gym proxy). Replacing with 0.0 makes the affected token look more likely under the rollout policy, so any importance-style correction downweights rather than amplifies it — conservative in that direction — and the train-time recomputed logprobs (which dominate the loss) are unaffected. Happy to switch the sentinel or drop only the affected trajectory instead if you'd prefer different semantics. |
|
@yfw @ananthsub bump — unit tests went in three weeks ago per @yuki-97's sketch, CI is green, and the one open item is the behavior question in my Jul 15 comment that's addressed to you two. Could you weigh in so this can move to a decision? |
|
hi @terrykong , could you help take a look? #2962 (review) |
|
@yuchenwang3 sorry for the delay in looking into this. In the past when we've seen nan logprobs, this was usually indicative of a bug in vllm. My concern is this may mask an issue with the inference engine that needs further investigation. Do you have some cases where the nan logprob is expected from vllm? |
|
@yfw fair question — I went digging in the vLLM source instead of hand-waving, and it changed how I'd frame this. vLLM itself already sanitizes non-finite logprobs at the API boundary: the OpenAI serving layer clamps with There's also a documented source of -inf that has nothing to do with engine bugs: And the reason it bites as NaN specifically: On masking: agreed that a NaN can mean an upstream numerics problem worth chasing, and I don't want to hide it. But one bad trajectory 500-ing the endpoint and killing the whole GRPO rollout is the wrong failure mode — same call vLLM's clamp already makes. I can add a LOGGER.warning with a per-request count of replaced values so it stays visible without being fatal. Would that work for you? |
|
Made this runnable to be sure (CPU-only, no GPU needed). The clamp/serialization chain: That ValueError is the exact string from our rollout failures, so the NaN-specific story checks out. For the -inf side I ran vLLM's own i.e. under processed_logprobs, -inf in the payload is just what truncation looks like. The NaN mechanism that fits the ~49K observations — a single overflowed logit is enough: inf - inf = nan in the max-subtraction, so the whole row goes NaN at once. Can share the script if useful; the warning + counter offer above still stands. |
|
@yfw @ananthsub following up on the semantics question: would sanitizing with a single per-request warning and replacement count address the masking concern, or would you prefer the response to fail/drop instead? I have the warning/count version rebased on current main and ready to update once we agree on that behavior. |
3e688cb to
e56338d
Compare
|
Rebased this onto current main, fixed the DCO identity mismatch, and added one warning per response with the number of non-finite values replaced. The call site now wraps the current dynamic-message-fields dump helper. @yfw @ananthsub @terrykong, could one of you run |
e56338d to
219af62
Compare
|
@yfw @ananthsub @terrykong @yuki-97 — I refreshed this onto current main and resolved the only conflict (the new refit-watchdog import and this helper shared the same insertion point; both are preserved). The sanitize + one-warning/count behavior is unchanged. Current head: |
vLLM can return NaN or Inf logprobs, which Starlette JSONResponse rejects because it serializes with allow_nan=False. Sanitize the dumped response before serialization so the rollout request remains valid JSON. Emit one warning with a per-response replacement count so numerical instability remains visible, and cover nested values plus warning behavior with a unit test. Signed-off-by: Yuchen Wang <93700456+yuchenwang3@users.noreply.github.com> (cherry picked from commit e56338d)
219af62 to
0a53d46
Compare
|
/ok to test 0a53d46 |
|
@yfw, following up on your concern about masking inference-engine bugs. The PR now makes replacements observable with a per-response warning and count, but the policy choice still needs your judgment: is sanitize-with-warning acceptable, or should these responses fail/be dropped? Could you confirm the preferred direction before we take this further? Thanks. |
Signed-off-by: Yuchen Wang <93700456+yuchenwang3@users.noreply.github.com>
|
Synced with current main and resolved the async-worker conflicts. The upstream engine-loop adapter and token-capture path are preserved; response sanitization now happens after capture, immediately before JSON serialization. This leaves the original capture input untouched. Changed-file Ruff/format checks pass. Isolated CPU checks cover NaN/Inf serialization, preservation of finite values and capture coordinates, and AST comparison confirming the rest of the upstream async worker is unchanged. These are not full vLLM/Ray integration tests. The sanitize-versus-reject design question remains open; this update does not change that policy. |
vLLM can emit non-finite logprobs. We hit a NaN consistently around 49K context, and vLLM also uses
-infintentionally when sampling filters remove tokens. Starlette'sJSONResponseserializes withallow_nan=False, so any remaining NaN or infinity raisesValueErrorand turns the chat-completion request into a 500.This PR sanitizes the dumped response immediately before JSON serialization:
+inf, and-inffloats with0.0;model_dump_chat_response_with_dynamic_message_fieldsresult onmain.0.0is a conservative sentinel for the rollout importance ratio because the train-time logprob is recomputed. The warning/count is intended to preserve the signal needed to investigate genuine model or vLLM numerical failures.The unit test covers nested dict/list values, all three non-finite forms, finite and non-float passthrough, replacement count, one-warning behavior, and the no-warning finite path. Ruff lint/format, syntax compilation, and DCO pass on the rebased head.
Recent bounded retry and exhausted-rollout handling reduce the blast radius of a 500, but they do not make this response serializable; a deterministic NaN can still fail every retry. This keeps the fix at the source boundary.