Skip to content

Checkpoint the curriculum data sampler's own RNG state - #8406

Open
ebarkhordar wants to merge 1 commit into
deepspeedai:masterfrom
ebarkhordar:fix/curriculum-sampler-rng-state
Open

Checkpoint the curriculum data sampler's own RNG state#8406
ebarkhordar wants to merge 1 commit into
deepspeedai:masterfrom
ebarkhordar:fix/curriculum-sampler-rng-state

Conversation

@ebarkhordar

Copy link
Copy Markdown
Contributor

DeepSpeedDataSampler draws from self.np_rng, the generator it seeds in __init__, but state_dict() saved np.random.get_state() and load_state_dict() restored that. The global legacy RandomState was never the RNG the sampler used, so the generator stayed out of the checkpoint and a resumed run started its stream again from the seed: same cluster mix per step, same shuffles.

Saves and restores self.np_rng.bit_generator.state instead. A state dict written by an older version carries the old tuple, and that shape still goes through np.random.set_state so old checkpoints keep loading the way they did.

Added two tests for the resume path and one that loads an old-style state dict. The two behaviour ones fail on master.

Fixes #8405

DeepSpeedDataSampler draws from self.np_rng, but state_dict() saved
np.random.get_state() and load_state_dict() restored it. That is the
global legacy RandomState, so the sampler's generator was never in the
checkpoint and a resumed run replayed its sampling stream from the seed.

Save and restore self.np_rng.bit_generator.state instead. A state dict
written by an older version carries the old tuple, which still goes
through np.random.set_state.

Signed-off-by: Ehsan Barkhordar <realbarkhordar@gmail.com>

@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: 8384ae8662

ℹ️ 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".

Comment on lines +160 to +163
resumed = _curriculum_data_sampler(tmp_path)
resumed.load_state_dict(saved.state_dict())
next_draws = [resumed.sample_from_clusters().tolist() for _ in range(3)]
assert next_draws == [saved.sample_from_clusters().tolist() for _ in range(3)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Exercise checkpoint resume through the training loop

The resume test passes an in-memory dictionary directly between samplers, so it cannot catch failures in the actual DeepSpeedEngine.save_checkpoint()/load_checkpoint() path, serialization, dataloader restoration, or distributed execution. Because this change modifies checkpoint-resume behavior observable by a training loop, add an integration test that compares uninterrupted and checkpoint-resumed sampling on actual devices and report the hardware result.

AGENTS.md reference: AGENTS.md:L35-L36

Useful? React with 👍 / 👎.

sampler = _curriculum_data_sampler(tmp_path)
for _ in range(3):
sampler.sample_from_clusters()
assert sampler.state_dict()[CURRICULUM_LEARNING_NP_RNG_STATE] == sampler.np_rng.bit_generator.state

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace the implementation-specific RNG-state assertion

This assertion pins the test to the sampler's private np_rng.bit_generator.state representation rather than the checkpoint-resume contract; an otherwise correct implementation that copies, normalizes, or encodes the generator state differently would fail it. The following test already checks continued sampling behavior, so remove this assertion or replace it with a round-trip assertion over observable samples.

AGENTS.md reference: AGENTS.md:L30-L32

Useful? React with 👍 / 👎.

@ebarkhordar

Copy link
Copy Markdown
Contributor Author

On the RNG assertion: bit_generator.state is numpy's own interface for a Generator's state, and it is exactly the value the checkpoint now stores, so that line compares the field against its source rather than against an internal encoding.

It also pins something the resume test does not, which is which generator gets checkpointed. Putting the sampler back on the process-global np.random for both the draws and the state would keep test_curriculum_sampler_resumes_its_rng_stream green and bring back the shared-state coupling this change is about. So I'd rather keep it.

On the engine-level resume test: TestDataEfficiency::test_curriculum_learning already drives the engine, and a save_checkpoint/load_checkpoint comparison alongside it would be worth having. I have not run one on a GPU, so adding it here would mean shipping a test I cannot report a result for. If you want it in this PR, say so and I will write it.

@ebarkhordar

ebarkhordar commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

#8425 opened a couple of hours ago with the same source change as this one, legacy-tuple branch included, so what actually differs is the tests. This PR adds three to test_data_efficiency.py: that state_dict stores the sampler's own bit_generator.state, that a reload continues that stream instead of restarting it from the seed, and that an old-style state dict still loads.

I am happy to close this in favour of #8425 if you would rather take that one, though the tests are worth carrying over either way, since nothing else in the suite catches this coming back.

Updated 2026-09-08: there are now three PRs on #8405, since #8460 opened this morning. All three make the same one-file change, saving and restoring self.np_rng.bit_generator.state with a branch for the old legacy tuple, so the sentence above about tests being the difference no longer separates this PR from #8460: it covers the same three cases in a new test_curriculum_sampler_rng.py. #8425 still ships none.

My view is that whichever one lands should carry tests, and past that I do not think it matters which. Happy to close this in favour of #8460 if you would rather take that one.

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.

[BUG] Curriculum data sampler checkpoints the global numpy RNG, not its own, so resume replays the sampling stream

1 participant