Skip to content

Commit ca5d69d

Browse files
RecML authorsrecml authors
authored andcommitted
Internal change
PiperOrigin-RevId: 944615290
1 parent 8e8b05b commit ca5d69d

4 files changed

Lines changed: 1395 additions & 70 deletions

File tree

recml/core/training/keras_trainer.py

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,32 @@ def __init__(
122122
max_checkpoints_to_keep: int = 5,
123123
checkpoint_save_interval_epochs: int = 1,
124124
rng_seed: int = core.DEFAULT_RNG_SEED,
125-
legacy_checkpoint_format: bool = True,
125+
checkpoint_version: keras_utils.CheckpointVersion | str = "v2",
126+
legacy_checkpoint_format: bool | None = None,
126127
):
127-
"""Initializes the instance."""
128+
"""Initializes the instance.
129+
130+
Args:
131+
distribution: The distribution strategy to use.
132+
model_dir: The directory to save checkpoints and logs.
133+
train_steps: Total number of training steps.
134+
steps_per_eval: Number of steps between evaluations.
135+
continuous_eval_timeout: Timeout for continuous evaluation.
136+
steps_per_loop: Number of steps per training loop.
137+
max_checkpoints_to_keep: Maximum number of checkpoints to keep.
138+
checkpoint_save_interval_epochs: Interval in epochs to save checkpoints.
139+
rng_seed: Random seed.
140+
checkpoint_version: The checkpoint version to use. Supported versions:
141+
"v1" (legacy V1), "v2" (V2, default), "v3" (V3).
142+
legacy_checkpoint_format: Deprecated. Use checkpoint_version instead. If
143+
set, True maps to V1, and False maps to V2. TODO(b/542602169): Remove
144+
this in v2.
145+
resume_training_launcher: Launcher to resume training.
146+
enable_xmanager_measurements: Whether to enable XManager measurements.
147+
enable_autoxprof: Whether to enable AutoXprof.
148+
autoxprof_settings: Settings for AutoXprof.
149+
s2_logging_settings: Settings for S2 logging.
150+
"""
128151

129152
keras.utils.set_random_seed(rng_seed)
130153

