Conversation
Move FSDP2 wrapping and plan verification to distributed/fsdp.py, keep integrations/fsdp.py as a backward-compatible re-export, and update core call sites to import from transformers.distributed.fsdp.
…uggingface/transformers into split/a-pr-3-dual-path-loading
naming Co-authored-by: Arthur <48595927+ArthurZucker@users.noreply.github.com>
…huggingface/transformers into split/a-pr-4-fsdp-orchestration
|
flagging #47253 for here |
|
[For maintainers] Suggested jobs to run (before merge) run-slow: cohere2_moe, deepseek_v4, glm_moe_dsa, gpt_oss |
CI recapDashboard: View test results in Grafana |
ArthurZucker
left a comment
There was a problem hiding this comment.
overall LGTM, would like to remove onliners, many are introduced where. we might not need them!
| if self._tp_size is not None: | ||
| state_dict, needs_dist_barrier = self._gather_tp_state_dict_for_save( | ||
| state_dict, is_checkpoint_writer=save_on_this_rank | ||
| ) | ||
| elif is_fsdp_managed_module(model_to_save): | ||
| state_dict, needs_dist_barrier = self._gather_fsdp_state_dict_for_save(model_to_save) | ||
|
|
There was a problem hiding this comment.
should be a single distributed function with the if else modeling does not care
| "metadata": {"total_parameters": self.num_parameters(), **state_dict_split.metadata}, | ||
| "weight_map": weight_map, | ||
| } | ||
| # TODO: it would be very nice to do the writing concurrently, but safetensors never releases the GIL, |
| "torch", | ||
| docker_image=[{"image": "huggingface/transformers-torch-light"}], | ||
| marker="not generate", | ||
| marker="not (generate or is_training_test or is_tensor_parallel_test or is_fsdp_test)", |
|
|
||
| distributed_config = DistributedConfig(enable_expert_parallel=True) | ||
| distributed_config = DistributedConfig( | ||
| tp_size=int(os.environ["WORLD_SIZE"]), |
There was a problem hiding this comment.
do you actually need this?
| raise ValueError( | ||
| "FSDP+TP is not supported yet. " | ||
| "Use DistributedConfig(fsdp_size=N) or DistributedConfig(tp_size=N), not both. " | ||
| "2D support will come soon." |
There was a problem hiding this comment.
link the PR, maybe next release?!
| if not is_torch_available(): | ||
| raise RuntimeError("PyTorch is required to use DistributedConfig.") | ||
|
|
||
| if not torch.distributed.is_available() or not torch.distributed.is_initialized(): | ||
| raise RuntimeError( | ||
| "torch.distributed must be initialized before using DistributedConfig with tp_size > 1 or " | ||
| "fsdp_size > 1. Call dist.init_process_group(...) first, or launch with torchrun." | ||
| ) |
There was a problem hiding this comment.
this file can't be imported already no?
| elif distributed_config.fsdp_size > 1: | ||
| device_map, device_mesh = initialize_fully_sharded_data_parallelism(distributed_config) | ||
|
|
||
| distributed_config.validate() |
There was a problem hiding this comment.
weird that we validate last
| def _gather_tp_state_dict_for_save( | ||
| self, | ||
| local_state_dict: dict, | ||
| *, | ||
| is_checkpoint_writer: bool = True, | ||
| ) -> tuple[dict, bool]: | ||
| """All-gather TP-sharded weights for checkpoint writing.""" | ||
| full_state_dict = gather_state_dict_for_save(local_state_dict, self._tp_plan, self._device_mesh, self._tp_size) | ||
| if not is_checkpoint_writer: | ||
| full_state_dict = {} | ||
| return full_state_dict, True | ||
|
|
||
| def _gather_fsdp_state_dict_for_save(self, model_to_save) -> tuple[dict, bool]: | ||
| """Gather FSDP-sharded weights to full CPU tensors on rank 0.""" | ||
| if not _is_torch_distributed_initialized(): | ||
| raise ValueError( | ||
| "Saving an FSDP-wrapped model requires torch.distributed to be initialized. " | ||
| "Call save_pretrained from every rank after init_process_group." | ||
| ) | ||
| return gather_full_state_dict(model_to_save), True |
There was a problem hiding this comment.
loads of one liners, we usually avoid them when possible
| # `gather_full_state_dict` concentrates the full state on rank 0 only; | ||
| # other ranks then loop over an empty shard list and would race ahead | ||
| # of rank 0's safetensors writes. Barrier so any subsequent | ||
| # `from_pretrained` on this path sees the consolidated files. | ||
| if needs_dist_barrier: | ||
| _distributed_barrier() |
There was a problem hiding this comment.
not sure this should be here
Summary
base_fsdp_plan. Will do another PR to edit every other models laterfrom_pretrainedshard-on-Read+ saving like TP (DCP optional)DistributedConfigeverywhere (no more tp_plan=auto)Stack