DeepSpeed
8af59b2e - Restore fp32 masters for ZeRO-3 and the ZeRO-1 BF16 optimizer (#8892)

Commit
2 days ago
Restore fp32 masters for ZeRO-3 and the ZeRO-1 BF16 optimizer (#8892) ## Summary - Follow-up to #8891 (merged as `2dd22dc`). The delta against `master` is ZeRO-3 and the dedicated ZeRO-1 BF16 optimizer, plus the review fixes listed below. - `load_checkpoint` still always restores saved fp32 masters. `load_optimizer_states=False` keeps those masters and starts Adam with fresh moments. `load_module_only=True` rebuilds the dedicated BF16 optimizer from the loaded bf16 weights. ZeRO-3 still reads its fp32 partition on a module-only load, because the model checkpoint does not hold the partitioned weights. - `ZeRO-3` and `BF16_Optimizer.load_state_dict(..., load_from_fp32_weights=False)` now rebuild the masters from the bit16 weights, matching ZeRO stage 1 and 2. `DeepSpeedEngine.load_checkpoint` passes true, so a normal resume keeps the saved masters. `zero_optimization.load_from_fp32_weights` stays deprecated and ignored. - Review fixes: a pipeline-serialized load now checks that ZeRO shards exist before waiting on the previous rank, so a missing shard raises instead of leaving ranks blocked in `dist.recv`. A universal load that skips optimizer state keeps `optim_fragment`, so `safe_get_full_optimizer_state` and `safe_set_full_optimizer_state` still work. A leftover debug `print` in `BF16_Optimizer` is removed. AI assistance was used to implement and test this change. This does not duplicate an open DeepSpeed PR. ## Test plan - [x] CPU, torch 2.9.1+cu130, Python 3.12.12, Intel Xeon Platinum 8488C, gloo, world size 1, `tests/unit/checkpoint/test_fp32_master_only.py` and `tests/unit/checkpoint/test_fp32_master_stage3_bf16.py`: 10 passed. Both files ran on the tree that became `a22f0cc4`. - The dedicated BF16 optimizer keeps the saved masters when optimizer state is skipped and rebuilds them for `load_module_only=True`. ZeRO-3 keeps the saved masters for both of those loads. Adam moments stay fresh. - A direct `load_state_dict(..., load_from_fp32_weights=False)` after a module-only load rebuilds the masters from the bit16 weights, for both BF16 and ZeRO-3. - Optimizer globals (`clip_grad`, `overflow`, `dynamic_loss_scale`) are not restored on a masters-only load. - A universal masters-only load into a stepped engine keeps `safe_get_full_optimizer_state` and `safe_set_full_optimizer_state` working. - A pipeline-serialized load with missing shards raises before `dist.recv`. The NVMe masters-only copy restores only the fp32 swap file. - [x] 4×H200, torch 2.9.1+cu130, Python 3.12.12, same two files on tree `a22f0cc4`: 10 passed in 52s. This includes the three data-parallel change tests (universal, legacy, elastic), which spawn 2 ranks on `cuda:0`/`cuda:1`. The #8891 suite there does not cover these two optimizers. Validated cells: dedicated ZeRO-1 BF16 optimizer and ZeRO-3, same data-parallel size, legacy checkpoint, bf16, no offload. ZeRO-1/2 universal masters-only load covered by the fragment test. Open cells: fp16, MoE, real NVMe offload (the NVMe test uses a stub swapper), and a data-parallel size change. --------- Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Parents
Loading