Skip to content

Commit 3265c27

Browse files
committed
[moe] rename RoutedExperts field grouped_experts -> inner_experts
Address review (tianyu-l on #3859): the config field / module attribute (and thus the FQN moe.routed_experts.grouped_experts) is renamed to inner_experts, while the class name stays GroupedExperts and the fused_grouped_experts override (which targets GroupedExperts.Config, not the field) is unchanged. State-dict adapters, sharding, quantization, graph_trainer, RL configs, tests and docs updated to the new FQN. Pure rename (word-boundary; class/override names preserved).
1 parent ed8267c commit 3265c27

21 files changed

Lines changed: 65 additions & 65 deletions

File tree

tests/unit_tests/test_fsdp_moe_sharding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def _get_expert_shard_dim(model: Qwen3Model) -> int | None:
6060
"""Return the shard dim used for expert params, or None if not sharded."""
6161
for layer in model.layers.values():
6262
if layer.moe_enabled:
63-
for param in layer.moe.routed_experts.grouped_experts.parameters():
63+
for param in layer.moe.routed_experts.inner_experts.parameters():
6464
if hasattr(param, "placements"):
6565
for p in param.placements:
6666
if isinstance(p, Shard):

tests/unit_tests/test_inference_moe.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
The inference path fuses the grouped experts (``fused_grouped_experts``) AND flips
1010
the DeepEP dispatcher to the cudagraph-able EXPAND layout (``deepep_inference``).
11-
Because ``grouped_experts`` and ``token_dispatcher`` are sibling config nodes under
11+
Because ``inner_experts`` and ``token_dispatcher`` are sibling config nodes under
1212
``moe.routed_experts`` (neither an ancestor of the other), both overrides can be
1313
applied together without the ancestor/descendant conflict that a single combined
1414
node would cause.
@@ -36,7 +36,7 @@
3636

3737
# fused_swiglu registers both the dense-FFN and the routed-experts (fused_grouped_experts)
3838
# overrides; the test MoE config has no dense FeedForward node, so importing it only
39-
# claims the grouped_experts node here.
39+
# claims the inner_experts node here.
4040
_FUSED_SWIGLU = "torchtitan.overrides.fused_swiglu"
4141
_DEEPEP_INFERENCE = "torchtitan.overrides.deepep_inference"
4242

@@ -67,7 +67,7 @@ def _moe_config(comm_backend: str):
6767
class TestInferenceMoEOverrides(unittest.TestCase):
6868
def test_grouped_experts_and_dispatcher_are_siblings(self):
6969
cfg = _moe_config("deepep")
70-
self.assertIsInstance(cfg.routed_experts.grouped_experts, GroupedExperts.Config)
70+
self.assertIsInstance(cfg.routed_experts.inner_experts, GroupedExperts.Config)
7171
self.assertIsInstance(
7272
cfg.routed_experts.token_dispatcher, DeepEPTokenDispatcher.Config
7373
)
@@ -76,15 +76,15 @@ def test_deepep_both_overrides_apply_without_conflict(self):
7676
cfg = _moe_config("deepep")
7777

7878
# Both overrides on the same actor: the experts-fusion claims
79-
# grouped_experts, the dispatcher flip claims the sibling dispatcher.
79+
# inner_experts, the dispatcher flip claims the sibling dispatcher.
8080
replacements = apply_overrides(
8181
OverrideConfig(imports=[_FUSED_SWIGLU, _DEEPEP_INFERENCE]),
8282
cfg,
8383
)
8484

8585
self.assertEqual(len(replacements), 2)
8686
self.assertIsInstance(
87-
cfg.routed_experts.grouped_experts, FusedGroupedExperts.Config
87+
cfg.routed_experts.inner_experts, FusedGroupedExperts.Config
8888
)
8989
self.assertIsInstance(
9090
cfg.routed_experts.token_dispatcher, DeepEPTokenDispatcher.Config
@@ -103,31 +103,31 @@ def test_non_deepep_dispatcher_flip_is_noop(self):
103103

104104
self.assertEqual(len(replacements), 1)
105105
self.assertIsInstance(
106-
cfg.routed_experts.grouped_experts, FusedGroupedExperts.Config
106+
cfg.routed_experts.inner_experts, FusedGroupedExperts.Config
107107
)
108108

109109
def test_composition_is_order_independent(self):
110-
# Because grouped_experts and dispatcher are disjoint sibling nodes, applying
110+
# Because inner_experts and dispatcher are disjoint sibling nodes, applying
111111
# the experts-fusion and the dispatcher-flip in either order must yield the
112112
# same result (unlike an ancestor/descendant pair, where an ancestor
113113
# replacement would detach the descendant's claimed subtree).
114114
def summarize(ge):
115115
return (
116-
type(ge.grouped_experts).__qualname__,
116+
type(ge.inner_experts).__qualname__,
117117
type(ge.token_dispatcher).__qualname__,
118118
ge.token_dispatcher.cudagraphable,
119119
)
120120

121121
a = _moe_config("deepep").routed_experts
122-
a.grouped_experts = fused_grouped_experts(a.grouped_experts)
122+
a.inner_experts = fused_grouped_experts(a.inner_experts)
123123
a.token_dispatcher = deepep_inference(a.token_dispatcher)
124124

125125
b = _moe_config("deepep").routed_experts
126126
b.token_dispatcher = deepep_inference(b.token_dispatcher)
127-
b.grouped_experts = fused_grouped_experts(b.grouped_experts)
127+
b.inner_experts = fused_grouped_experts(b.inner_experts)
128128

129129
self.assertEqual(summarize(a), summarize(b))
130-
self.assertIsInstance(a.grouped_experts, FusedGroupedExperts.Config)
130+
self.assertIsInstance(a.inner_experts, FusedGroupedExperts.Config)
131131
self.assertTrue(a.token_dispatcher.cudagraphable)
132132

133133
def test_trainer_uses_only_experts_fusion(self):
@@ -138,7 +138,7 @@ def test_trainer_uses_only_experts_fusion(self):
138138
apply_overrides(OverrideConfig(imports=[_FUSED_SWIGLU]), cfg)
139139

140140
self.assertIsInstance(
141-
cfg.routed_experts.grouped_experts, FusedGroupedExperts.Config
141+
cfg.routed_experts.inner_experts, FusedGroupedExperts.Config
142142
)
143143
self.assertFalse(cfg.routed_experts.token_dispatcher.cudagraphable)
144144

torchtitan/components/quantization/mx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def __init__(self, config: Config):
176176

177177
def convert(self, model_config):
178178
for _fqn, config, parent, attr in model_config.traverse(GroupedExperts.Config):
179-
# ``parent`` is the RoutedExperts.Config that owns this grouped_experts
179+
# ``parent`` is the RoutedExperts.Config that owns this inner_experts
180180
# node and its sibling dispatcher.
181181
swap_token_dispatcher(parent, self.config.pad_multiple)
182182
base_module_cls = type(config)._owner

torchtitan/distributed/fsdp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def apply_fsdp_to_decoder(
189189
# The routed-expert weights live on the grouped-GEMM child; the
190190
# sibling dispatcher holds no parameters.
191191
# pyrefly: ignore [missing-attribute]
192-
experts = transformer_block.moe.routed_experts.grouped_experts
192+
experts = transformer_block.moe.routed_experts.inner_experts
193193
expert_params = set(experts.parameters())
194194
num_experts = experts.num_experts
195195

torchtitan/experiments/graph_trainer/common_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ def get_default_transformer_block_buckets(
266266
f"layers.{layer_id}.moe.router",
267267
f"layers.{layer_id}.moe.shared_experts",
268268
],
269-
f"layers.{layer_id}.moe.routed_experts.grouped_experts",
269+
f"layers.{layer_id}.moe.routed_experts.inner_experts",
270270
]
271271
)
272272
else:
@@ -335,7 +335,7 @@ def apply_simple_fsdp(
335335
) -> nn.Module:
336336
"""Wrap the model (and any MoE experts) with graph_trainer's simple_fsdp.
337337
338-
For MoE-enabled models, the ``moe.routed_experts.grouped_experts`` submodules
338+
For MoE-enabled models, the ``moe.routed_experts.inner_experts`` submodules
339339
(the routed-expert weights) are separately wrapped on the EDP mesh when expert
340340
parallelism is enabled.
341341
"""
@@ -371,16 +371,16 @@ def apply_simple_fsdp(
371371
moe = getattr(transformer_block, "moe", None)
372372
if moe is None:
373373
continue
374-
grouped_experts = moe.routed_experts.grouped_experts
374+
inner_experts = moe.routed_experts.inner_experts
375375
experts_shard_dim = 0
376376
if (
377377
edp_mesh["efsdp"].size() * parallel_dims.ep
378-
> grouped_experts.num_experts
378+
> inner_experts.num_experts
379379
):
380380
experts_shard_dim = 1
381381

382-
moe.routed_experts.grouped_experts = data_parallel(
383-
grouped_experts,
382+
moe.routed_experts.inner_experts = data_parallel(
383+
inner_experts,
384384
edp_mesh,
385385
dp_mode,
386386
mp_policy=mp_policy,

torchtitan/experiments/graph_trainer/tests/test_passes.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ def test_transformer_block_bucket_counts_follow_bucket_plan(self):
437437
"layers.1.moe.router",
438438
"layers.1.moe.shared_experts",
439439
],
440-
"layers.1.moe.routed_experts.grouped_experts",
440+
"layers.1.moe.routed_experts.inner_experts",
441441
["norm", "lm_head"],
442442
],
443443
n_layers=2,
@@ -1074,12 +1074,12 @@ def test_fsdp_dense_scheduler_places_rs_after_moe_backward(self):
10741074
c10d.all_to_all_single.default,
10751075
args=(ffn_norm, [], [], "ep_pg"),
10761076
),
1077-
"layers.1.moe.routed_experts.grouped_experts",
1077+
"layers.1.moe.routed_experts.inner_experts",
10781078
backward=True,
10791079
)
10801080
moe_dispatch_wait = self._tag_fsdp_schedule_node(
10811081
graph.call_function(c10d.wait_tensor.default, args=(moe_dispatch,)),
1082-
"layers.1.moe.routed_experts.grouped_experts",
1082+
"layers.1.moe.routed_experts.inner_experts",
10831083
backward=True,
10841084
)
10851085
dense1_attention = self._tag_fsdp_schedule_node(
@@ -3432,7 +3432,7 @@ def test_moe_efsdp_bucket_plan_splits_expert_buckets(self):
34323432
],
34333433
buckets,
34343434
)
3435-
self.assertIn("layers.1.moe.routed_experts.grouped_experts", buckets)
3435+
self.assertIn("layers.1.moe.routed_experts.inner_experts", buckets)
34363436
self.assertNotIn("layers.1", buckets)
34373437

34383438
def test_prepare_ep_overlap_trace_inputs_marks_batch_dims(self):

torchtitan/experiments/rl/examples/alphabet_sort/config_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ def rl_grpo_qwen3_moe_debug_deepep() -> Controller.Config:
604604
overrides (``generator.override``) to its own copy: ``fused_swiglu`` (which
605605
registers both the dense-FFN and routed-experts gate+up fusion) and
606606
``deepep_inference`` (switch the sibling DeepEP dispatcher to the cudagraph-able
607-
EXPAND layout, ``cudagraphable=True``). ``grouped_experts`` and ``token_dispatcher`` are
607+
EXPAND layout, ``cudagraphable=True``). ``inner_experts`` and ``token_dispatcher`` are
608608
sibling nodes under ``moe.routed_experts``, so the experts fusion and the
609609
dispatcher flip do not conflict. The overrides touch only the generator's spec, so
610610
the trainer and weight sync are unaffected. (This mirrors how converters are

torchtitan/experiments/rl/examples/search_r1/config_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def rl_grpo_qwen3_30b_a3b_deepep_search_r1_perf() -> Controller.Config:
162162
sibling ``deepep_inference`` override, which switches the DeepEP dispatcher to the
163163
cudagraph-able EXPAND layout. Both actors apply ``fused_swiglu`` (which registers
164164
both the dense-FFN and routed-experts gate+up fusion) + ``helion_rope`` (CUDA-only),
165-
as ``rl_grpo_qwen3_30b_a3b_varlen_perf``. ``grouped_experts`` and ``token_dispatcher`` are
165+
as ``rl_grpo_qwen3_30b_a3b_varlen_perf``. ``inner_experts`` and ``token_dispatcher`` are
166166
sibling nodes under ``moe.routed_experts``, so the experts fusion and the generator's
167167
dispatcher flip do not conflict.
168168
"""

torchtitan/experiments/rl/models/vllm_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def model_spec_to_hf_config_dict(spec: ModelSpec) -> dict[str, Any]:
172172
hf["num_experts_per_tok"] = (
173173
moe.router.top_k
174174
) # top_k is on the router, not experts
175-
hf["moe_intermediate_size"] = moe.routed_experts.grouped_experts.hidden_dim
175+
hf["moe_intermediate_size"] = moe.routed_experts.inner_experts.hidden_dim
176176
hf["decoder_sparse_step"] = 1
177177
hf.setdefault("norm_topk_prob", True)
178178

torchtitan/models/common/config_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,14 +386,14 @@ def make_routed_experts_config(
386386
) -> RoutedExperts.Config:
387387
"""Build a fully-specified RoutedExperts.Config.
388388
389-
Composes the pure grouped-GEMM ``grouped_experts`` (weights) with the token
389+
Composes the pure grouped-GEMM ``inner_experts`` (weights) with the token
390390
``token_dispatcher`` (communication) -- the two sibling children of the routed
391391
region. The ``fused_grouped_experts`` override targets the former; the DeepEP
392392
inference override targets the latter.
393393
"""
394394
return RoutedExperts.Config(
395395
num_experts=num_experts,
396-
grouped_experts=GroupedExperts.Config(
396+
inner_experts=GroupedExperts.Config(
397397
dim=dim,
398398
hidden_dim=hidden_dim,
399399
num_experts=num_experts,

0 commit comments

Comments
 (0)