feat(fsdp2): load model on meta device for non-rank0 ranks (0 CPU RAM per worker) - #9982
Open
cben484 wants to merge 2 commits into
Open
feat(fsdp2): load model on meta device for non-rank0 ranks (0 CPU RAM per worker)#9982cben484 wants to merge 2 commits into
cben484 wants to merge 2 commits into
Conversation
transformers 5.x has no rank0-only zero-memory loading path: its FSDP non-rank0 branch materializes the full model as CPU zeros per rank (~model-size host RAM each). On hosts that budget CPU RAM per device, N x model-size inevitably OOM-kills workers during loading/prepare. Let non-rank0 ranks load under init_empty_weights() with ACCELERATE_USE_FSDP temporarily masked (to bypass the CPU-zeros branch), and let accelerate's cpu_ram_efficient_loading distribute rank0 weights during prepare. Validated on 16x Ascend 910B (Qwen3.5-35B-A3B, LoRA DPO/KTO): - host RAM peak: 187G total (rank0 only), independent of world size (was ~2.2T with 16 ranks full-loading -> global OOM kill) - NPU HBM: 19.5G/card (DPO) / 20.9G/card (KTO) at 16 cards - identical training metrics vs full loading (DPO loss start 0.6914=ln2, logps/chosen -129.0 vs -128.8) Requires modelscope#9980 as prerequisite. Disable with SWIFT_FSDP2_META_LOADING=0.
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.
Problem
With
--fsdp fsdp2, transformers 5.x has no rank0-only zero-memory loading path. Infrom_pretrained, the FSDP non-rank0 branch (transformers/modeling_utils.py,_move_missing_keys) materializes every parameter withtorch.zeros_like(param, device="cpu")— i.e. each rank allocates the full model in CPU RAM (a 35B bf16 model ≈ 67G per rank, ~136G peak including prepare-time copies). On hosts that budget CPU RAM per device (e.g. 125G per accelerator),N x 136Ginevitably OOM-kills workers during loading/prepare, regardless of world size.Measured on 16x Ascend 910B (2T host RAM, Qwen3.5-35B-A3B): 16 workers x ~136G anon RSS ≈ 2.2T → kernel global OOM kill during prepare (dmesg evidence captured).
Solution
Only rank 0 needs real weights; accelerate's
cpu_ram_efficient_loadingpath (accelerate/utils/fsdp_utils.py: "Ifcpu_ram_efficient_loadingis enabled, only rank 0 loads the weights") already distributes rank-0 weights to all ranks duringprepare. This PR makes non-rank0 ranks load on the meta device and keeps the model there until accelerate takes over:_fsdp2_use_meta_loading()— enabled when FSDP2 + world_size>1, disable viaSWIFT_FSDP2_META_LOADING=0get_model_processor(): non-rank0 loads underinit_empty_weights(), withACCELERATE_USE_FSDPtemporarily masked during the loading windowTwo details that matter:
ACCELERATE_USE_FSDPinside the loading window is essential: transformers' non-rank0 branch would otherwise turn meta params back into CPU zeros (full model in host RAM per rank), silently defeating the optimization.device_mapis resolved earlier in the same function, so masking at this point is safe.prepare: we prototyped a per-parameterbroadcast(p.to_local(), src=0)hook onTrainer._prepare_for_trainingand it deadlocks HCCL collective ordering (HcclAllGatherdispatch timeout), because accelerate's own sync already runs insideprepare. The official path handles everything — this PR deliberately contains no sync code.Verification (16x Ascend 910B 64G, ms-swift 4.4.2, Qwen3.5-35B-A3B LoRA)
DPO and KTO smoke tests both pass end-to-end at full 16-card world size.
Requires #9980 (
ACCELERATE_USE_FSDPfix) as prerequisite — without it,device_map='npu:{rank}'forces full per-device NPU loading before this code path is even reached.