refactor(meta-init): unify the meta-init build/restore path across bundles (fixes garbage RoPE on VeOmni / sharded-FSDP)#207
Open
zzhuoxin1508 wants to merge 7 commits into
Conversation
…init to_empty so self-attn LoRA trains
Collaborator
Author
…it_state Route meta-init bundles through build_meta_init_transformer, drop the fragile model-bound deferred stamp, and stash captures on the bundle so load_trainable_weights restores rope/sincos after to_empty across Ray.
zzhuoxin1508
force-pushed
the
fix/veomni-meta-init-rope
branch
from
July 15, 2026 12:59
37eac2f to
9992e5f
Compare
…ransformer Drop the standalone finalize_meta_init and its unreachable on-meta warning (capture_init_state already raises on meta buffers before it runs). Fold the dtype cast + no-op init_weights stamp directly into build_meta_init_transformer, its sole caller.
…ross bundles Rename qwen3's init_state local to meta_init_state (matching the other 6 bundles) and normalize the bundle._meta_init_state stash comment to the shared 'Ray-robust restore carrier for init-computed non-persistent state.' line.
Fold the two adjacent comments describing the non-persistent buffer restore into one block above the restore_init_state call.
zzhuoxin1508
force-pushed
the
fix/veomni-meta-init-rope
branch
from
July 16, 2026 05:24
1151eb7 to
420dca7
Compare
Collapse the now-short AutoModelForCausalLM.from_config lambda onto one line.
zzhuoxin1508
marked this pull request as ready for review
July 16, 2026 06:01
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
This started as a bug fix and turned into a refactor of the meta-init machinery.
Root cause (unchanged): under the meta-init path (
meta_init_transformer=true,used by the VeOmni / sharded-FSDP
load_shardedbackends) several bundles built thetransformer on
torch.device("meta"), which forces non-persistent buffers ontometa too — exactly the init-computed tables the checkpoint does not carry
(
WanRotaryPosEmbed.freqs_cos/freqs_sin, SD3's sincospos_embed, rope tables).After
to_empty()these hold garbage, and the no-opinit_weightsnever rebuildsthem.
For WAN 2.1 this silently breaks RoPE: self-attn q/k get multiplied by junk cos/sin.
Because the multiply is by ~zero garbage, the gradient to the
attn1.to_q/attn1.to_kLoRA adapters is killed — those never train whileattn2/v/outtrain normally, so LoRA-RL reward plateaus below the classic-FSDP baseline.What changed (refactor)
The base already restored captured state via
bundle._meta_init_stateinload_trainable_weights— but only bundles that actually captured benefited, andthe capture path was split across two mechanisms. This PR unifies it:
unirl/models/types/meta_init.pybuild_meta_init_transformer(factory, *, dtype): builds underaccelerate.init_empty_weights(include_buffers=False)(params on meta,non-persistent buffers/attrs real on CPU), captures them via
capture_init_state, casts dtype, and no-opsinit_weights. Returns(transformer, captured).finalize_meta_init(folded into the above) andstamp_init_state_restore(the model-bound deferred-closure restore, which canbe dropped across Ray-actor boundaries — the bundle carrier already covers it,
robustly).
capture_init_state/restore_init_state.wan21,sd3,qwen3,qwen_vl,flux2_klein,hunyuan_video,hunyuan_video15) now build viabuild_meta_init_transformerand stash the capture on
bundle._meta_init_state.load_trainable_weightsreplaysrestore_init_state(model, bundle._meta_init_state)after the sharded weight load(Ray-robust — a plain picklable dict survives crossing actors).
unirl/train/backend/sharded_load.py— comment cleanup only.Net: 9 files, +128 / −190 — a single build/restore path, less code.
Related Issue
N/A
Test Plan
WAN 2.1-T2V-1.3B FlowGRPO, VeOmni backend, 720p, 2×8 GPU:
curve in comment
SD3.5-medium FlowGRPO, VeOmni backend, 8 GPU (
diffusion/sd3_trainside_veomni):Compatibility / Risk
Behavioral change only on the
meta_init_transformer=truepath (VeOmni / shardedFSDP). The classic-FSDP
from_pretrained(eager) path is untouched. Noconfig/checkpoint/API format changes. Models with no init-computed non-persistent
buffers are unaffected —
restore_init_stateis a no-op when the capture is empty.Reviewer Notes
AI-assisted. Root-caused and validated end-to-end on WAN 2.1 (reward alignment +
gradient map). The capturing build path lives in the shared
build_meta_init_transformer, so every meta-init model (e.g. SD3's sincospos_embed) gets the same restore for free.Checklist