Skip to content

[fix][fsdp] Resume bitsandbytes checkpoints strictly - #2007

Open
bvolpato wants to merge 3 commits into
NovaSky-AI:mainfrom
bvolpato:bvolpato/fsdp-bnb-checkpoint-resume
Open

[fix][fsdp] Resume bitsandbytes checkpoints strictly#2007
bvolpato wants to merge 3 commits into
NovaSky-AI:mainfrom
bvolpato:bvolpato/fsdp-bnb-checkpoint-resume

Conversation

@bvolpato

@bvolpato bvolpato commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

FSDP checkpoints for bitsandbytes 4-bit modules include quantization metadata beside each weight. Freshly constructed modules can accept the weight while reporting that metadata as unexpected, causing strict resume to fail before optimizer, scheduler, RNG, or dataloader state is restored.

Changes

  • Collect model-load incompatibilities without immediately raising.
  • Accept known FP4 and NF4 metadata only when its associated base weight was accepted.
  • Preserve strict failure for missing keys and unrelated unexpected keys.
  • Leave non-strict loading unchanged.

Checkpoint format and model-loading behavior outside bitsandbytes metadata remain unchanged.

Validation

  • Focused checkpoint tests (9 passed)
  • Ruff, Black, Gitleaks, and git diff --check
  • Single-GPU NF4 QLoRA resume from step 20 to 40, then step 40 to 60, with trainer, dataloader, policy, optimizer, scheduler, and RNG state restored
  • Nonzero gradients observed and checkpoints saved after both resumed phases

@bvolpato
bvolpato force-pushed the bvolpato/fsdp-bnb-checkpoint-resume branch from fcdd6d9 to 52951bc Compare August 10, 2026 00:56
@bvolpato bvolpato changed the title [fix][fsdp] Resume bitsandbytes checkpoints [fix][fsdp] Resume bitsandbytes checkpoints strictly Aug 10, 2026
@bvolpato
bvolpato force-pushed the bvolpato/fsdp-bnb-checkpoint-resume branch from 52951bc to 035c703 Compare August 10, 2026 01:04
@bvolpato
bvolpato force-pushed the bvolpato/fsdp-bnb-checkpoint-resume branch from 035c703 to a980437 Compare August 13, 2026 01:20
@bvolpato
bvolpato marked this pull request as ready for review August 13, 2026 01:23

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a custom model state dict loading helper, _load_model_state_dict, to support strict loading while ignoring bitsandbytes quantization metadata keys (such as .absmax, .quant_map, etc.). It also adds corresponding unit tests to verify this behavior. The review feedback suggests optimizing the quantization key check by leveraging str.endswith with a tuple of suffixes to avoid unnecessary loops, and simplifying the calculation of loaded keys using set operations on dictionary keys.

Comment thread skyrl/backends/skyrl_train/distributed/fsdp_strategy.py Outdated
Comment thread skyrl/backends/skyrl_train/distributed/fsdp_strategy.py Outdated

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit e283a48. Configure here.

return

incompatible = model.load_state_dict(state_dict, strict=False)
loaded_keys = state_dict.keys() - incompatible.unexpected_keys

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Strict load crashes on key set math

High Severity

loaded_keys is computed with state_dict.keys() - incompatible.unexpected_keys. PyTorch returns unexpected_keys as a list, and dict_keys subtraction only accepts a set, so strict resume raises TypeError before any bitsandbytes filtering runs.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e283a48. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is not reproducible on supported Python. Both dict_keys and odict_keys accept list operands for subtraction and return a set: {'a': 1, 'b': 2}.keys() - ['b'] == {'a'}. Focused strict-load tests also pass with PyTorch's list-valued unexpected_keys.

for suffix in _BNB_QUANTIZATION_STATE_SUFFIXES:
if key.endswith(suffix):
return key[: -len(suffix)] in loaded_keys
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nested quant suffixes match too early

High Severity

_is_bnb_quantization_state_key now returns on the first suffix that matches by endswith, so .nested_absmax and .nested_quant_map are stripped with .absmax / .quant_map instead. Valid double-quant metadata is then treated as unexpected and strict resume fails.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e283a48. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These suffixes do not overlap because their delimiters differ: weight.nested_absmax.endswith(.absmax) and weight.nested_quant_map.endswith(.quant_map) are both false. Focused tests cover all six supported suffixes, including both nested forms, and strict loading passes.

- short-circuit unrelated unexpected keys before suffix iteration
- derive loaded keys through state-dict key-view subtraction
@bvolpato
bvolpato force-pushed the bvolpato/fsdp-bnb-checkpoint-resume branch from e283a48 to b000937 Compare August 14, 2026 04:37
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