diff --git a/src/megatron/bridge/models/conversion/param_mapping.py b/src/megatron/bridge/models/conversion/param_mapping.py index fef5d979d4..ef282f0b2d 100644 --- a/src/megatron/bridge/models/conversion/param_mapping.py +++ b/src/megatron/bridge/models/conversion/param_mapping.py @@ -993,7 +993,11 @@ def hf_to_megatron( hf_weights = hf_weights.to(target_param.dtype) actual_dim0_size = hf_weights.shape[0] - expect_dim0_size = target_param.shape[0] * self.tp_size + # DTensor.shape is already the global shape across TP ranks, while + # a regular Megatron parameter stores only its local TP shard. + expect_dim0_size = target_param.shape[0] + if not isinstance(target_param, DTensor): + expect_dim0_size *= self.tp_size if actual_dim0_size != expect_dim0_size: assert self.megatron_param in {"embedding.word_embeddings.weight", "output_layer.weight"}, ( f"{hf_weights.shape=} {target_param.shape=} {self.tp_size=} {self.megatron_param=} {self.hf_param=}" diff --git a/tests/functional_tests/test_groups/converter/test_hf_fsdp_conversion.py b/tests/functional_tests/test_groups/converter/test_hf_fsdp_conversion.py index ae92cf319f..dc4822c432 100644 --- a/tests/functional_tests/test_groups/converter/test_hf_fsdp_conversion.py +++ b/tests/functional_tests/test_groups/converter/test_hf_fsdp_conversion.py @@ -101,9 +101,8 @@ def qwen3_moe_toy_model_path(self, tmp_path_factory): @pytest.mark.parametrize( "tp,ep,nproc,test_name", [ - # CI only covers TP=1 cases. TP>1 cases depend on Megatron-LM commit - # 8cbc45b6e (PR#3191, merged 2026-04-07). (1, 1, 1, "FSDP_base"), + (2, 1, 2, "FSDP_TP2"), ], ) def test_hf_fsdp_roundtrip(self, qwen3_moe_toy_model_path, tmp_path, tp, ep, nproc, test_name):