|
16 | 16 | StateDictOptions, |
17 | 17 | ) |
18 | 18 | from torch.distributed.checkpoint.stateful import Stateful |
| 19 | +from torch.distributed.checkpoint.default_planner import DefaultLoadPlanner |
19 | 20 |
|
20 | 21 | from fastgen.configs.config import BaseCheckpointerConfig |
21 | 22 | from fastgen.utils.distributed.s3_filesystem import S3StorageWriter, S3StorageReader |
@@ -397,39 +398,51 @@ def load( |
397 | 398 | for k, v in optimizer_dict.items(): |
398 | 399 | logger.info(f"Loading the FSDP optimizer dict for key {k}...") |
399 | 400 | optim_wrapper = OptimizerWrapper(model=model_dict[k], optimizer=v) |
400 | | - # For fresh optimizers with no state, we need to initialize with fake gradients |
401 | | - # that are DTensors (not regular Tensors) to avoid the mixed Tensor/DTensor error |
402 | | - if len(v.state) == 0: |
403 | | - # Set fake DTensor gradients to initialize optimizer state |
404 | | - for param in model_dict[k].parameters(): |
405 | | - if param.requires_grad and param.grad is None: |
406 | | - param.grad = torch.zeros_like(param) |
407 | 401 | optim_state_dict = optim_wrapper.state_dict() |
408 | 402 | assert os.path.exists(f"{path}.{k}_model"), f"Key {k} does not exist in FSDP model dict" |
409 | 403 | storage_reader = self.get_storage_reader(checkpoint_path=f"{path}.{k}_optim") |
410 | 404 |
|
411 | 405 | try: |
412 | | - dcp.load( |
413 | | - state_dict=optim_state_dict, |
414 | | - storage_reader=storage_reader, |
415 | | - ) |
416 | | - optim_wrapper.load_state_dict(optim_state_dict) |
417 | | - logger.success(f"Successfully loaded optimizer state for {k}") |
| 406 | + metadata = storage_reader.read_metadata() |
| 407 | + # DCP metadata keys are flattened ("state.<param>.<buffer>"), |
| 408 | + # while optim_state_dict keys are nested ("state", "param_groups"). |
| 409 | + # Compare at the parameter level instead. |
| 410 | + ckpt_param_names = set() |
| 411 | + for mkey in metadata.state_dict_metadata: |
| 412 | + if mkey.startswith("state."): |
| 413 | + rest = mkey[len("state.") :] |
| 414 | + last_dot = rest.rfind(".") |
| 415 | + if last_dot > 0: |
| 416 | + ckpt_param_names.add(rest[:last_dot]) |
| 417 | + |
| 418 | + current_param_names = set(optim_state_dict.get("state", {}).keys()) |
| 419 | + |
| 420 | + missing_from_ckpt = current_param_names - ckpt_param_names |
| 421 | + extra_in_ckpt = ckpt_param_names - current_param_names |
| 422 | + if missing_from_ckpt: |
| 423 | + logger.warning( |
| 424 | + f"Optimizer {k}: {len(missing_from_ckpt)} params in current model " |
| 425 | + f"but missing from checkpoint (will keep initialized values)" |
| 426 | + ) |
| 427 | + logger.debug(f"Missing params: {sorted(missing_from_ckpt)}") |
| 428 | + if extra_in_ckpt: |
| 429 | + logger.warning( |
| 430 | + f"Optimizer {k}: {len(extra_in_ckpt)} params in checkpoint " |
| 431 | + f"but not in current model (will be ignored)" |
| 432 | + ) |
| 433 | + logger.debug(f"Extra params: {sorted(extra_in_ckpt)}") |
| 434 | + if not missing_from_ckpt and not extra_in_ckpt: |
| 435 | + logger.info(f"Optimizer {k}: all {len(current_param_names)} params match checkpoint") |
418 | 436 | except Exception as e: |
419 | | - error_msg = str(e) |
420 | | - if ( |
421 | | - "Missing key" in error_msg |
422 | | - or "Unexpected key" in error_msg |
423 | | - or "CheckpointException" in error_msg |
424 | | - ): |
425 | | - logger.warning(f"Optimizer checkpoint compatibility issue for {k}: {type(e).__name__}") |
426 | | - logger.warning(f"Initializing fresh optimizer state for {k} - training will continue") |
427 | | - # Reset to fresh optimizer state |
428 | | - v.__setstate__({"state": {}, "param_groups": v.param_groups}) |
429 | | - logger.info(f"Reset optimizer state for {k} due to parameter mismatch") |
430 | | - else: |
431 | | - logger.error(f"Unexpected optimizer loading error for {k}: {e}") |
432 | | - raise e |
| 437 | + logger.debug(f"Could not read checkpoint metadata for param comparison: {e}") |
| 438 | + |
| 439 | + dcp.load( |
| 440 | + state_dict=optim_state_dict, |
| 441 | + storage_reader=storage_reader, |
| 442 | + planner=DefaultLoadPlanner(allow_partial_load=True), |
| 443 | + ) |
| 444 | + optim_wrapper.load_state_dict(optim_state_dict) |
| 445 | + logger.success(f"Successfully loaded optimizer state for {k}") |
433 | 446 |
|
434 | 447 | state = self._load_checkpoint(f"{path}.pth", device=device) |
435 | 448 |
|
|
0 commit comments