fix(datapipes): resume-reproducible set_epoch across readers and transforms#1817
fix(datapipes): resume-reproducible set_epoch across readers and transforms#1817peterdsharpe wants to merge 3 commits into
Conversation
manual_seed(initial_seed() + epoch) drifts: manual_seed updates initial_seed, so the epoch->seed mapping depended on call history (sequential epochs 1..5 from base 42 gave seeds 43,45,48,52,57; resuming directly at epoch 5 gave 47), silently changing subsample blocks on checkpoint resume. Capture the base seed once in set_generator and reseed as base + epoch. Same fix as the zarr mesh readers, which inherited this code; behavior is pinned by test_set_epoch_resume_reproducible.
…sforms Every set_epoch implementation reseeded with initial_seed() + epoch, which compounds: manual_seed updates initial_seed, so the epoch->seed mapping depended on call history (base 42, sequential epochs 1..5 -> seeds 43,45,48,52,57; resuming directly at epoch 5 -> 47). A run resumed from a checkpoint therefore drew different subsample blocks and augmentations than an uninterrupted run -- a silent reproducibility break. Capture the base seed once at set_generator time and reseed with base + epoch. Applied to NumpyReader, ZarrReader, TensorStoreZarrReader, Transform, and MeshTransform (transforms fall back to capturing the base lazily on first set_epoch for generators declared without set_generator). RNG.md and DISTRIBUTIONS.md documented the old idiom as the contract; updated to the resume-reproducible semantics. Pinned by sequential-vs- direct set_epoch equivalence tests for MeshReader, ZarrReader, and the MeshDataset transform pipeline.
for more information, see https://pre-commit.ci
|
/ok to test 268aff0 |
Greptile SummaryThis PR fixes a real reproducibility bug in the datapipes stack where
Important Files Changed
|
|
Closing in favor of #1742, which fixes the reader half of this more thoroughly (per- |
Description
Every
set_epochimplementation in the datapipes stack reseeded its generator withinitial_seed() + epoch. That idiom compounds:manual_seedupdatesinitial_seed, so the epoch→seed mapping depends on call history. With base seed 42, sequentialset_epoch(1..5)produces seeds 43, 45, 48, 52, 57 — while a run resumed from a checkpoint that callsset_epoch(5)directly gets 47. A resumed run therefore silently draws different subsample blocks and augmentations than an uninterrupted run, breaking seed-matched A/B protocols. Within a single uninterrupted run nothing is wrong (still deterministic), which is why this went unnoticed.Fix: capture the base seed once at
set_generatortime and reseed withbase_seed + epoch. Applied to all sites:readers/mesh.py(MeshReader,DomainMeshReader) — cherry-picked byte-identical from Feat(datapipes): Zarr mesh schema and readers — a zarr storage path for mesh-native CAE pipelines #1798 (which applies the same fix to its new zarr mesh readers), so the two PRs merge cleanly in either orderreaders/numpy.py,readers/zarr.py,readers/tensorstore_zarr.pytransforms/base.py(Transform),transforms/mesh/base.py(MeshTransform) — with a lazy fallback for generators declared by subclasses without going throughset_generatorRNG.mdandDISTRIBUTIONS.mddocumented the drifting idiom as the contract; both are updated to the resume-reproducible semantics.Composeonly delegates and needed no change.Behavior note: epoch ≥ 2 now draws different (finally history-independent) blocks/augmentations than before the fix, so bitwise loss-curve comparisons across this change will differ; training statistics are unaffected.
Tests: sequential-vs-direct
set_epochequivalence (block([1,2,3]) == block([3])) pinned forMeshReader,ZarrReadercoordinated subsampling, and theMeshDatasettransform pipeline. Fulltest/datapipes/readers/+test_mesh_augmentations.pysuites pass (114 passed).Checklist
Dependencies
None.