feat(qwen-image): bitwise train<->rollout parity via autocast-parity norms and a pinned flash kernel - #108
Open
zhihengy wants to merge 1 commit into
Open
feat(qwen-image): bitwise train<->rollout parity via autocast-parity norms and a pinned flash kernel#108zhihengy wants to merge 1 commit into
zhihengy wants to merge 1 commit into
Conversation
…norms and a pinned flash kernel Dump-verified on the reduced pickscore GRPO smoke (2 samples x 2 SDE steps x cond/uncond, LoRA r64 step-0): all 27 checked module outputs are bit-exact across all 8 paired forwards, including the root DiT output. Three independent divergences fixed: 1. Fused-norm rounding points: the sgld LayerNormScaleShift patch rounds the LN output to bf16 and modulates in bf16 eager (pre-#103 train semantics). Under #103's ambient autocast the train side keeps fp32 through LN+modulation and rounds once at the next matmul input, leaving rel ~1.4e-3 at every norm site. New rollout patch group "qwen_image" (patch_qwen_image_autocast_norms) re-patches LayerNormScaleShift + ScaleResidualLayerNormScaleShift with the autocast rounding points; it is declared per-family so the recorded SD3/Wan standards keep the sgld semantics. 2. Attention kernel: rollout's torch_sdpa executes SDPA flash bitwise, while the train-side diffusers default dispatch picks a different kernel. The aligned recipe now pins --fsdp-attention-backend _native_flash (verified fwd+bwd under --deterministic-mode). 3. Identity attention mask: collate always passed encoder_hidden_states_mask; an all-True mask is the identity but makes the mask-less flash path raise, and rollout always runs mask-free batch-1 requests. Drop the mask when mask.all() (bitwise no-op). Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Full-stack precision check of the Qwen-Image GRPO pipeline (FSDP2 train actor vs sglang-d rollout engine) with the dumper toolchain, then fixes until every paired module output is bit-exact: 27 checkpoints × 8 paired forwards (2 samples × 2 SDE steps × cond/uncond, LoRA r64 at step 0) all 0-ULP, including the root DiT output; the full record sweep shows 106/106 same-dtype pairs identical.
Starting point was final-output rel err ≈ 4.5e-3 per forward. Three independent root causes, in execution order:
Fused-norm rounding points (first divergence, every
*_norm1/*_norm2site, rel ~1.4e-3). ThesgldLayerNormScaleShiftpatch rounds the LN output to bf16 and modulates in bf16 eager — the pre-refactor(fsdp)(4.3/5): family-declared input dtype policy at the model boundary #103 train semantics. Since refactor(fsdp)(4.3/5): family-declared input dtype policy at the model boundary #103 the train side runs under ambient autocast:nn.LayerNormcomputes and returns fp32,(1 + scale)rounds once in bf16, the modulation promotes to fp32, and the single bf16 rounding happens at the next matmul input. One extra rounding on the rollout side broke parity.Fix: new rollout patch group
qwen_image(monkey_patches/patch_qwen_image_autocast_norms.py) re-patchingLayerNormScaleShift+ScaleResidualLayerNormScaleShiftwith the autocast rounding points. It is applied aftersgld(family group appends after the CLI group) and declared per-family viaQwenImageTrainPipelineConfig.rollout_patch_group, so the recorded SD3/Wan parity standards (which bake in the oldsgldsemantics) are untouched.Attention kernel mismatch. Offline arbitration on dumped q/k/v proved the rollout (
--sglang-attention-backend torch_sdpa) executes SDPA flash bitwise, while the train-side diffusers default dispatch picks a different kernel (max |Δ| ~2.5e-1 vs flash on H200).Fix: pin
--fsdp-attention-backend _native_flashin the aligned pickscore recipe (verified fwd+bwd works under--deterministic-mode).Identity attention mask blocks flash.
collate_cond_for_sample_batchalways passedencoder_hidden_states_mask; with micro-batch 1 it is all-True (identity), but any non-None mask makes the flash path raise — and the rollout side always runs mask-free batch-1 requests.Fix: drop the mask when
mask.all()(bitwise no-op, unlocks the flash path and matches rollout dispatch).Pre-existing pieces this rests on:
_rebuild_pos_embed_freqs_on_cuda, thesgldop patches, and #92's exact-sigma replay. No dependency on #106 (the Qwen parallel plan stays empty).Verification
prep_align_qwenimage.py validate→ 216/216 bit-exact across all 8 paired forwards; rootoutput0-ULP.to_q/norm_q/attn outputs).tests/fast/backends/sglang_diffusion_utils/test_rollout_patch_groups.pypasses (extended to assert the new group registers); patch-order smoke:qwen_imageapplied aftersgldwins on both norm classes.Test plan
pytest tests/fast/backends/sglang_diffusion_utils/test_rollout_patch_groups.pyrun-diffusion-grpo-pickscore-5gpu-flowgrpo-aligned.shsanity run with the pinned flash backendMade with Cursor