Skip to content

[port] Megatron-Core APIs needed by Megatron-Bridge main (#4452 MLA resolver, gdn alias, #5865 FSDP names) - #102

Merged
yushengsu-thu merged 3 commits into
radixark:miles-mainfrom
yushengsu-thu:port/bridge-new-mla-qk-norm-resolver
Sep 23, 2026
Merged

yushengsu-thu merged 3 commits into
radixark:miles-mainfrom
yushengsu-thu:port/bridge-new-mla-qk-norm-resolver

Conversation

@yushengsu-thu

@yushengsu-thu yushengsu-thu commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Megatron-Bridge main (radixark bridge-new @ 2e09c234a) needs three things from Megatron-Core that miles-main does not have. Required by radixark/miles#3291.

Commits

  1. megatron.core.transformer.mla_qk_norm_config + megatron.core.models.backends.get_backend: the MLA half of Port Multi-Latent Attention to HybridModel NVIDIA/Megatron-LM#4452 (HybridModel half not ported; absorbed_mla.py keeps miles-main's combined/split K/V up-projection structure).
  2. experimental_attention_variant="gdn" accepted as an alias of gated_delta_net (upstream Bump FLA to 0.5.1 and integrate GDN2 kernels NVIDIA/Megatron-LM#5765 rename; Megatron-Bridge selects Qwen3.5 linear attention with the new name).
  3. FullyShardedDataParallelV1 (= FullyShardedDataParallel) and a non-constructible FullyShardedDataParallelV2 placeholder in mcore_fsdp_adapter.py: the names Add MFSDP v2 MCore integration with FullyShardedOptimizer NVIDIA/Megatron-LM#5865 introduced and Megatron-Bridge's unwrap_model imports since fix(fsdp): drop stale MFSDP v2 guard rails for clip_grad, precision-aware optimizer, HSDP, and optimizer CUDA graphs NVIDIA-NeMo/Megatron-Bridge#5933. MFSDP v2 itself is not ported.

Validation

  • Commits 1–2: 2× RTX 4090, bridge-new unit tests (deepseek, glm_moe_dsa, kimi, model_bridge, peft) 695 passed / 14 skipped, identical to NVIDIA main @ f6c33bde4; Megatron-LM MLA tests unchanged (8 passed / 1 pre-existing failure). ci(docker): bump Megatron-Bridge to bridge-new (CI probe) and testing miles#3291 rounds 2–7 run against this PR via ci-megatron-pr: #102; bridge-mode LoRA / multi-LoRA / GLM / Nemotron suites green.
  • Commit 3: tests/unit_tests/distributed/mfsdp_v1/test_mcore_fsdp_adapter_names.py (alias + placeholder class; not yet run on GPU).

…#4452)

Megatron-Bridge main (radixark bridge-new) imports
megatron.core.transformer.mla_qk_norm_config.get_backend and overrides
MLASelfAttention._resolve_qk_norm_config in its DeepSeek/GLM-5 attention
(models/deepseek/attention.py); on miles-main the import fails and the hook
does not exist, so every Bridge-backed path (LoRA, multi-LoRA, bridge-mode
weight update) dies at import.

Ported from upstream 4464d1c "Port Multi-Latent Attention to HybridModel
(NVIDIA#4452)", MLA half only:

* new megatron/core/transformer/mla_qk_norm_config.py (identical to upstream
  main @ f6c33bd)
* MLASelfAttention / FusedMLASelfAttention build q/kv norms and up-projections
  through _resolve_qk_norm_config -> QKNormConfigResolver
* AbsorbedMLA does the same; the combined K/V up-projection goes through the
  resolver, the miles-main split K/V path keeps its explicit submodules
* backends.get_backend(transformer_impl) (Literal import)
* the qk_layernorm test refactors that applied cleanly

Not ported: the HybridModel MLA support (hybrid_block / hybrid_layer_allocation /
hybrid_layer_specs, Symbols.MLA in arguments.py, hybrid tests) -- it conflicts
with the miles-main hybrid code and nothing in miles uses HybridModel MLA.

(cherry picked from commit 4464d1c, hybrid-model parts dropped)

Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
…a_net

Upstream Megatron-LM renamed the Gated DeltaNet variant to "gdn" (NVIDIA#5765, with
"gated_delta_net" kept as a deprecated alias). Megatron-Bridge main selects
Qwen3.5 linear attention with the new name, so on miles-main every Qwen3.5
model build died in get_experimental_attention_variant_module_spec with
"Invalid experimental attention variant: gdn".

miles-main keeps "gated_delta_net" as the canonical spelling and now treats
"gdn" as an alias:

* transformer_config: Literal accepts "gdn"; __post_init__ canonicalizes it;
  is_gated_delta_net_variant() replaces the literal comparisons so a value
  assigned after construction (Bridge sets it on the provider and never
  re-runs __post_init__) is recognized as well.
* experimental_attention_variant_module_specs: dispatch and
  is_linear_attention_variant go through the helper, which is also exported
  from this module (Megatron-Bridge looks it up there).
* training.py FLOP accounting accepts both spellings.

"gdn2" (GDN2 kernels, upstream NVIDIA#5765) is intentionally not accepted: miles-main
has no implementation for it and should keep failing loudly.

Verified with Megatron-Bridge bridge-new: a GPTModelProvider configured like
qwen35_bridge (experimental_attention_variant="gdn", linear_attention_freq=4,
transformer_layer_spec=get_transformer_block_with_experimental_attention_variant_spec)
raised the ValueError before and now resolves to GatedDeltaNet layers.

Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
@Zhichenzzz

Copy link
Copy Markdown
Collaborator

LGTHuman, lets wait for the ci pass of radixark/miles#3291

…for Megatron-Bridge

Upstream Megatron-Core split Megatron-FSDP into FullyShardedDataParallelV1 (the
implementation miles-main ships as FullyShardedDataParallel) and
FullyShardedDataParallelV2, and turned FullyShardedDataParallel into a factory
(NVIDIA#5865). Megatron-Bridge main imports both names from
mcore_fsdp_adapter since NVIDIA-NeMo/Megatron-Bridge#5933
(conversion/utils.py::unwrap_model, training/setup.py, training/train.py), so on
miles-main every bridge-mode weight load / export raised ImportError with
bridge-new @ 2e09c234a.

Add FullyShardedDataParallelV1 as an alias of the existing class and a
FullyShardedDataParallelV2 placeholder whose constructor raises
NotImplementedError. The v2 implementation is not ported and
FullyShardedDataParallel stays the v1 class, so the existing isinstance checks
in megatron/core/utils.py and megatron/training/training.py are unchanged.

Signed-off-by: Yusheng Su <yushengsu.thu@gmail.com>
@yushengsu-thu
yushengsu-thu force-pushed the port/bridge-new-mla-qk-norm-resolver branch from 365ba82 to 00fac22 Compare September 18, 2026 18:47
@yushengsu-thu yushengsu-thu changed the title [port] MLA QK-norm resolver and backends.get_backend (upstream #4452) [port] Megatron-Core APIs needed by Megatron-Bridge main (#4452 MLA resolver, gdn alias, #5865 FSDP names) Sep 18, 2026

@Zhichenzzz Zhichenzzz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTHuman, please check the cc comments 👇

@Zhichenzzz

Copy link
Copy Markdown
Collaborator

Reviewed against upstream NVIDIA/Megatron-LM@main and Megatron-Bridge bridge-new @ 2e09c234a (the consumer this port targets). Overall the port is faithful; a few things worth addressing or knowing about before merge.

1. Behavior change for MLA with q_lora_rank=None (not hit by any shipped miles recipe).
QKNormConfigResolver._mla_fused_linear_or_default returns the backend's fused norm+linear whenever the spec's linear_q_proj is not already fused. miles-main's gpt_layer_specs MLA spec passes linear_q_proj=backend.column_parallel_linear(), so with TE + qk_layernorm=True + no query LoRA, linear_q_proj silently changes from TEColumnParallelLinear to TELayerNormColumnParallelLinear and gains a layer_norm_weight with no HF counterpart. The path is _resolve_mla_qk_layernorm -> else branch (no q-LoRA) -> _mla_fused_linear_or_default(submodules.linear_q_proj) -> _require_linear(self.fused_norm_linear_impl), which ignores the non-fused class the spec asked for.
All 12 MLA recipes in scripts/models/ set --q-lora-rank, so none are affected today. The exposed path is miles' megatron_to_hf/kimi_vl.py (Moonlight-style, no q-LoRA), which only maps linear_q_proj.weight. This is upstream's chosen behavior (Megatron-Bridge added MLASelfAttentionWithoutQueryNorm to work around it); worth a line in the PR body and, ideally, a guard or converter update on the miles side.

2. attention_latent_norm_epsilon is not ported.
Upstream MLATransformerConfig has attention_latent_norm_epsilon: float | None = None, defaulted to layernorm_epsilon in __post_init__. Megatron-Bridge's MLASelfAttentionWithoutQueryNorm._resolve_qk_norm_config does hasattr(self.config, "attention_latent_norm_epsilon") and, when missing, replaces self.config with a SimpleNamespace(**vars(self.config), ...) for the rest of the module's life. That fallback only runs on the no-q-LoRA DeepSeek path, but it is a 4-line port to make it never run. Suggest adding the field + the __post_init__ default.

3. get_backend is ported with the older single-argument signature.
Upstream main now takes use_kitchen, use_te_op_fuser, cross_entropy_*, cuda_graph_impl and has get_backend_from_config. Bridge at 2e09c234a only calls get_backend(transformer_impl), so this is fine for the pinned commit; the next bridge-new bump may need the wider signature. Not a blocker.

4. Minor consistency nit.
megatron/training/training.py:1030 uses a literal in ("gated_delta_net", "gdn") while every other site goes through is_gated_delta_net_variant. Harmless, but the alias now has two sources of truth.

Checked and clean

  • mla_qk_norm_config.py is byte-identical to upstream main; MLA / FusedMLA / AbsorbedMLA wiring matches upstream line for line, and the _resolve_qk_norm_config hook has the exact signature Bridge's DeepSeek subclass overrides.
  • The backend API the resolver needs (layer_norm(rms_norm, for_qk), column_parallel_layer_norm_linear, column_parallel_linear) exists on all three miles-main providers.
  • Existing miles MLA specs (DeepSeek-V3/V3.2, GLM-5.x, Kimi-K2, JoyAI) pass the resolver's new validation: fused up-projections with IdentityOp norms is exactly the accepted MLA form. glm5 (DSAMLASelfAttention) and deepseek_v4 build their own attention and never enter the resolver.
  • gdn alias: __post_init__ canonicalizes to gated_delta_net, Bridge's finalize() re-runs MCoreTransformerConfig.__post_init__ after provider.experimental_attention_variant = "gdn", and Bridge has no == "gdn" comparisons on the variant afterwards. No literal gated_delta_net comparisons remain outside transformer_config.py/training.py.
  • FSDP names: V1 is FullyShardedDataParallel, V2 importable and non-constructible; matches what Bridge's unwrap_model / setup.py / train.py import and isinstance against. Test is CPU-runnable.
  • Test edits to test_schedule_layer_1f1b.py / test_finalize_model_grads.py / test_submodule_callables.py only parametrize qk_layernorm; nothing previously valid was removed.

@yushengsu-thu
yushengsu-thu merged commit 8a5dbe5 into radixark:miles-main Sep 23, 2026
1 check passed
HJSang added a commit that referenced this pull request Oct 9, 2026
Re-based directly on miles-main (no longer stacked on GDN2, which was
closed as unadopted by any open-source model). Same content as the
original port, re-derived from a fresh cherry-pick of upstream 788e9e1
so this PR's diff only carries KDA, not GDN2.

Adds experimental_attention_variant="kda": a channel-wise Gated DeltaNet
variant with direct Q/K/V/F/G projections and a separate beta projection,
backed by fla.ops.kda.chunk_kda. Also brings in the generic primitives this
commit packages alongside KDA: MLA output gating (attention_output_gate /
gated_attention_proj_granularity), a HybridModel-aware qk_clip traversal,
and a MoE router metric-indexing refactor.

kda.py itself needed no hand-adaptation: it was already authored upstream
against the same headwise/chunkwise-CP, selective-recompute base class our
common.py/gdn.py carry, so it applies as a clean add.

Kept the earlier decision not to eagerly normalize
experimental_attention_variant from "gated_delta_net" to "gdn" in
__post_init__ (re-introduced by this upstream commit): our fork's
dsv4_hybrid/gated_delta_net validation checks still key off the literal
"gated_delta_net" string, so mutating it before those checks run would
silently disable them. is_gated_delta_net_variant (non-mutating,
canonically defined in transformer_config.py per #102/#108) is used
everywhere a GDN/KDA family check is needed instead.

Adopted upstream's `not self.is_hybrid_model` condition in place of a
narrower "only gated_delta_net requires linear_attention_freq" check for
the same requirement, since it is the more general form already present
in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UawmJvYe7WkXApRw26ZFMd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants