Skip to content

Commit 6a96243

Browse files
committed
Address CodeRabbit review feedback on GDN FLOPs calculator
- Make GDN config strict: raise ValueError when linear_attention_freq is None, raise TypeError on invalid types, assert list length matches num_layers (mirrors existing MoE validation pattern) - Replace getattr fallbacks for GDN dims with direct attribute access so missing fields fail explicitly - Add @pytest.mark.unit to TestGDNLayerFlops class - Strengthen test_gdn_layer_freq_list to verify 6/2 split by comparing against equivalent int freq=3 and against pure-standard baseline Signed-off-by: Chen Cui <chcui@nvidia.com>
1 parent ec08ba0 commit 6a96243

2 files changed

Lines changed: 64 additions & 39 deletions

File tree

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

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -388,45 +388,55 @@ def transformer_flops():
388388
# weighted sum of GDN and standard-attention per-layer costs.
389389
experimental_attention_variant = getattr(cfg.model, "experimental_attention_variant", None)
390390
if experimental_attention_variant == "gated_delta_net":
391-
linear_attention_freq = getattr(cfg.model, "linear_attention_freq", None)
392-
if linear_attention_freq is not None:
393-
if isinstance(linear_attention_freq, int):
394-
linear_attention_pattern = [
395-
0 if ((i + 1) % linear_attention_freq == 0) else 1 for i in range(num_layers)
396-
]
397-
elif isinstance(linear_attention_freq, list):
398-
linear_attention_pattern = list(linear_attention_freq)
399-
else:
400-
linear_attention_pattern = [1] * num_layers
401-
402-
num_gdn_layers = sum(linear_attention_pattern)
403-
num_standard_attn_layers = num_layers - num_gdn_layers
404-
405-
standard_self_attn_per_layer = self_attn_term / num_layers if num_layers > 0 else 0
406-
407-
qk_head_dim = getattr(cfg.model, "linear_key_head_dim", 128)
408-
v_head_dim = getattr(cfg.model, "linear_value_head_dim", 128)
409-
num_qk_heads = getattr(cfg.model, "linear_num_key_heads", 16)
410-
num_v_heads = getattr(cfg.model, "linear_num_value_heads", 48)
411-
conv_kernel_dim = getattr(cfg.model, "linear_conv_kernel_dim", 4)
412-
413-
qk_dim = qk_head_dim * num_qk_heads
414-
v_dim = v_head_dim * num_v_heads
415-
416-
gdn_self_attn_per_layer = (
417-
3
418-
* 2
419-
* (
420-
cfg.model.hidden_size * (2 * qk_dim + 2 * v_dim + 2 * num_v_heads)
421-
+ conv_kernel_dim * (2 * qk_dim + v_dim)
422-
+ num_v_heads * (v_head_dim**2) * 4
423-
+ cfg.model.hidden_size * v_dim
424-
)
391+
linear_attention_freq = cfg.model.linear_attention_freq
392+
if linear_attention_freq is None:
393+
raise ValueError(
394+
"linear_attention_freq must be set when experimental_attention_variant='gated_delta_net'"
395+
)
396+
if isinstance(linear_attention_freq, int):
397+
linear_attention_pattern = [
398+
0 if ((i + 1) % linear_attention_freq == 0) else 1 for i in range(num_layers)
399+
]
400+
elif isinstance(linear_attention_freq, list):
401+
linear_attention_pattern = list(linear_attention_freq)
402+
assert len(linear_attention_pattern) == num_layers, (
403+
f"Invalid length of linear_attention_pattern: {len(linear_attention_pattern)}, "
404+
f"expected {num_layers}, "
405+
f"current linear_attention_freq: {linear_attention_freq}"
406+
)
407+
else:
408+
raise TypeError(
409+
f"linear_attention_freq must be int or list, got {type(linear_attention_freq).__name__}"
425410
)
426411

427-
self_attn_term = (
428-
gdn_self_attn_per_layer * num_gdn_layers + standard_self_attn_per_layer * num_standard_attn_layers
412+
num_gdn_layers = sum(linear_attention_pattern)
413+
num_standard_attn_layers = num_layers - num_gdn_layers
414+
415+
standard_self_attn_per_layer = self_attn_term / num_layers if num_layers > 0 else 0
416+
417+
qk_head_dim = cfg.model.linear_key_head_dim
418+
v_head_dim = cfg.model.linear_value_head_dim
419+
num_qk_heads = cfg.model.linear_num_key_heads
420+
num_v_heads = cfg.model.linear_num_value_heads
421+
conv_kernel_dim = cfg.model.linear_conv_kernel_dim
422+
423+
qk_dim = qk_head_dim * num_qk_heads
424+
v_dim = v_head_dim * num_v_heads
425+
426+
gdn_self_attn_per_layer = (
427+
3
428+
* 2
429+
* (
430+
cfg.model.hidden_size * (2 * qk_dim + 2 * v_dim + 2 * num_v_heads)
431+
+ conv_kernel_dim * (2 * qk_dim + v_dim)
432+
+ num_v_heads * (v_head_dim**2) * 4
433+
+ cfg.model.hidden_size * v_dim
429434
)
435+
)
436+
437+
self_attn_term = (
438+
gdn_self_attn_per_layer * num_gdn_layers + standard_self_attn_per_layer * num_standard_attn_layers
439+
)
430440

431441
padded_vocab_size = calculate_padded_vocab_size(
432442
cfg.model.vocab_size,

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ def test_swiglu_scaling_factor(self):
388388
assert flops_swiglu == expected_swiglu, f"SwiGLU: expected {expected_swiglu:.2e} but got {flops_swiglu:.2e}"
389389

390390

391+
@pytest.mark.unit
391392
class TestGDNLayerFlops:
392393
"""Tests for Gated DeltaNet (GDN) FLOPs calculation in transformer_flops path."""
393394

@@ -487,10 +488,10 @@ def test_gdn_only_layers(self):
487488
)
488489

489490
def test_gdn_layer_freq_list(self):
490-
"""Test GDN with linear_attention_freq as a list pattern."""
491+
"""Test GDN with linear_attention_freq as a list pattern (6 GDN, 2 standard)."""
491492
batch_size = 1
492-
# [1, 1, 0, 1, 1, 0, 1, 1] means 6 GDN, 2 standard
493-
freq_list = [1, 1, 0, 1, 1, 0, 1, 1]
493+
freq_list = [1, 1, 0, 1, 1, 0, 1, 1] # 6 GDN, 2 standard
494+
assert sum(freq_list) == 6
494495
model_cfg = self._qwen35_27b_config(
495496
num_layers=8,
496497
linear_attention_freq=freq_list,
@@ -499,6 +500,20 @@ def test_gdn_layer_freq_list(self):
499500
flops = num_floating_point_operations(cfg, batch_size=batch_size)
500501
assert flops > 0
501502

503+
# Verify the mask is actually applied: must differ from pure standard attention.
504+
baseline_cfg = MockConfigContainer(
505+
model=self._qwen35_27b_config(num_layers=8, experimental_attention_variant=None)
506+
)
507+
baseline_flops = num_floating_point_operations(baseline_cfg, batch_size=batch_size)
508+
assert flops != baseline_flops, "List-based GDN mask should differ from pure standard attention"
509+
510+
# freq_list [1,1,0,1,1,0,1,1] is identical to the pattern generated by int freq=3.
511+
int_freq_cfg = MockConfigContainer(model=self._qwen35_27b_config(num_layers=8, linear_attention_freq=3))
512+
int_freq_flops = num_floating_point_operations(int_freq_cfg, batch_size=batch_size)
513+
assert flops == int_freq_flops, (
514+
"List [1,1,0,1,1,0,1,1] should produce the same FLOPs as int freq=3 (equivalent 6/2 split)"
515+
)
516+
502517
def test_gdn_exact_self_attn_term(self):
503518
"""Verify the GDN self_attn_term matches the expected formula from Megatron-LM."""
504519
batch_size = 1

0 commit comments

Comments
 (0)