BonanDing commited on
Commit
0e5371e
·
1 Parent(s): 71dedf8

Fix curriculum auto-resume stage handoff

Browse files
experiments/exp_base.py CHANGED
@@ -9,6 +9,7 @@ from abc import ABC, abstractmethod
9
  from typing import Optional, Union, Literal, List, Dict
10
  import pathlib
11
  import os
 
12
  from datetime import timedelta
13
 
14
  import hydra
@@ -72,6 +73,13 @@ _DEMEMWM_FRAME_MEMORY_MARKERS = (
72
  )
73
 
74
 
 
 
 
 
 
 
 
75
  def _short_list(items, limit=8):
76
  items = list(items)
77
  suffix = "" if len(items) <= limit else f", ... (+{len(items) - limit} more)"
@@ -483,9 +491,18 @@ class BaseLightningExperiment(BaseExperiment):
483
  return None if self.customized_load else self.ckpt_path
484
 
485
  def _fit_training_curriculum(self, stages, ckpt_path) -> None:
 
486
  for stage_idx, stage in enumerate(stages):
487
  until_step = self._curriculum_until_step(stage, stage_idx)
488
  self._apply_curriculum_stage(stage, stage_idx, until_step)
 
 
 
 
 
 
 
 
489
  trainer = self._build_training_trainer(max_steps=until_step)
490
  trainer.fit(
491
  self.algo,
 
9
  from typing import Optional, Union, Literal, List, Dict
10
  import pathlib
11
  import os
12
+ import re
13
  from datetime import timedelta
14
 
15
  import hydra
 
73
  )
74
 
75
 
76
+ def _checkpoint_step_from_path(ckpt_path) -> Optional[int]:
77
+ if ckpt_path is None:
78
+ return None
79
+ step_match = re.search(r"step[=_-]?(\d+)", pathlib.Path(str(ckpt_path)).stem)
80
+ return int(step_match.group(1)) if step_match else None
81
+
82
+
83
  def _short_list(items, limit=8):
84
  items = list(items)
85
  suffix = "" if len(items) <= limit else f", ... (+{len(items) - limit} more)"
 
491
  return None if self.customized_load else self.ckpt_path
492
 
493
  def _fit_training_curriculum(self, stages, ckpt_path) -> None:
494
+ resume_step = _checkpoint_step_from_path(ckpt_path) if self.auto_resuming else None
495
  for stage_idx, stage in enumerate(stages):
496
  until_step = self._curriculum_until_step(stage, stage_idx)
497
  self._apply_curriculum_stage(stage, stage_idx, until_step)
498
+ if resume_step is not None and resume_step >= until_step:
499
+ if is_rank_zero:
500
+ print(
501
+ cyan("Skipping curriculum stage:"),
502
+ f"{stage.get('name', stage_idx + 1)} until step {until_step}; "
503
+ f"resume checkpoint is already at step {resume_step}",
504
+ )
505
+ continue
506
  trainer = self._build_training_trainer(max_steps=until_step)
507
  trainer.fit(
508
  self.algo,
tests/test_resume_checkpoint_logic.py CHANGED
@@ -184,6 +184,46 @@ class ResumeCheckpointLogicTests(unittest.TestCase):
184
  self.assertEqual(DummyTrainer.save_checkpoint_calls, [Path("/tmp/curriculum_stage.ckpt")])
185
  self.assertEqual(float(experiment.root_cfg.algorithm.memory_selection.pose_similarity_radius), 8.0)
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  def test_validation_load_checkpoint_takes_priority_over_separate_load(self):
188
  DummyTrainer.validate_calls = []
189
  experiment = DummyExperiment(_root_cfg(auto_resuming=False), logger=None, ckpt_path="/tmp/trained.ckpt")
 
184
  self.assertEqual(DummyTrainer.save_checkpoint_calls, [Path("/tmp/curriculum_stage.ckpt")])
185
  self.assertEqual(float(experiment.root_cfg.algorithm.memory_selection.pose_similarity_radius), 8.0)
186
 
187
+ def test_auto_resume_curriculum_skips_completed_stages(self):
188
+ DummyTrainer.init_calls = []
189
+ DummyTrainer.fit_calls = []
190
+ DummyTrainer.save_checkpoint_calls = []
191
+ cfg = _root_cfg(auto_resuming=True)
192
+ cfg.experiment.training.curriculum = {
193
+ "enabled": True,
194
+ "stages": [
195
+ {"name": "near", "until_step": 3, "dataset": {"wo_updown": True}},
196
+ {"name": "full", "until_step": 5, "dataset": {"wo_updown": False}},
197
+ ],
198
+ }
199
+ experiment = DummyExperiment(cfg, logger=None, ckpt_path="/tmp/epoch1_step4.ckpt")
200
+ experiment.algo = object()
201
+ seen_dataset = []
202
+
203
+ def build_training_loader():
204
+ seen_dataset.append(bool(experiment.root_cfg.dataset.wo_updown))
205
+ return None
206
+
207
+ experiment._build_training_loader = build_training_loader
208
+
209
+ with patch.object(exp_base.pl, "Trainer", DummyTrainer), patch.object(
210
+ DummyExperiment,
211
+ "_curriculum_checkpoint_path",
212
+ return_value=Path("/tmp/curriculum_stage.ckpt"),
213
+ ):
214
+ experiment.training()
215
+
216
+ self.assertEqual(seen_dataset, [False])
217
+ self.assertEqual(
218
+ [call["kwargs"]["ckpt_path"] for call in DummyTrainer.fit_calls],
219
+ ["/tmp/epoch1_step4.ckpt"],
220
+ )
221
+ self.assertEqual(
222
+ [call["kwargs"]["max_steps"] for call in DummyTrainer.init_calls],
223
+ [5],
224
+ )
225
+ self.assertEqual(DummyTrainer.save_checkpoint_calls, [])
226
+
227
  def test_validation_load_checkpoint_takes_priority_over_separate_load(self):
228
  DummyTrainer.validate_calls = []
229
  experiment = DummyExperiment(_root_cfg(auto_resuming=False), logger=None, ckpt_path="/tmp/trained.ckpt")