Skip to content

Commit 923f894

Browse files
committed
fix(muse-glimmer): decouple TE norms from attention backend
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
1 parent bfc486f commit 923f894

4 files changed

Lines changed: 43 additions & 21 deletions

File tree

‎skills/adding-model-support/SKILL.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ Transformer Engine version pinned by Megatron-Core. Use the backend-provided `TE
185185
corresponding TE module) for affine LayerNorm and RMSNorm whenever TE implements the required
186186
semantics. Preserve `eps`, parameter dtype/device initialization, sequence-parallel metadata, and
187187
`zero_centered_gamma`; standard-gamma and zero-centered-gamma norms are not interchangeable.
188+
The attention/linear backend and normalization choice need not be coupled: a local attention spec
189+
may still use `TENorm`. Retain a semantics-equivalent local fallback when model construction must
190+
work in an environment where Transformer Engine itself is unavailable.
188191

189192
Do not introduce a dummy affine parameter merely to reach a fused kernel. For example, current TE
190193
RMSNorm always owns a learnable `weight`, so a genuinely weightless/scaleless RMSNorm must retain a

‎src/megatron/bridge/models/muse_glimmer/modeling_muse_glimmer.py‎

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import torch.nn as nn
2525
import torch.nn.functional as F
2626
from megatron.core import parallel_state
27+
from megatron.core.extensions.transformer_engine import HAVE_TE, TENorm
2728
from megatron.core.fusions.fused_bias_dropout import get_bias_dropout_add
2829
from megatron.core.models.backends import get_backend
2930
from megatron.core.models.hybrid.hybrid_block import HybridStack, HybridStackSubmodules
@@ -132,20 +133,18 @@ def forward(self, hidden_states: Tensor) -> Tensor:
132133

133134

134135
def _centered_rms_norm_cls(config: TransformerConfig) -> type[nn.Module]:
135-
"""Select the backend-native centered RMSNorm when it supports Muse semantics."""
136-
if config.transformer_impl == "local":
137-
return MuseGlimmerCenteredRMSNorm
138-
return get_backend(config.transformer_impl).layer_norm(rms_norm=True)
136+
"""Use TE for affine normalization whenever it is available."""
137+
return TENorm if HAVE_TE else MuseGlimmerCenteredRMSNorm
139138

140139

141140
def _standard_rms_norm(config: TransformerConfig, hidden_size: int, eps: float) -> nn.Module:
142-
"""Build a standard-gamma RMSNorm with the configured model backend."""
143-
if config.transformer_impl == "local":
141+
"""Build a standard-gamma RMSNorm with TE when it is available."""
142+
if not HAVE_TE:
144143
return MuseGlimmerRMSNorm(config, hidden_size, eps=eps)
145144

146145
norm_config = copy(config)
147146
norm_config.layernorm_zero_centered_gamma = False
148-
return get_backend(config.transformer_impl).layer_norm(rms_norm=True)(
147+
return TENorm(
149148
norm_config,
150149
hidden_size,
151150
eps=eps,
@@ -156,15 +155,15 @@ def _vision_layer_norm(
156155
config: MuseGlimmerVisionModelConfig,
157156
transformer_config: TransformerConfig,
158157
) -> nn.Module:
159-
"""Build the vision LayerNorm with TE unless the local backend is explicit."""
160-
if transformer_config.transformer_impl == "local":
158+
"""Build the vision LayerNorm with TE when it is available."""
159+
if not HAVE_TE:
161160
return nn.LayerNorm(config.hidden_size, eps=config.layer_norm_epsilon)
162161

163162
norm_config = copy(transformer_config)
164163
norm_config.normalization = "LayerNorm"
165164
norm_config.layernorm_zero_centered_gamma = False
166165
norm_config.sequence_parallel = False
167-
return get_backend(transformer_config.transformer_impl).layer_norm()(
166+
return TENorm(
168167
norm_config,
169168
config.hidden_size,
170169
eps=config.layer_norm_epsilon,

‎src/megatron/bridge/models/muse_glimmer/muse_glimmer_bridge.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def hf_config_to_model_config(self, hf_config: PretrainedConfig) -> MuseGlimmerM
216216
params_dtype=params_dtype,
217217
fp16=params_dtype == torch.float16,
218218
bf16=params_dtype == torch.bfloat16,
219-
transformer_impl="transformer_engine",
219+
transformer_impl="local",
220220
output_multiplier=float(text_config.output_multiplier),
221221
final_logit_softcapping=float(text_config.final_logit_softcapping),
222222
)

‎tests/unit_tests/models/muse_glimmer/test_muse_glimmer.py‎

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_config_conversion_uses_model_config_path_only() -> None:
113113
assert isinstance(model_config, MuseGlimmerModelConfig)
114114
assert isinstance(model_config, HybridModelConfig)
115115
assert model_config.get_builder_cls() is MuseGlimmerModelBuilder
116-
assert model_config.transformer_impl == "transformer_engine"
116+
assert model_config.transformer_impl == "local"
117117
assert model_config.num_layers == 4
118118
assert model_config.hidden_size == 64
119119
assert model_config.ffn_hidden_size == 128
@@ -209,7 +209,7 @@ def test_hybrid_attention_layers_use_full_transformer_flops() -> None:
209209
@pytest.mark.parametrize(
210210
("transformer_impl", "expected_qkv", "expected_attention", "expected_norm"),
211211
[
212-
("local", ColumnParallelLinear, DotProductAttention, MuseGlimmerCenteredRMSNorm),
212+
("local", ColumnParallelLinear, DotProductAttention, TENorm),
213213
("transformer_engine", TEColumnParallelLinear, TEDotProductAttention, TENorm),
214214
],
215215
)
@@ -358,6 +358,20 @@ def test_weightless_rms_norm_does_not_add_a_transformer_engine_parameter() -> No
358358
assert norm.state_dict() == {}
359359

360360

361+
def test_affine_norms_retain_a_local_fallback_without_transformer_engine() -> None:
362+
model_config = MuseGlimmerBridge().hf_config_to_model_config(_tiny_hf_config())
363+
config = model_config.transformer
364+
365+
with patch("megatron.bridge.models.muse_glimmer.modeling_muse_glimmer.HAVE_TE", False):
366+
stack_spec = get_muse_glimmer_hybrid_stack_spec(config)
367+
final_norm = _standard_rms_norm(config, config.hidden_size, config.layernorm_epsilon)
368+
vision_model = MuseGlimmerVisionModel(model_config.vision, config)
369+
370+
assert stack_spec.submodules.attention_layer.submodules.input_layernorm is MuseGlimmerCenteredRMSNorm
371+
assert isinstance(final_norm, MuseGlimmerRMSNorm)
372+
assert isinstance(vision_model.ln_pre, torch.nn.LayerNorm)
373+
374+
361375
def test_centered_norm_sharded_state_dict_identifies_tensor_parallel_replicas() -> None:
362376
config = MuseGlimmerBridge().hf_config_to_model_config(_tiny_hf_config()).transformer
363377
norm = MuseGlimmerCenteredRMSNorm(config, hidden_size=4)
@@ -403,11 +417,12 @@ def test_tiny_vision_model_preserves_expected_token_count() -> None:
403417
model_config = MuseGlimmerBridge().hf_config_to_model_config(_tiny_hf_config())
404418
model_config.transformer_impl = "local"
405419
vision_config = model_config.vision
406-
model = MuseGlimmerVisionModel(vision_config, model_config.transformer)
407420
pixel_values = torch.randn(16, 24)
408421
grid_thw = torch.tensor([[1, 4, 4]])
409422

410-
output = model(pixel_values, grid_thw)
423+
with patch("megatron.bridge.models.muse_glimmer.modeling_muse_glimmer.HAVE_TE", False):
424+
model = MuseGlimmerVisionModel(vision_config, model_config.transformer)
425+
output = model(pixel_values, grid_thw)
411426

412427
assert output.shape == (4, 64)
413428

@@ -460,14 +475,17 @@ def test_vision_layer_recomputation_is_training_only() -> None:
460475
model_config = MuseGlimmerBridge().hf_config_to_model_config(_tiny_hf_config())
461476
model_config.transformer_impl = "local"
462477
vision_config = model_config.vision
463-
model = MuseGlimmerVisionModel(vision_config, model_config.transformer, recompute_layers=True)
464478
pixel_values = torch.randn(16, 24)
465479
grid_thw = torch.tensor([[1, 4, 4]])
466480

467-
with patch(
468-
"megatron.bridge.models.muse_glimmer.modeling_muse_glimmer.torch_checkpoint",
469-
side_effect=lambda function, *args, **kwargs: function(*args),
470-
) as checkpoint:
481+
with (
482+
patch("megatron.bridge.models.muse_glimmer.modeling_muse_glimmer.HAVE_TE", False),
483+
patch(
484+
"megatron.bridge.models.muse_glimmer.modeling_muse_glimmer.torch_checkpoint",
485+
side_effect=lambda function, *args, **kwargs: function(*args),
486+
) as checkpoint,
487+
):
488+
model = MuseGlimmerVisionModel(vision_config, model_config.transformer, recompute_layers=True)
471489
training_output = model(pixel_values, grid_thw)
472490
model.eval()
473491
inference_output = model(pixel_values, grid_thw)
@@ -524,7 +542,9 @@ def test_builder_constructs_native_hybrid_model_on_cpu(tiny_hybrid_model: MuseGl
524542
assert "decoder.layers.0.self_attention.post_layernorm.weight" in names
525543
assert "decoder.layers.0.mlp.post_layernorm.weight" in names
526544
assert not any(name.startswith("language_model.") for name in names)
527-
assert isinstance(model.decoder.final_norm, MuseGlimmerRMSNorm)
545+
assert isinstance(model.decoder.final_norm, TERMSNorm)
546+
assert isinstance(model.vision_tower.ln_pre, TELayerNorm)
547+
assert isinstance(model.vision_tower.layers[0].norm1, TELayerNorm)
528548
assert names["vision_tower.patch_embedder.patch_embedding.weight"].dtype == torch.float32
529549
assert all(
530550
getattr(parameter, "average_gradients_across_tp_domain", False)

0 commit comments

Comments
 (0)