fix(bagel): rebuild replay KV contexts in eval() so navit keeps inference dispatch - #277
Conversation
|
Closing in favour of #276, which fixes the same bug and was filed first (10:09 UTC vs 10:46 UTC here). Same file, same function — they cannot both land. #276 is also the more accurate account of the failure. The rationale in this PR said the spawning process "legitimately exits after handing off", but that cannot be what happened: The A/B table here also overstates its isolation: the two arms toggled both The empirical data from this branch is still useful and has been carried over to #276 as a review comment: the 4-replica The one behavioural difference: this PR removed PDEATHSIG entirely, whereas #276 keeps it for children created on the parent's main thread. #276's narrower scope is the safer change. |
…ence dispatch
BAGEL FlowGRPO training died in the training forward with
TypeError: Qwen2Model.forward_train() got an unexpected keyword argument
'packed_query_sequence'
Every vendored navit module dispatches forward_train vs forward_inference on
self.training, and the vllm_omni rollout path ships only the prompt text, so
replay rebuilds the three KV contexts trainer-side through the vendored
inferencer's *inference* (packed-query) signature. TrainStack.train_track puts
the model in train() before the gradient-bearing replay (HF gradient
checkpointing is gated on it), so the rebuild arrived in train() mode and the
packed kwargs were routed into forward_train.
rl_ops.forward_flow already forces eval() for exactly this reason and
require_inference_dispatch documents the contract ("replay runs in eval() with
grads enabled"); the context rebuild was the one vendored inference-signature
call in the replay path still missing the guard. Restoring the caller's mode
afterwards is safe: the und/text path is frozen and the block already runs under
no_grad, so no activation-checkpoint recompute revisits it during .backward().
Only the deferred-prompt path is affected. A trainside/colocate rollout carries
opaque contexts, so has_contexts() short-circuits before the rebuild — which is
why this surfaced only on the vllm_omni BAGEL recipe.
Verified together with the PR's first commit on 8xH20, 1 rollout of
diffusion/bagel/bagel_vllmomni forced onto two slabs: rc=0, 4/4 engines booted,
zero worker deaths, zero navit dispatch errors — a full generate -> reward ->
train step.
1c20d98 to
81ca0c2
Compare
Same behaviour, less machinery. nn.Module.train() takes the mode, so train(was_training) restores both cases and neither `if was_training` is needed -- matching the idiom already used in unirl/train/stack/base.py. Comment trimmed to the constraint the code cannot show; the full root-cause narrative lives in the PR description.
GPU end-to-end: PASS on clean
|
|
Post-merge review note (evidence hygiene, the fix itself checks out): the Test Plan line citing One real follow-up it surfaced: |
Summary
BAGEL FlowGRPO died in the training forward with:
Every vendored navit module dispatches
forward_trainvsforward_inferenceonself.training. The vllm_omni rollout ships only the prompt text (KV caches cannot cross the worker->driver IPC boundary), so replay rebuilds the three KV contexts trainer-side through the vendored inferencer's inference (packed-query) signature.TrainStack.train_trackputs the model intrain()before the gradient-bearing replay so HF gradient checkpointing engages, so the rebuild arrived intrain()mode and the packed kwargs were routed intoforward_train.rl_ops.forward_flowalready forceseval()for exactly this reason, andrequire_inference_dispatchdocuments the contract ("replay runs in eval() with grads enabled") — the context rebuild was the one vendored inference-signature call in the replay path still missing the guard. Restoring the caller's mode afterwards is safe: the und/text path is frozen and the block already runs underno_grad, so no activation-checkpoint recompute revisits it during.backward().Only the deferred-prompt path is affected; a trainside/colocate rollout carries opaque contexts and
has_contexts()short-circuits before the rebuild, which is why this surfaced only on the vllm_omni BAGEL recipe.Related Issue
N/A
Test Plan
Ran on one 8xH20 pod with
main's own recipe forced onto two slabs:Result: a complete
generate -> reward -> trainstep,navit_dispatch_errors=0, optimizer step finishing.Reaching the training forward at all on this recipe additionally requires #276, which fixes an unrelated
PR_SET_PDEATHSIGbug that SIGKILLs healthyDiffusionWorkers before the firstgenerate. The acceptance run above was executed with that fix also applied. This PR is independent of it and touches a disjoint file.ruff check/ruff formatclean, and the full pre-commit suite (includingcheck-recipe-targets) passes via the pre-push hook.Compatibility / Risk
No config, checkpoint, data-format or API change. One behavioural change confined to one function: the
eval()guard is scoped to the frozen,no_gradcontext rebuild and restores the caller's mode, so gradient checkpointing for the trained surface is untouched.Reviewer Notes
This PR was originally filed bundled with a
PR_SET_PDEATHSIGfix inunirl/rollout/engine/vllm_omni/patches/runtime.py. That half has been dropped and the branch rewritten (force-push1c20d989->81ca0c23), because #276 fixes the same bug, was filed first, and carries the correct root-cause account. See the discussion on #276 for the kernel probe that settles it.Checklist