@@ -153,25 +176,42 @@ def __init__(
153176
self._checkpoint_dir = os.path.join(model_dir, core.CHECKPOINT_DIR)
154177
self._max_checkpoints_to_keep = max_checkpoints_to_keep
155178
self._checkpoint_save_interval_epochs = checkpoint_save_interval_epochs
156-
self._legacy_checkpoint_format = legacy_checkpoint_format
179+
if legacy_checkpoint_format is not None:
180+
logging.warning(
181+
"legacy_checkpoint_format is deprecated, use checkpoint_version"
182+
" instead."
183+
)
184+
self._checkpoint_version = (
185+
keras_utils.CheckpointVersion.V1
186+
if legacy_checkpoint_format
187+
else keras_utils.CheckpointVersion.V2
188+
)
189+
else:
190+
self._checkpoint_version = keras_utils.CheckpointVersion(
191+
checkpoint_version
192+
)
157193

158194
@functools.cached_property
159195
def train_callbacks(self) -> list[keras.callbacks.Callback]:
160196
"""Returns the training callbacks."""
161197
if keras.backend.backend() == "jax":
162-
if self._legacy_checkpoint_format:
163-
checkpoint_manager = keras_utils.KerasOrbaxCheckpointManager(
164-
checkpoint_dir=self._checkpoint_dir,
165-
max_to_keep=self._max_checkpoints_to_keep,
166-
save_interval_epochs=self._checkpoint_save_interval_epochs,
167-
)
198+
if self._checkpoint_version == keras_utils.CheckpointVersion.V1:
199+
manager_cls = keras_utils.KerasOrbaxCheckpointManager
200+
elif self._checkpoint_version == keras_utils.CheckpointVersion.V2:
201+
manager_cls = keras_utils.KerasOrbaxCheckpointManagerV2
202+
elif self._checkpoint_version == keras_utils.CheckpointVersion.V3:
203+
manager_cls = keras_utils.KerasOrbaxCheckpointManagerV3
168204
else:
169-
checkpoint_manager = keras_utils.KerasOrbaxCheckpointManagerV2(
170-
checkpoint_dir=self._checkpoint_dir,
171-
max_to_keep=self._max_checkpoints_to_keep,
172-
save_interval_epochs=self._checkpoint_save_interval_epochs,
205+
raise ValueError(
206+
f"Unsupported checkpoint version: {self._checkpoint_version}"
173207
)
174208

209+
checkpoint_manager = manager_cls(
210+
checkpoint_dir=self._checkpoint_dir,
211+
max_to_keep=self._max_checkpoints_to_keep,
212+
save_interval_epochs=self._checkpoint_save_interval_epochs,
213+
)
214+
175215
callbacks = [
176216
keras_utils.EpochSummaryCallback(
177217
log_dir=os.path.join(self._model_dir, core.LOG_DIR),
@@ -379,7 +419,7 @@ def timeout_fn() -> bool:
379419
else:
380420
steps_msg = "running complete evaluation..."
381421

382-
use_legacy_checkpoint_format = self._legacy_checkpoint_format
422+
checkpoint_version = self._checkpoint_version
383423

384424
class _RestoreCallback(keras.callbacks.Callback):
385425
"""Callback for restoring the model from the latest checkpoint."""
@@ -393,14 +433,21 @@ def __init__(
393433
self._epoch = epoch
394434

395435
def on_test_begin(self, logs: Mapping[str, Any] | None = None):
396-
if use_legacy_checkpoint_format:
436+
if checkpoint_version == keras_utils.CheckpointVersion.V1:
397437
keras_utils.restore_keras_model(
398438
model, self._checkpoint_dir, step=self._epoch
399439
)
400-
else:
440+
elif checkpoint_version in (
441+
keras_utils.CheckpointVersion.V2,
442+
keras_utils.CheckpointVersion.V3,
443+
):
401444
keras_utils.restore_keras_checkpoint(
402445
self._checkpoint_dir, model=model, epoch=self._epoch
403446
)
447+
else:
448+
raise ValueError(
449+
f"Unsupported checkpoint version: {checkpoint_version}"
450+
)
404451

405452
history = None
406453
for epoch in ocp.checkpoint_utils.checkpoints_iterator(

recml/core/training/keras_trainer_test.py

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import keras
2323
from recml.core.training import core
2424
from recml.core.training import keras_trainer
25+
from recml.core.utils import keras_utils
2526
import tensorflow as tf
2627

2728

@@ -55,6 +56,7 @@ class KerasTrainerTest(parameterized.TestCase):
5556

5657
def setUp(self):
5758
super().setUp()
59+
keras.backend.clear_session()
5860
# Workaround to make `create_tempdir` work with pytest.
5961
if not flags.FLAGS.is_parsed():
6062
flags.FLAGS.mark_as_parsed()
@@ -63,26 +65,38 @@ def setUp(self):
6365
{"testcase_name": "train", "mode": core.Trainer.Mode.TRAIN},
6466
{"testcase_name": "eval", "mode": core.Trainer.Mode.EVAL},
6567
{
66-
"testcase_name": "train_and_eval",
68+
"testcase_name": "train_and_eval_v3",
6769
"mode": core.Trainer.Mode.TRAIN_AND_EVAL,
70+
"checkpoint_version": "v3",
6871
},
6972
{
70-
"testcase_name": "continuous_eval_",
71-
"mode": core.Trainer.Mode.CONTINUOUS_EVAL,
73+
"testcase_name": "train_and_eval_v2",
74+
"mode": core.Trainer.Mode.TRAIN_AND_EVAL,
75+
"checkpoint_version": "v2",
7276
},
7377
{
74-
"testcase_name": "train_and_eval_legacy_checkpoint_format",
78+
"testcase_name": "train_and_eval_v1",
7579
"mode": core.Trainer.Mode.TRAIN_AND_EVAL,
76-
"legacy_checkpoint_format": True,
80+
"checkpoint_version": "v1",
81+
},
82+
{
83+
"testcase_name": "continuous_eval_v3",
84+
"mode": core.Trainer.Mode.CONTINUOUS_EVAL,
85+
"checkpoint_version": "v3",
7786
},
7887
{
79-
"testcase_name": "continuous_eval_legacy_checkpoint_format",
88+
"testcase_name": "continuous_eval_v2",
8089
"mode": core.Trainer.Mode.CONTINUOUS_EVAL,
81-
"legacy_checkpoint_format": True,
90+
"checkpoint_version": "v2",
91+
},
92+
{
93+
"testcase_name": "continuous_eval_v1",
94+
"mode": core.Trainer.Mode.CONTINUOUS_EVAL,
95+
"checkpoint_version": "v1",
8296
},
8397
)
8498
def test_keras_task_and_trainer(
85-
self, mode: str, legacy_checkpoint_format: bool = False
99+
self, mode: str, checkpoint_version: str = "v3"
86100
):
87101
if keras.backend.backend() == "jax":
88102
distribution = keras.distribution.DataParallel()
@@ -98,13 +112,14 @@ def test_keras_task_and_trainer(
98112
steps_per_loop=2,
99113
model_dir=self.create_tempdir().full_path,
100114
continuous_eval_timeout=5,
101-
legacy_checkpoint_format=legacy_checkpoint_format,
115+
checkpoint_version=checkpoint_version,
102116
)
103117
experiment = core.Experiment(_KerasTask(), trainer)
104118

105119
if mode == core.Trainer.Mode.CONTINUOUS_EVAL:
106120
# Produce one checkpoint so there is something to evaluate.
107121
core.run_experiment(experiment, core.Trainer.Mode.TRAIN)
122+
keras.backend.clear_session()
108123

109124
history = core.run_experiment(experiment, mode)
110125

@@ -142,6 +157,35 @@ def test_eval_name(self):
142157
self.assertTrue(os.path.exists(expected_log_dir))
143158
self.assertEqual(experiment.task.last_eval_name, eval_name)
144159

160+
def test_legacy_checkpoint_format_deprecated(self):
161+
model_dir = self.create_tempdir().full_path
162+
163+
# Test legacy_checkpoint_format=True -> V1
164+
with self.assertLogs(level="WARNING") as log:
165+
trainer = keras_trainer.KerasTrainer(
166+
model_dir=model_dir,
167+
legacy_checkpoint_format=True,
168+
)
169+
self.assertEqual(
170+
trainer._checkpoint_version, keras_utils.CheckpointVersion.V1
171+
)
172+
self.assertIn(
173+
"legacy_checkpoint_format is deprecated", log.output[0]
174+
)
175+
176+
# Test legacy_checkpoint_format=False -> V2
177+
with self.assertLogs(level="WARNING") as log:
178+
trainer = keras_trainer.KerasTrainer(
179+
model_dir=model_dir,
180+
legacy_checkpoint_format=False,
181+
)
182+
self.assertEqual(
183+
trainer._checkpoint_version, keras_utils.CheckpointVersion.V2
184+
)
185+
self.assertIn(
186+
"legacy_checkpoint_format is deprecated", log.output[0]
187+
)
188+
145189

146190
if __name__ == "__main__":
147191
absltest.main()

0 commit comments

Comments
 (0)