Skip to content

Commit 34d8d4b

Browse files
fix(training): correct parameter norm reporting with GTP (#6133)
Signed-off-by: ykarnati <ykarnati@nvidia.com>
1 parent c30562d commit 34d8d4b

2 files changed

Lines changed: 153 additions & 5 deletions

File tree

‎src/megatron/bridge/training/utils/train_utils.py‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from megatron.core.transformer.module import MegatronModule
3131
from megatron.core.transformer.moe.moe_utils import track_moe_metrics
3232
from megatron.core.transformer.multi_token_prediction import MTPLossLoggingHelper
33-
from megatron.core.utils import get_data_parallel_group_if_dtensor, to_local_if_dtensor
33+
from megatron.core.utils import get_data_parallel_group_if_dtensor, get_pg_rank, to_local_if_dtensor
3434

3535
from megatron.bridge.models.common.heads import (
3636
LinearForLastLayer as LinearForLastLayer,
@@ -237,20 +237,31 @@ def calc_params_l2_norm(
237237
sharded_moe_params_data = []
238238
data_parallel_group = None
239239
pg_collection = get_pg_collection(model)
240+
gtp_rank = get_pg_rank(pg_collection.gtp_remat)
241+
expert_gtp_group = pg_collection.expt_gtp_remat
242+
expert_gtp_rank = get_pg_rank(expert_gtp_group)
240243

241244
for model_chunk in model:
242245
for param in model_chunk.parameters():
243246
data_parallel_group = get_data_parallel_group_if_dtensor(param, data_parallel_group)
247+
is_gtp = getattr(param, "is_gtp_weight_remat", False)
244248
# MCore uses allreduce=False to mark parameters that use expert-parallel process groups.
245249
uses_expert_parallel_groups = not getattr(param, "allreduce", True)
246-
is_not_tp_duplicate = param_is_not_tensor_parallel_duplicate(
250+
# GTP parameters are unique across TP ranks. Other parameters still need TP filtering.
251+
if not is_gtp and not param_is_not_tensor_parallel_duplicate(
247252
param,
248253
tp_group=pg_collection.tp,
249254
expert_tp_group=pg_collection.expt_tp,
250-
)
251-
if not is_not_tp_duplicate:
255+
):
256+
continue
257+
258+
# Parameters that are not GTP-sharded are replicated across the corresponding GTP axis.
259+
if uses_expert_parallel_groups:
260+
if not is_gtp and expert_gtp_rank != 0:
261+
continue
262+
elif not is_gtp and gtp_rank != 0:
252263
continue
253-
assert is_not_tp_duplicate
264+
254265
if uses_expert_parallel_groups:
255266
assert param_is_not_shared(param)
256267
param = to_local_if_dtensor(param)
@@ -360,6 +371,15 @@ def calc_params_l2_norm(
360371
)
361372
moe_norm_2 += sharded_moe_norm_2
362373

374+
# Expert model parallel excludes expert GTP. This reduction collects both unique GTP shards
375+
# and ordinary expert parameters, which are counted only on expert GTP rank zero above.
376+
if expert_gtp_group is not None:
377+
torch.distributed.all_reduce(
378+
moe_norm_2,
379+
op=torch.distributed.ReduceOp.SUM,
380+
group=expert_gtp_group,
381+
)
382+
363383
# Reduce norm across model parallel groups (dense and expert).
364384
# Dense params should sum across all model-parallel GPUs (tensor + pipeline).
365385
dense_reduce_group = pg_collection.mp

‎tests/unit_tests/training/utils/test_train_utils.py‎

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2718,7 +2718,9 @@ def __init__(self):
27182718
# Minimal set of groups used by calc_params_l2_norm
27192719
self.dp_cp = object()
27202720
self.expt_dp = object()
2721+
self.expt_gtp_remat = None
27212722
self.expt_tp = object()
2723+
self.gtp_remat = object()
27222724
self.mp = object()
27232725
self.tp = object()
27242726
self.tp_ep_pp = object()
@@ -2762,6 +2764,128 @@ def mock_model_config_bf16(self):
27622764
config.bf16 = True
27632765
return config
27642766

2767+
def test_gtp_param_norm_filters_replicas_and_keeps_shards(
2768+
self,
2769+
monkeypatch,
2770+
mock_model_config_fp32,
2771+
_patch_pg_collection,
2772+
):
2773+
"""Non-sharded parameters are counted once while every GTP shard contributes."""
2774+
2775+
class _DenseGTPModel(torch.nn.Module):
2776+
def __init__(self):
2777+
super().__init__()
2778+
self.norm = torch.nn.Parameter(torch.tensor([2.0], device="cuda"))
2779+
self.weight = torch.nn.Parameter(torch.tensor([3.0], device="cuda"))
2780+
self.weight.is_gtp_weight_remat = True
2781+
2782+
model = _DenseGTPModel()
2783+
duplicate_filter = mock.MagicMock(return_value=True)
2784+
2785+
def fake_all_reduce(tensor, op, group):
2786+
del op
2787+
if group is _patch_pg_collection.mp:
2788+
# Rank zero contributes the replicated norm (2**2) and its GTP shard (4**2).
2789+
tensor.add_(20.0)
2790+
2791+
monkeypatch.setattr(
2792+
"megatron.bridge.training.utils.train_utils.get_data_parallel_group_if_dtensor",
2793+
lambda param, group: None,
2794+
)
2795+
monkeypatch.setattr(
2796+
"megatron.bridge.training.utils.train_utils.param_is_not_tensor_parallel_duplicate",
2797+
duplicate_filter,
2798+
)
2799+
monkeypatch.setattr(
2800+
"megatron.bridge.training.utils.train_utils.to_local_if_dtensor",
2801+
lambda param: param,
2802+
)
2803+
monkeypatch.setattr(
2804+
"megatron.bridge.training.utils.train_utils.get_pg_rank",
2805+
lambda group: 1 if group is _patch_pg_collection.gtp_remat else 0,
2806+
)
2807+
monkeypatch.setattr("torch.distributed.get_process_group_ranks", lambda group: [0])
2808+
monkeypatch.setattr("torch.distributed.all_reduce", fake_all_reduce)
2809+
2810+
actual_norm = calc_params_l2_norm(model, mock_model_config_fp32)
2811+
2812+
assert actual_norm == pytest.approx(math.sqrt(29.0))
2813+
duplicate_filter.assert_called_once_with(
2814+
model.norm,
2815+
tp_group=_patch_pg_collection.tp,
2816+
expert_tp_group=_patch_pg_collection.expt_tp,
2817+
)
2818+
2819+
@pytest.mark.parametrize(
2820+
("expert_gtp_rank", "remote_contribution", "expected_local_input"),
2821+
[
2822+
pytest.param(0, 9.0, 20.0, id="rank-zero-keeps-replica"),
2823+
pytest.param(1, 13.0, 16.0, id="nonzero-rank-drops-replica"),
2824+
],
2825+
)
2826+
def test_expert_gtp_param_norm_reduces_over_expert_gtp(
2827+
self,
2828+
monkeypatch,
2829+
mock_model_config_fp32,
2830+
_patch_pg_collection,
2831+
expert_gtp_rank,
2832+
remote_contribution,
2833+
expected_local_input,
2834+
):
2835+
"""Expert GTP shards are summed while replicated parameters contribute once."""
2836+
2837+
class _ExpertGTPModel(torch.nn.Module):
2838+
def __init__(self):
2839+
super().__init__()
2840+
self.weight = torch.nn.Parameter(torch.tensor([4.0], device="cuda"))
2841+
self.weight.allreduce = False
2842+
self.weight.is_gtp_weight_remat = True
2843+
self.bias = torch.nn.Parameter(torch.tensor([2.0], device="cuda"))
2844+
self.bias.allreduce = False
2845+
2846+
_patch_pg_collection.expt_gtp_remat = object()
2847+
model = _ExpertGTPModel()
2848+
duplicate_filter = mock.MagicMock(return_value=True)
2849+
expert_gtp_inputs = []
2850+
2851+
def fake_all_reduce(tensor, op, group):
2852+
del op
2853+
if group is _patch_pg_collection.expt_gtp_remat:
2854+
expert_gtp_inputs.append(tensor.item())
2855+
tensor.add_(remote_contribution)
2856+
2857+
monkeypatch.setattr(
2858+
"megatron.bridge.training.utils.train_utils.get_data_parallel_group_if_dtensor",
2859+
lambda param, group: None,
2860+
)
2861+
monkeypatch.setattr(
2862+
"megatron.bridge.training.utils.train_utils.param_is_not_tensor_parallel_duplicate",
2863+
duplicate_filter,
2864+
)
2865+
monkeypatch.setattr(
2866+
"megatron.bridge.training.utils.train_utils.to_local_if_dtensor",
2867+
lambda param: param,
2868+
)
2869+
monkeypatch.setattr(
2870+
"megatron.bridge.training.utils.train_utils.get_pg_rank",
2871+
lambda group: expert_gtp_rank if group is _patch_pg_collection.expt_gtp_remat else 0,
2872+
)
2873+
monkeypatch.setattr(
2874+
"torch.distributed.get_process_group_ranks",
2875+
lambda group: [0] if group is _patch_pg_collection.mp else [1],
2876+
)
2877+
monkeypatch.setattr("torch.distributed.all_reduce", fake_all_reduce)
2878+
2879+
actual_norm = calc_params_l2_norm(model, mock_model_config_fp32)
2880+
2881+
assert expert_gtp_inputs == pytest.approx([expected_local_input])
2882+
assert actual_norm == pytest.approx(math.sqrt(29.0))
2883+
duplicate_filter.assert_called_once_with(
2884+
model.bias,
2885+
tp_group=_patch_pg_collection.tp,
2886+
expert_tp_group=_patch_pg_collection.expt_tp,
2887+
)
2888+
27652889
@mock.patch("megatron.bridge.training.utils.train_utils.get_data_parallel_group_if_dtensor")
27662890
@mock.patch("megatron.bridge.training.utils.train_utils.param_is_not_tensor_parallel_duplicate")
27672891
@mock.patch("megatron.bridge.training.utils.train_utils.to_local_if_dtensor")
@@ -3000,6 +3124,8 @@ def fake_all_reduce(tensor, op, group):
30003124
tensor.add_(12.0)
30013125
elif group is _patch_pg_collection.expt_dp:
30023126
tensor.add_(16.0)
3127+
elif group is _patch_pg_collection.expt_gtp_remat:
3128+
pass
30033129
elif group is not _patch_pg_collection.mp:
30043130
raise AssertionError("unexpected reduction group")
30053131

@@ -3212,6 +3338,8 @@ def __init__(self):
32123338
expt_tp=expert_tp_group,
32133339
dp_cp=reduce_group,
32143340
expt_dp=reduce_group,
3341+
expt_gtp_remat=reduce_group,
3342+
gtp_remat=reduce_group,
32153343
mp=reduce_group,
32163344
tp_ep_pp=reduce_group,
32173345
)

0 commit comments

Comments
 (0)