Skip to content

Commit fcb7ebe

Browse files
Merge pull request #4304 from AI-Hypercomputer:jackyf/proactive-scan-layers
PiperOrigin-RevId: 941788193
2 parents f59d857 + f407634 commit fcb7ebe

4 files changed

Lines changed: 76 additions & 6 deletions

File tree

src/maxtext/common/checkpointing.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,11 +1201,12 @@ def save_checkpoint(checkpoint_manager, step, state, config=None, data_iterator=
12011201
grain_iters_to_save.append((data_iter.local_iterator, process_index, process_count_total))
12021202
save_args_composite["iter"] = GrainCheckpointSave(item=grain_iters_to_save)
12031203

1204-
custom_metadata = None
1205-
if config and hasattr(config, "lora") and config.lora:
1206-
lora_rank = getattr(config.lora, "lora_rank", 0)
1207-
if lora_rank > 0 and hasattr(config.lora, "model_dump"):
1208-
custom_metadata = {"lora": config.lora.model_dump()}
1204+
custom_metadata = {}
1205+
if config:
1206+
if hasattr(config, "scan_layers"):
1207+
custom_metadata["scan_layers"] = config.scan_layers
1208+
if hasattr(config, "lora") and config.lora and getattr(config.lora, "lora_rank", 0) > 0:
1209+
custom_metadata["lora"] = config.lora.model_dump()
12091210

12101211
match (checkpoint_manager, config, data_iterator):
12111212
case (checkpoint_manager, _, _) if isinstance(

src/maxtext/utils/model_creation_utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import jax
4545
import jax.numpy as jnp
4646
from jax.sharding import Mesh
47+
from maxtext.common import checkpointing
4748
from maxtext.common.checkpointing import handle_checkpoint_mismatch
4849
from maxtext.common.common_types import MODEL_MODE_AUTOREGRESSIVE, MODEL_MODE_TRAIN
4950
from maxtext.configs import pyconfig
@@ -864,6 +865,15 @@ def from_pretrained(
864865
}
865866
)
866867
config = pyconfig.HyperParameters(new_config)
868+
# Proactive verification of scan_layers from checkpoint metadata
869+
if config.load_parameters_path:
870+
custom_metadata = checkpointing.load_checkpoint_metadata(config.load_parameters_path)
871+
saved_scan_layers = custom_metadata.get("scan_layers")
872+
if isinstance(saved_scan_layers, bool) and saved_scan_layers != config.scan_layers:
873+
raise ValueError(
874+
f"Configuration mismatch: Your run specifies scan_layers={config.scan_layers}, "
875+
f"but the checkpoint was saved with scan_layers={saved_scan_layers}."
876+
)
867877

868878
if config.pure_nnx:
869879
_create_model, abstract_model = create_nnx_abstract_model(

tests/post_training/unit/lora_utils_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ def test_save_checkpoint_passes_metadata(self):
389389
mock_manager.save.assert_called_once()
390390
_, kwargs = mock_manager.save.call_args
391391
self.assertIn("custom_metadata", kwargs)
392-
self.assertEqual(kwargs["custom_metadata"], {"lora": cfg.lora.model_dump()})
392+
self.assertEqual(kwargs["custom_metadata"]["lora"], cfg.lora.model_dump())
393393

394394
def test_save_and_restore_metadata_integration(self):
395395
"""Integration test checking that Orbax CheckpointManager writes and reads custom LoRA metadata."""

tests/unit/model_creation_utils_test.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,65 @@ def test_checkpoint_load_error_propagates(self, mock_ocp):
723723
with self.assertRaises(RuntimeError):
724724
model_creation_utils.from_pretrained(cfg, self.mesh)
725725

726+
@patch("maxtext.utils.model_creation_utils.checkpointing.load_checkpoint_metadata")
727+
def test_scan_layers_mismatch_raises_error(self, mock_load_meta):
728+
"""ValueError is raised if run specifies scan_layers=True but checkpoint specifies scan_layers=False."""
729+
mock_load_meta.return_value = {"scan_layers": False}
730+
731+
cfg = _make_config(
732+
enable_checkpointing=True, load_parameters_path="gs://fake/scan_layers_false_ckpt", scan_layers=True
733+
)
734+
735+
with self.assertRaises(ValueError) as context:
736+
model_creation_utils.from_pretrained(cfg, self.mesh)
737+
self.assertIn(
738+
"Configuration mismatch: Your run specifies scan_layers=True, "
739+
"but the checkpoint was saved with scan_layers=False",
740+
str(context.exception),
741+
)
742+
743+
@patch("maxtext.utils.model_creation_utils.checkpointing.load_checkpoint_metadata")
744+
@patch("maxtext.utils.model_creation_utils.ocp")
745+
def test_scan_layers_match_no_error(self, mock_ocp, mock_load_meta):
746+
"""If the run specifies scan_layers=True and the checkpoint matches, it proceeds without error."""
747+
mock_load_meta.return_value = {"scan_layers": True}
748+
749+
mock_ckptr = MagicMock()
750+
mock_ckptr.metadata.return_value = self._make_linen_metadata_mock()
751+
mock_ckptr.restore.side_effect = lambda path, item=None, **kw: item
752+
mock_ocp.Checkpointer.return_value = mock_ckptr
753+
mock_ocp.PyTreeCheckpointHandler.return_value = MagicMock()
754+
mock_ocp.checkpoint_utils.construct_restore_args.return_value = {}
755+
mock_ocp.ArrayRestoreArgs = ocp.ArrayRestoreArgs
756+
757+
cfg = _make_config(
758+
enable_checkpointing=True, load_parameters_path="gs://fake/scan_layers_true_ckpt", scan_layers=True
759+
)
760+
761+
model = model_creation_utils.from_pretrained(cfg, self.mesh)
762+
self.assertIsInstance(model, models.Transformer)
763+
764+
@patch("maxtext.utils.model_creation_utils.checkpointing.load_checkpoint_metadata")
765+
@patch("maxtext.utils.model_creation_utils.ocp")
766+
def test_scan_layers_missing_metadata_no_error(self, mock_ocp, mock_load_meta):
767+
"""Skip verification and proceed if custom_metadata lacks 'scan_layers'."""
768+
mock_load_meta.return_value = {}
769+
770+
mock_ckptr = MagicMock()
771+
mock_ckptr.metadata.return_value = self._make_linen_metadata_mock()
772+
mock_ckptr.restore.side_effect = lambda path, item=None, **kw: item
773+
mock_ocp.Checkpointer.return_value = mock_ckptr
774+
mock_ocp.PyTreeCheckpointHandler.return_value = MagicMock()
775+
mock_ocp.checkpoint_utils.construct_restore_args.return_value = {}
776+
mock_ocp.ArrayRestoreArgs = ocp.ArrayRestoreArgs
777+
778+
cfg = _make_config(
779+
enable_checkpointing=True, load_parameters_path="gs://fake/scan_layers_missing_ckpt", scan_layers=True
780+
)
781+
782+
model = model_creation_utils.from_pretrained(cfg, self.mesh)
783+
self.assertIsInstance(model, models.Transformer)
784+
726785

727786
class TestSetupDecodeStateFromNnx(unittest.TestCase):
728787
"""Tests for setup_decode_state_from_nnx()."""

0 commit comments

Comments
 (0)