[feat][fsdp] Add QLoRA and Gemma 4 training support - #1968
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for FSDP QLoRA by integrating 4-bit base-weight quantization using bitsandbytes. It adds the necessary configuration options, updates model loading and wrapping logic, handles in-place sharding for 4-bit parameters, and ensures embeddings within wrapped parent modules are not redundantly wrapped. Feedback on the changes includes addressing a regex bug in LoRA exclusions when exclude_modules is empty, optimizing module traversal in apply_fsdp2 to a single pass, and replacing configuration assertions with explicit ValueError exceptions to prevent issues if Python is run with optimization flags.
There was a problem hiding this comment.
Code Review
This pull request introduces support for FSDP QLoRA (4-bit base-weight quantization with LoRA adapters) using bitsandbytes, including configuration options, validation rules, and documentation/examples (specifically for Gemma 4 E2B). It updates model loading and FSDP sharding logic to handle 4-bit quantized weights, and fixes embedding wrapping behavior in FSDP. The reviewer noted that while FSDPPolicyWorkerBase supports 4-bit quantization, FSDPRefWorkerBase in fsdp_worker.py does not yet pass these parameters to HFModelWrapper, which could cause the reference model to load in full precision during RL training and lead to high memory usage or OOM.
Signed-off-by: Bruno Volpato <brunocvcunha@gmail.com>
a4e1a88 to
fbd41bb
Compare
Summary
Add opt-in bitsandbytes 4-bit loading for FSDP policy and reference models so SkyRL can train LoRA adapters against a quantized base model in SFT and RL. Include a runnable Gemma 4 E2B example for small single-GPU experiments.
Changes
model.bitsandbytes_4bitconfig with NF4/FP4 and double-quantization controls.Params4bitthrough the meta device.language_model_onlyLoRA adapters to the language model so text-only vLLM sync excludes audio and vision towers.flash_attnselection and use SDPA plus a Gemma-safe FSDP boundary in the example.qloradependency extra, config tests, wrap-policy regression coverage, and SFT docs.Repro
After accepting the Gemma license on Hugging Face:
Extra config arguments can reduce this to a one-step smoke:
bash examples/train/sft/run_sft_gemma4_e2b_qlora.sh \ 'train_dataset_splits=["train[:1]"]' \ max_length=64 \ num_steps=1 \ disable_cache=true \ num_workers=0Testing
uv run --isolated --extra fsdp --extra dev pytest -q tests/train tests/backends/skyrl_train --ignore=tests/backends/skyrl_train/gpu -m 'not vllm'(1402 passed, 5 skipped, 5 deselectedafter rebasing onto currentmain)pre-commit run --all-files --config .pre-commit-config.yaml(Ruff, Black, secret scan passed)17.6720to0.0127; every gradient was finite.q_a_projtarget reduced loss monotonically from12.4953to11.3287; every gradient was finite.inference-optimization/GLM-5.2-0.8B-A0.8BGRPO QLoRA on a 16 GB RTX 5070 Ti completed 4-bit policy load, vLLM rollout, reward, advantages, FSDP backward, optimizer, and post-update LoRA sync. Absolute advantage was0.7071, gradient norm was0.0449, and process exited 0. Rollout used a test-only dense MLA fallback because native sparse MLA rejects this tiny checkpoint's 192-wide compressed KV head on SM120.all-linearand explicit LoRA targets: 276 and 50 language-model adapters respectively, with zero audio or vision tower adapters.1.0, advantage1.0, policy loss-1.0, and finite grad norm6.78125; process exited 0.unsloth/gemma-4-E2B-itmirror on a 16 GB RTX 5070 Ti: 200 distinct training examples, plus a disjoint 31-example held-out set. Held-out loss fell from4.2274before training to1.1885at step 50 and1.1558at step 100; gradients stayed finite and all optimizer steps completed.(1, 4, 262144).2.7434to0.8364with finite gradients.2.7891before training to1.1250at step 50 and1.1067at step 100.Did this cause any problems?
Rollback by reverting this PR. New behavior is disabled by default.
0.6938over steps 1-4 to0.7147over steps 9-12, with expected sampling noise. Dense MLA required prefix caching disabled because vLLM v0.23 compressed-cache gather kernels do not accept this model’s 192-wide head.