Repository navigation
refactor(trainer): one gradient manager for GPU and offload gradients - #3844
Closed
faresobeid wants to merge 1 commit into
Closed
faresobeid wants to merge 1 commit into
faresobeid wants to merge 1 commit into
Conversation
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
utils.pyhad five wrappers (prepare_gradient_offload,begin_backward,finish_backward,scale_gradients_,clip_grad_norm_) whose only job was to branch ongradient_manager is None. SFT also had two scaling paths: one with offload and one without.This PR:
GPUGradientManager(optim/base.py). It has the samebegin_step/begin_backward/finish_backward/clip_grad_norm_/closeinterface asGradientOffloadManager. It applies the step's gradient scale after the final backward of the step.setup_optimizeralways return a manager.modelis now a required argument.GradientOffloadManager.scale_and thewait_for_copiesargument offinish_backward. Nothing used them: the wrapper always passedFalse.Behavior change
None. One side effect: SFT without offload now keeps the
grad_accumCPU micro-batches of a step in memory. The offload path already did this.What I ran
pytest tests/unit/orchestrator tests/unit/test_configs* -m "not gpu": 291 passedpytest tests/unit/train/rl tests/unit/train/sft tests/unit/train/test_state_offload.py tests/unit/train/test_cpu_adam.py tests/unit/train/test_perf.py tests/unit/train/test_world.py -m "not gpu": 93 passed, 1 failed. The failure istest_native_cpu_adamw_matches_fused_torch_and_preserves_gradients, which needsninjato build the C++ extension. It fails the same way on main in this environment.ruff checkandruff format --checkon the changed filesmax_normNone / 0.05 / 100, for both the SFT and the RL scaling flow. Old wrappers withNone(main) vsGPUGradientManager(branch): the gradients after scale and clip and the returned grad norm are bit-identical in all 12 cases. I also checked that the SFT up-front count (sum(full loss_mask) over dp_cp // cp) equals the old streamed count (sum of CP-shard counts) for cp in {1, 2, 4}.GPU checks to do
max_norm(single node, with and without CP): loss and grad_norm match main for the first steps.max_norm: loss and grad_norm match main.model.full_offload: still run (the manager path is unchanged apart from the removed unused methods).optim_cpu_offload: grad_norm matches main.Conflicts
git merge-treemerges cleanly with all of these. None of them call the removed wrappers.rl/train.py. Nearby hunks: imports and the end oftrain().rl/train.py. Nearby hunks: imports and thecompute_losscall just before the backward block.optim/__init__.py(imports) andsft/train.py(different region).rl/train.py, different regions.Note
Medium Risk
Touches core training-step gradient scaling, clipping, and FSDP normalization in RL/SFT; intended behavior-preserving but affects every optimizer step and distributed grad-norm logic.
Overview
Introduces
GPUGradientManagerso GPU-resident training uses the same step/backward/clip/close API asGradientOffloadManager.setup_optimizernow always returns a manager (GPU path getsGPUGradientManager; full CPU offload still returns the offload manager) andmodelis required.Removes the
trainer/utils.pyhelpers that only branched ongradient_manager is None. RL and SFT callgradient_manager.begin_step/begin_backward/finish_backward/clip_grad_norm_directly; post-step FSDP undo scaling for RL moves intobegin_stepinstead of a separatescale_gradients_pass.SFT drops the dual no-offload vs offload scaling paths: every step prefetches grad-accum micro-batches and computes the global token denominator up front (aligned with RL/offload).
GradientOffloadManagerloses unusedscale_and thewait_for_copiesflag onfinish_backward.Reviewed by Cursor Bugbot for commit 216a8f5. Bugbot is set up for automated code reviews on this repo. Configure here.