Skip to content

ZeRO-3 partial offload: fix LR schedules and checkpoints of the device sub-groups - #8973

Open
xylian86 wants to merge 2 commits into
deepspeedai:masterfrom
xylian86:xinyu/zero3-partial-offload-optimizer
Open

xylian86 wants to merge 2 commits into
deepspeedai:masterfrom
xylian86:xinyu/zero3-partial-offload-optimizer

Conversation

@xylian86

@xylian86 xylian86 commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

With offload_optimizer.ratio < 1, ZeRO-3 keeps some sub-groups on the device and steps them with a separate torch
optimizer (backup_optimizer). That optimizer has several bugs that change the training result without an error.
SuperOffload uses the same optimizer for its device sub-groups; the rest of SuperOffload is fixed in #8972.

Fixes

  1. The device sub-groups ignored LR schedules and set_lr(), and changes of betas, eps and weight_decay. Ina
    first step at lr 0, the weights moved by 3.0e-3. The backup optimizer now copies these values before each step.
  2. Checkpoints had no Adam state for the device sub-groups (a resume was 4.9e-3 off after one step). It is now saved
    with the state of the CPU sub-groups: a resume is bit-identical, and a checkpoint loads with another ratio.
  3. After a checkpoint load, every device step also updated the CPU sub-groups of param groups 1 and up, because their
    dicts were shared with the main optimizer (3.0e-3 extra per step at lr 3e-3). Each param group of the backup
    optimizer now has its own dict.
  4. safe_get_full_optimizer_state and safe_set_full_optimizer_state raised KeyError for the device sub-groups, and
    ds_to_universal raised KeyError: 0. Both work now.
  5. With adam_w_mode: false, the device sub-groups used decoupled weight decay (2.4e-2 off after 4 steps); they now use
    torch.optim.Adam (L2). bias_correction: false with ratio < 1 now raises a ValueError.

… sub-groups and checkpoint their Adam state

With offload_optimizer.ratio < 1, the sub-groups kept on the device are stepped by a separate torch optimizer.

- Give each of its param groups its own dict and copy lr, betas, eps and weight_decay from self.optimizer's group
  before each device step (ZeRO-3 and SuperOffload). LR schedules ignored the first group, and the shared dicts of the
  other groups kept every CPU partition after a checkpoint load, so device steps also updated CPU sub-groups.
- Save the device sub-groups' Adam state with the other sub-groups' in optimizer_state_dict['state'], and move it to
  the backup optimizer on load and on universal load, so that checkpoints do not depend on the ratio and
  ds_to_universal finds every sub-group.
- Read the state of a device sub-group from the backup optimizer in safe_get/set_full_optimizer_state.
- Use torch Adam (L2) when adam_w_mode is off, and reject bias_correction=False.

Signed-off-by: Xinyu Lian <lian7@illinois.edu>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T06:40:35.578678Z afded17 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afded1772a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if len(self.optimizer.param_groups) > 1:
for i in range(1, len(self.optimizer.param_groups)):
self.backup_optimizer.add_param_group(self.optimizer.param_groups[i])
if not all(group.get("bias_correction", True) for group in self.optimizer.param_groups):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Revalidate bias correction after loading optimizer groups

When a checkpoint whose saved optimizer groups have bias_correction=False is resumed into a ratio < 1 engine constructed with the current/default value True, this check passes, but Optimizer.load_state_dict (and the universal-checkpoint loop) later restores False only to self.optimizer; _sync_backup_optimizer_group never propagates or rejects it. CPU subgroups then run without bias correction while device subgroups use torch Adam's correction, silently changing the resumed training result instead of raising the promised ValueError, so the loaded groups need to be revalidated.

Useful? React with 👍 / 👎.

Comment on lines +56 to +58
"bf16": {
"enabled": True
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard the BF16-only test configuration

When this test file runs on a supported accelerator without BF16 support (for example a CUDA V100), nearly every _config call reaches DeepSpeedEngine._do_sanity_check, which raises ValueError("Type bf16 is not supported on your device.") before any partial-offload behavior is exercised. The test should select a supported precision or skip explicitly via get_accelerator().is_bf16_supported() so the new regression suite remains runnable across supported hardware.

Useful? React with 👍 / 👎.

…ram dtype

torch Adam keeps the moments of the device sub-groups in the param dtype and fails on fp32 moments for bf16 params
("expected dtype float for `end`"). DeepSpeedCPUAdam keeps fp32 moments for bf16 params (fp32_optimizer_states), so a
load that keeps them in fp32 would pass fp32 moments to the backup optimizer. Cast them to the param dtype when they
move there. Test: resume with bf16 master weights at ratio 0.5.

Signed-off-by: Xinyu Lian <lian7@illinois.edu>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant