Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fa17762
Added flextron code, training/eval example scripts, functional and un…
sheliang-nv Apr 21, 2026
cb56a0f
Removed flextron examples
sheliang-nv Apr 21, 2026
4c18286
Added Flextron overrides to load_teacher_model_config
sheliang-nv Apr 21, 2026
c38d1f1
Added all reduce Flextron router grads for PP
sheliang-nv Apr 21, 2026
3b8f6ff
Merge remote-tracking branch 'main/main' into shel/flex_merge
sheliang-nv Apr 22, 2026
892ca2f
Sync with upstream main: adopt Hybrid* naming and new pretrain() entry
sheliang-nv Apr 22, 2026
8b9ffae
Applied linter fixes
sheliang-nv Apr 22, 2026
044965d
Merge branch 'main' into shel/flex_merge
sheliang-nv Apr 22, 2026
aab70ea
Fixed multi argument wrong formatting for flextron functional test
sheliang-nv Apr 23, 2026
c03c840
Added explicit arg model type for flextron func test
sheliang-nv Apr 23, 2026
93608a8
Bugfix for when main_param is None
sheliang-nv Apr 24, 2026
3c864d3
Added golden values for functional test
sheliang-nv Apr 27, 2026
b53eba1
Removed commented out block
sheliang-nv Apr 27, 2026
e8c12da
Removed comments and dead code, redundant all reduces from loss_func.…
sheliang-nv Apr 27, 2026
e05503d
Removed hardcoded teacher config fields
sheliang-nv Apr 27, 2026
fe19e6a
Removed comments, redundant reporting_loss, and defaulted teacher fle…
sheliang-nv Apr 28, 2026
115f5b5
Elastification autoformatted
sheliang-nv Apr 28, 2026
7f2b92f
Assertion message bugfix for flextron_elasticity_hooks.py
sheliang-nv Apr 28, 2026
0ae4d4d
Update functional test to use HybridModel
sheliang-nv Apr 28, 2026
39b93a3
Raise error bugfix in pretrain_mamba_flex.py
sheliang-nv Apr 28, 2026
6b742b6
Bugfix for config.override_selected_budget
sheliang-nv Apr 28, 2026
77bd779
Uncomment budget fallback when not not set in args
sheliang-nv Apr 28, 2026
0f0ac6f
Removed dead out_proj_mask_list, moved training script to elastificat…
sheliang-nv Apr 28, 2026
f871331
Fixed typos and renamed router param attr for PP sync
sheliang-nv Apr 29, 2026
32c7d09
Changed entry path for flextron functional test
sheliang-nv Apr 29, 2026
160336b
Removed unused imports and dead config field
sheliang-nv Apr 30, 2026
e975d5e
Merge branch 'main' into shel/flex_merge
sheliang-nv Apr 30, 2026
33b5c9e
Removed attention head masking
sheliang-nv Apr 30, 2026
96c8e5c
Removed head masking args from tests
sheliang-nv Apr 30, 2026
1570c80
Fix expert_bias mutation, PP-sync test attr, pretrain() signature
sheliang-nv Apr 30, 2026
b8bdfd6
Removed dead code and comments, added init file for router
sheliang-nv Apr 30, 2026
9311c95
Fix MoE expert MSE drop, mlp_per UnboundLocalError, budget=1.0 fallba…
sheliang-nv Apr 30, 2026
0894912
Added args check for int-per-lists, added loss_func unit test
sheliang-nv Apr 30, 2026
41e8976
Fix budget=1.0 KeyError paths, retire original_model kill-switch, sor…
sheliang-nv Apr 30, 2026
c2f58f9
Fix CUDA RNG leak in DP gumbel softmax, update stale loss_func docstring
sheliang-nv Apr 30, 2026
f68b0e6
Removed unused imports and added double check for MLP hook
sheliang-nv Apr 30, 2026
3292cf7
Add unit tests for FlextronTopKRouter and FlextronGroupedMLP elastici…
sheliang-nv Apr 30, 2026
7e4c52e
Autoformat
sheliang-nv Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion megatron/core/distributed/finalize_model_grads.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

from functools import partial
from typing import Callable, List, Optional, Union
from typing import Callable, Dict, List, Optional, Union

import torch
from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors
Expand Down Expand Up @@ -275,6 +275,44 @@ def _allreduce_position_embedding_grads(
)


def _allreduce_router_grads(model: List[torch.nn.Module], config: TransformerConfig):
"""
All-reduce router grads.

Reduce grads across all the pp stages to ensure that parameters of the router stay in sync.
"""

if parallel_state.get_pipeline_model_parallel_world_size() > 1:
grads_dict: Dict[str, List[torch.Tensor]] = {}
for model_chunk in model:
for name, param in get_attr_wrapped_model(model_chunk, 'named_parameters')():
if param.requires_grad and getattr(param, 'pipeline_parallel', False):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we improve the attribute name? IIUC, pipeline_parallel means the parameters need to stay in sync across PP stages? If so, I think this attribute name needs to be a bit more descriptive.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed to "flextron_router_pp_sync" in latest commit

grad = param.main_grad
if name in grads_dict:
# Add all the virtual PP rank's gradients to
# the first local virtual PP rank.
grads_dict[name][0].add_(grad)
# Append to the end for later update after cross-rank reduce.
grads_dict[name].append(grad)
else:
grads_dict[name] = [grad]

if grads_dict:
# All-reduce the gradient on the first VPP rank.
grads = [param_grad[0] for _, param_grad in grads_dict.items()]
coalesced = _flatten_dense_tensors(grads)
torch.distributed.all_reduce(
coalesced, group=parallel_state.get_pipeline_model_parallel_group()
)
for buf, synced in zip(grads, _unflatten_dense_tensors(coalesced, grads)):
buf.copy_(synced)

# Update the gradients on other VPP ranks.
for grads in grads_dict.values():
for grad in grads[1:]:
grad.copy_(grads[0])

Comment on lines +278 to +314

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This new function modifies a core distributed file but has no unit test coverage. A test verifying the all-reduce behavior (especially the VPP gradient aggregation logic in lines 291-313) would help prevent regressions, since bugs here would silently produce incorrect router gradients across pipeline stages.


def reset_model_temporary_tensors(config: TransformerConfig, model: List[torch.nn.Module]):
"""
Reset the temporary tensors of the model.
Expand Down Expand Up @@ -457,6 +495,9 @@ def finalize_model_grads(
if config.timers is not None:
config.timers('conditional-embedder-grads-all-reduce').stop()

if getattr(config, 'flextron', False):
_allreduce_router_grads(model, config)

# All-reduce layer-norm grads (for sequence parallelism) and non-tensor parallel modules.
if config.timers is not None:
config.timers('non-tensor-parallel-grads-all-reduce', log_level=1).start(
Expand Down
1 change: 1 addition & 0 deletions megatron/elastification/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
216 changes: 216 additions & 0 deletions megatron/elastification/arguments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.

import math


def convert_per_lists_to_int_lists(config):
"""Convert all *_per_list attributes to *_int_list using model dimensions.

Called once after model dimensions are known so downstream code can always
use the int-list path without branching on which list type is active.
After this call every *_per_list is None and every *_int_list is set.
"""
conversions = [
('emb_per_list', 'emb_int_list', config.hidden_size),
('mlp_per_list', 'mlp_int_list', config.ffn_hidden_size),
('head_per_list', 'head_int_list', config.num_attention_heads),
('mamba_per_list', 'mamba_int_list', config.mamba_num_heads),
('moe_expert_per_list', 'moe_expert_int_list', config.num_moe_experts),
]
for per_attr, int_attr, ref_dim in conversions:
per_val = getattr(config, per_attr, None)
if per_val is not None:
setattr(config, int_attr, [math.floor(x * ref_dim) for x in per_val])
setattr(config, per_attr, None)


def validate_flextron_per_int_lists(args):
"""
Enforce mutual exclusion between ratio per-lists and integer choice lists.

