FSDP orchestration: apply + loading/saving#46990
Conversation
Wire distributed_config from_pretrained/save_pretrained alongside the legacy tp_plan path, add distributed/utils.py for mesh orchestration and checkpoint I/O, and extend sharding_utils with DTensor gather/optimizer fusion helpers needed by save/load.
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
Wire FSDP tests into the dynamic PR CI caller, mirroring tests_tensor_parallel_ci: detect tests_fsdp_ci_test_list.txt, run with is_fsdp_test marker and RUN_FSDP_TESTS, and exclude FSDP tests from the tests_torch job. Companion to huggingface/transformers#46990 (tests_fetcher changes stay in transformers). Co-authored-by: Cursor <cursoragent@cursor.com>
|
run-slow: cohere2_moe, deepseek_v4, glm_moe_dsa, gpt_oss |
|
This comment contains models: ["models/cohere2_moe", "models/deepseek_v4", "models/glm_moe_dsa", "models/gpt_oss"] |
CI ResultsCommit Info
The test failure analysis could not be completed. Please check the workflow run for details. |
Wire FSDP tests into the dynamic PR CI caller, mirroring tests_tensor_parallel_ci: detect tests_fsdp_ci_test_list.txt, run with is_fsdp_test marker and RUN_FSDP_TESTS, and exclude FSDP tests from the tests_torch job. Companion to huggingface/transformers#46990 (tests_fetcher changes stay in transformers). Co-authored-by: Cursor <cursoragent@cursor.com>
|
[For maintainers] Suggested jobs to run (before merge) run-slow: cohere2_moe, deepseek_v4, glm_moe_dsa, gpt_oss |
d70939a to
84dd107
Compare
CI recapDashboard: View test results in Grafana
|
ArthurZucker
left a comment
There was a problem hiding this comment.
ty, isolate changes as much as possible
| # Get the model state_dict | ||
| # --- FSDP path: DCP save (large models, torch >= 2.7) --- | ||
| if distributed_checkpoint: | ||
| self.save_distributed_checkpoint(model_to_save, save_directory) |
There was a problem hiding this comment.
| self.save_distributed_checkpoint(model_to_save, save_directory) | |
| self.save_distributed_checkpoint(model_to_save, save_directory, push_to_hub, save_on_this_rank) |
| if push_to_hub and save_on_this_rank: | ||
| model_card = create_and_tag_model_card(repo_id, self.model_tags, token=token) | ||
| model_card.save(os.path.join(save_directory, "README.md")) | ||
| self._upload_modified_files( | ||
| save_directory, | ||
| repo_id, | ||
| files_timestamps, | ||
| commit_message=commit_message, | ||
| token=token, | ||
| create_pr=create_pr, | ||
| ) | ||
| return |
There was a problem hiding this comment.
| if push_to_hub and save_on_this_rank: | |
| model_card = create_and_tag_model_card(repo_id, self.model_tags, token=token) | |
| model_card.save(os.path.join(save_directory, "README.md")) | |
| self._upload_modified_files( | |
| save_directory, | |
| repo_id, | |
| files_timestamps, | |
| commit_message=commit_message, | |
| token=token, | |
| create_pr=create_pr, | |
| ) | |
| return |
|
|
||
| # Get the model state_dict | ||
| # --- FSDP path: DCP save (large models, torch >= 2.7) --- | ||
| if distributed_checkpoint: |
There was a problem hiding this comment.
anything happening in this if -> in a separated function
| filename = os.path.join(save_directory, shard_file) | ||
| shard_state_dict = {} | ||
| for tensor_name in tensor_names: | ||
| # Get the tensor, and remove it from state_dict to avoid keeping the ref | ||
| tensor = state_dict.pop(tensor_name) | ||
|
|
||
| # If the param was offloaded, we need to load it back from disk to resave it. It's a strange pattern, | ||
| # but it would otherwise not be contained in the saved shard if we were to simply move the file | ||
| # or something | ||
| if is_offloaded and tensor.device.type == "meta": | ||
| tensor = load_offloaded_parameter(model_to_save, tensor_name) | ||
|
|
||
| # only do contiguous after it's permuted correctly in case of TP | ||
| shard_state_dict[tensor_name] = tensor.contiguous() | ||
|
|
||
| # TODO: it would be very nice to do the writing concurrently, but safetensors never releases the GIL, | ||
| # so it's not possible for now.... | ||
| # Write the shard to disk | ||
| safe_save_file(shard_state_dict, filename, metadata=metadata) | ||
| # Cleanup the data before next loop (important with offloading, so we don't blowup cpu RAM) | ||
| del shard_state_dict | ||
|
|
||
| if index is None: | ||
| path_to_weights = os.path.join(save_directory, weights_name) | ||
| logger.info(f"Model weights saved in {path_to_weights}") | ||
| else: | ||
| save_index_file = SAFE_WEIGHTS_INDEX_NAME | ||
| save_index_file = os.path.join(save_directory, _add_variant(save_index_file, variant)) | ||
| # Save the index as well | ||
| with open(save_index_file, "w", encoding="utf-8") as f: | ||
| content = json.dumps(index, indent=2, sort_keys=True) + "\n" | ||
| f.write(content) | ||
| logger.info( | ||
| f"The model is bigger than the maximum size per checkpoint ({max_shard_size}) and is going to be " | ||
| f"split in {len(state_dict_split.filename_to_tensors)} checkpoint shards. You can find where each parameters has been saved in the " | ||
| f"index located at {save_index_file}." |
There was a problem hiding this comment.
should be in distributed
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