For each module, at most one of (*_per_list, *_int_list) may be set. If neither
is set, *_per_list defaults to [1.0]. Skips when flextron-related args were not
registered on the parser.
"""
pairs = (
('mamba', 'mamba_per_list', 'mamba_int_list'),
('mlp', 'mlp_per_list', 'mlp_int_list'),
('emb', 'emb_per_list', 'emb_int_list'),
('head', 'head_per_list', 'head_int_list'),
('moe-expert', 'moe_expert_per_list', 'moe_expert_int_list'),
)
for cli_name, per_attr, int_attr in pairs:
per_val = getattr(args, per_attr)
int_val = getattr(args, int_attr)
per_set = per_val is not None
int_set = int_val is not None
if per_set:
for x in per_val:
assert 0.0 <= x <= 1.0, (
f'--{cli_name}-per-list values must be in [0, 1], got {x}.'
)
assert not (per_set and int_set), (
f'Use either --{cli_name}-per-list or --{cli_name}-int-list for {cli_name}, not both.'
)
if not per_set and not int_set:
setattr(args, per_attr, [1.0])
Comment on lines +51 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: validate_flextron_per_int_lists is defined and tested but never called from production code. This means its guards are dead:

  1. Mutual exclusion between --*-per-list and --*-int-list is not enforced — a user can pass both without error, leading to unpredictable behavior (whichever convert_per_lists_to_int_lists processes last wins).
  2. Default fallback (per_list = [1.0] when neither list is provided) never fires — if a user enables --flextron and --enable-router without any list args, all *_int_list fields stay None, which will crash downstream in the router (e.g., len(self.config.mamba_int_list)).

This should be called from mamba_flex_extra_args_provider in pretrain_hybrid_flex.py (or a post-parse validation hook) so the checks actually run.


def add_flextron_args(parser):
group = parser.add_argument_group(title='flextron')
# Distillation flags
group.add_argument('--distillation', action='store_true',
help='Enable self-distillation.')
group.add_argument('--distill-coeff', type=float, default=0.0,
help='Distillation coefficient.')
group.add_argument('--distill-only', action='store_true',
help='Distillation only.')
# Basic Flextron flags
group.add_argument('--flextron', action='store_true',
help='Enable Flextron.')
group.add_argument('--binary-mask', action='store_true',
help='Use binary mask in Flextron.')
group.add_argument('--slice', action='store_true',
help='Use slice in Flextron.')
group.add_argument('--enable-router', action='store_true',
help='Enable router in Flextron.')
group.add_argument('--add-skipping', action='store_true',
help='Add layer skipping in Flextron.')
group.add_argument('--no-attn-skip', action='store_true',
help='No attn skip in Flextron.')
group.add_argument('--lr-mult-router', type=float, default=1.0,
help='Learning rate multiplier for router in Flextron.')
group.add_argument('--flex-strict', action='store_true',
help='Strict loading of Flextron.')
group.add_argument('--is-flex-eval', action='store_true',
help='Is Flextron evaluation.')
group.add_argument('--freeze-router', action='store_true',
help='Freeze router in Flextron.')
group.add_argument('--freeze-model', action='store_true',
help='Freeze model in Flextron.')
group.add_argument('--flex-hetero-ffn', action='store_true',
help='Use flex hetero FFN in Flextron.')
group.add_argument('--flex-hetero-mamba', action='store_true',
help='Use flex hetero Mamba in Flextron.')
group.add_argument('--flex-hetero-head', action='store_true',
help='Use flex hetero attention head in Flextron.')
group.add_argument('--flex-hetero-moe-expert', action='store_true',
help='Use flex hetero MoE expert in Flextron.')
group.add_argument('--router-std', type=float, default=0.1,
help='Router init std for Flextron.')
group.add_argument('--normalize-router-logits', action='store_true',
help='Normalize router logits in Flextron.')
group.add_argument('--soft-mask', action='store_true',
help='Soft mask in Flextron.')

# Flextron hyperparameters
group.add_argument('--budget-probs', nargs='+', type=float, default=None,
help='List of budget probabilities for Flextron.')
group.add_argument('--prefill-chunk-size', type=int, default=16384,
help='Prefill chunk size for Flextron.')
group.add_argument('--mem-infer-seq-len', type=int, default=131072,
help='Memory infer sequence length for Flextron.')
group.add_argument('--mem-batch-size', type=int, default=1,
help='Memory batch size for Flextron.')
group.add_argument('--original-model-sample-prob', type=float, default=0.33,
help='Probability of sampling the original model in Flextron.')
group.add_argument('--force-router-skip', nargs='+', type=int, default=None,
help='Force router skip for Flextron router.')
group.add_argument('--force-mlp', nargs='+', type=float, default=None,
help='Force MLP for Flextron router.')
group.add_argument('--force-head', nargs='+', type=float, default=None,
help='Force Attn head for Flextron router.')
group.add_argument('--force-mamba', nargs='+', type=float, default=None,
help='Force Mamba for Flextron router.')
group.add_argument('--force-emb', nargs='+', type=float, default=None,
help='Force Embedding for Flextron router.')
group.add_argument('--skip-num-attn-layer-constraint', type=int, default=None,
help='Skip number of attention layer constraint for Flextron router.')
group.add_argument('--skip-total-layer-constraint', type=int, default=None,
help='Skip total layer constraint for Flextron router.')
group.add_argument('--disable-budget', action='store_true',
help='Disable budget for Flextron router.')
group.add_argument('--curr-iteration', type=int, default=None,
help='Current iteration for Flextron router.')
group.add_argument('--hard-sample-th', type=float, default=0.996,
help='Hard sample threshold for Flextron router.')
group.add_argument('--router-beta', type=float, default=1.0,
help='Beta value for Flextron router.')
group.add_argument('--loss-alpha', type=float, default=1.0,
help='Alpha coefficient for Flextron loss.')
group.add_argument('--tau-init', type=float, default=1.0,
help='Tau init for Flextron router.')
group.add_argument('--tau-decay', type=float, default=0.9999,
help='Tau decay for Flextron router.')
group.add_argument('--router-inter-dim', type=int, default=128,
help='Intermediate dimension for Flextron router.')
group.add_argument('--linear-scaler-start', type=float, default=1.0,
help='Linear scaler start for Flextron router.')
group.add_argument('--linear-scaler-end', type=float, default=10.0,
help='Linear scaler end for Flextron router.')
group.add_argument('--override-selected-budget', nargs='+', type=float, default=None,
help='Override selected budget for Flextron router.')
group.add_argument('--router-gbs', type=int, default=32,
help='Router gbs for Flextron router.')
# Model configuration lists
group.add_argument('--budget-list', nargs='+', type=float, default=[1.0],
help='List of budget values for Flextron.')
group.add_argument('--mamba-per-list', nargs='+', type=float, default=None,
help='List of Mamba percentage values for Flextron (mutually exclusive with --mamba-int-list).')
group.add_argument('--mlp-per-list', nargs='+', type=float, default=None,
help='List of MLP percentage values for Flextron (mutually exclusive with --mlp-int-list).')
group.add_argument('--emb-per-list', nargs='+', type=float, default=None,
help='List of embedding percentage values for Flextron (mutually exclusive with --emb-int-list).')
group.add_argument('--head-per-list', nargs='+', type=float, default=None,
help='List of head percentage values for Flextron (mutually exclusive with --head-int-list).')
group.add_argument('--moe-expert-per-list', nargs='+', type=float, default=None,
help='List of MoE expert percentage values for Flextron (mutually exclusive with --moe-expert-int-list).')
group.add_argument('--mamba-int-list', nargs='+', type=int, default=None,
help='List of Mamba integer router choices for Flextron (mutually exclusive with --mamba-per-list).')
group.add_argument('--mlp-int-list', nargs='+', type=int, default=None,
help='List of MLP integer router choices for Flextron (mutually exclusive with --mlp-per-list).')
group.add_argument('--emb-int-list', nargs='+', type=int, default=None,
help='List of embedding integer router choices for Flextron (mutually exclusive with --emb-per-list).')
group.add_argument('--head-int-list', nargs='+', type=int, default=None,
help='List of head integer router choices for Flextron (mutually exclusive with --head-per-list).')
group.add_argument('--moe-expert-int-list', nargs='+', type=int, default=None,
help='List of MoE expert integer router choices for Flextron (mutually exclusive with --moe-expert-per-list).')
group.add_argument('--budget-type', type=str, default='param',
choices=['param', 'mem'],
help='Type of budget.')
# Memory quantization profile
group.add_argument('--memory-profile', type=str, default='bf16',
help='Named memory quantization preset from memory_profiles.yaml '
'(e.g. bf16, fp8_kv, fp8_all, int8). '
'Individual --bpe-* overrides take priority.')
group.add_argument('--memory-profile-path', type=str, default=None,
help='Path to a custom memory_profiles.yaml. '
'Defaults to the bundled megatron/elastification/memory_profiles.yaml.')
group.add_argument('--bpe-params', type=float, default=None,
help='Override bytes-per-element for model parameters '
'(2=BF16, 1=FP8/INT8, 0.5625=FP4).')
group.add_argument('--bpe-kv-cache', type=float, default=None,
help='Override bytes-per-element for KV cache.')
group.add_argument('--bpe-ssm-cache', type=float, default=None,
help='Override bytes-per-element for Mamba SSM state cache.')
group.add_argument('--bpe-max-buffer', type=float, default=None,
help='Override bytes-per-element for MoE dispatch buffer.')
group.add_argument('--param-budget-target', type=str, default=None,
choices=['active', 'total'],
help='Whether param budget loss supervises on active params '
'(top-k experts only) or total params. '
'Overrides the preset value from --memory-profile.')
group.add_argument('--layer-ranking-list', nargs='+', type=int, default=None,
help='List of layer ranking.')
group.add_argument('--log-budgets', nargs='+', type=str, default=["all"],
help='Budget values to log distillation loss for (space-separated list or "all").')
# Additional parameters

group.add_argument('--basemodel-type', type=str, default='nemotronh_8b',
choices=['nemotronh_8b'],
help='Base model type for parameter loss calculation.')

# Budget configuration
group.add_argument('--flextron-config-file', type=str, default=None,
help='Configuration file for Flextron budget settings.')

return parser
Loading
Loading