Skip to content

Commit 61fd64d

Browse files
committed
[MaxText] Implement MoE routing mismatch measurement
Calculate the mismatch rate between model-calculated routing and externally supplied forced_routed_experts. This is limited to Qwen3 and Qwen3.5 models in training mode. The mismatch rate is logged as 'learning/routing_mismatch_rate'.
1 parent c3d6fdc commit 61fd64d

10 files changed

Lines changed: 517 additions & 36 deletions

File tree

src/maxtext/integration/tunix/tunix_adapter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __call__(
7070
attention_mask: Optional[Array], # [B, L, L] or None
7171
decoder_segment_ids: Optional[Array] = None,
7272
output_hidden_states: bool = False, # ignored
73+
forced_routed_experts: Optional[Array] = None,
7374
) -> Tuple[Array, None]:
7475
"""Forward compatible with Tunix Trainers default loss.
7576
Returns logits, None.
@@ -80,6 +81,7 @@ def __call__(
8081
decoder_input_tokens=input_tokens,
8182
decoder_positions=positions,
8283
decoder_segment_ids=decoder_segment_ids,
84+
forced_routed_experts=forced_routed_experts,
8385
)
8486
return logits, None
8587

src/maxtext/layers/decoders.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ def __call__(
794794
kv_caches: list[jax.Array] | None = None,
795795
attention_metadata=None,
796796
deepstack_visual_embeds: None | list[jnp.ndarray] = None,
797+
forced_routed_experts: jax.Array | None = None,
797798
):
798799
cfg = self.config
799800
mesh = self.mesh
@@ -1013,6 +1014,23 @@ def __call__(
10131014
current_broadcast_args = list(broadcast_args)
10141015
current_in_axes_tuple = list(in_axes_tuple)
10151016

1017+
supports_forced_routing = cfg.decoder_block in (
1018+
DecoderBlockType.QWEN3_MOE,
1019+
DecoderBlockType.QWEN3_NEXT,
1020+
DecoderBlockType.QWEN3_5,
1021+
)
1022+
1023+
if supports_forced_routing and forced_routed_experts is not None:
1024+
# Transpose [B, L, N, E] -> [N, B, L, E] for scan
1025+
forced_experts_t = jnp.transpose(forced_routed_experts, (2, 0, 1, 3))
1026+
cycle_interval = cfg.inhomogeneous_layer_cycle_interval
1027+
forced_routed_experts_t = jnp.reshape(
1028+
forced_experts_t,
1029+
(scan_length, cycle_interval) + forced_experts_t.shape[1:],
1030+
)
1031+
else:
1032+
forced_routed_experts_t = None
1033+
10161034
if kv_caches is not None:
10171035
# Stack kv_caches for scan: [num_layers, ...]
10181036
stacked_kv_cache = jnp.stack(kv_caches, axis=0)
@@ -1026,6 +1044,14 @@ def __call__(
10261044
current_broadcast_args.extend([None, None, None, attention_metadata])
10271045
current_in_axes_tuple.extend([nn.broadcast] * 4)
10281046

1047+
if supports_forced_routing:
1048+
if forced_routed_experts_t is not None:
1049+
current_broadcast_args.append(forced_routed_experts_t)
1050+
current_in_axes_tuple.append(0)
1051+
else:
1052+
current_broadcast_args.append(None)
1053+
current_in_axes_tuple.append(nn.broadcast)
1054+
10291055
max_logging.info(f"DEBUG: len(current_broadcast_args)={len(current_broadcast_args)}")
10301056
max_logging.info(f"DEBUG: current_broadcast_args={[type(a) for a in current_broadcast_args]}")
10311057

@@ -1047,8 +1073,19 @@ def __call__(
10471073
kv_caches[i] = returned_kv_cache[i]
10481074
else:
10491075
# Fallback to old behavior if kv_caches is None (not vLLM RPA)
1050-
current_broadcast_args.append(None)
1051-
current_in_axes_tuple.append(nn.broadcast)
1076+
if supports_forced_routing:
1077+
current_broadcast_args.extend([None, None, None, None])
1078+
current_in_axes_tuple.extend([nn.broadcast] * 4)
1079+
1080+
if forced_routed_experts_t is not None:
1081+
current_broadcast_args.append(forced_routed_experts_t)
1082+
current_in_axes_tuple.append(0)
1083+
else:
1084+
current_broadcast_args.append(None)
1085+
current_in_axes_tuple.append(nn.broadcast)
1086+
else:
1087+
current_broadcast_args.append(None)
1088+
current_in_axes_tuple.append(nn.broadcast)
10521089

10531090
y, _ = self.scan_decoder_layers(
10541091
cfg,
@@ -1077,6 +1114,11 @@ def __call__(
10771114
global_layer_idx = global_layer_idx_offset + index
10781115
kv_cache = kv_caches[index] if kv_caches is not None else None
10791116
input_tokens = decoder_input_tokens if cfg.engram_layers else None
1117+
extra_kwargs = {}
1118+
if layer_prefix == "moe_layers" and forced_routed_experts is not None:
1119+
forced_experts = forced_routed_experts[:, :, index, :]
1120+
extra_kwargs["forced_routed_experts"] = forced_experts
1121+
10801122
y, kv_cache = layer(
10811123
config=cfg,
10821124
mesh=mesh,
@@ -1095,6 +1137,7 @@ def __call__(
10951137
kv_cache=kv_cache,
10961138
attention_metadata=attention_metadata,
10971139
decoder_input_tokens=input_tokens,
1140+
**extra_kwargs,
10981141
)
10991142
if kv_caches is not None and kv_cache is not None:
11001143
kv_caches[index] = kv_cache
@@ -1114,6 +1157,7 @@ def __call__(
11141157
slot=slot,
11151158
)
11161159
else:
1160+
moe_lyr_idx = 0
11171161
for lyr in range(cfg.num_decoder_layers):
11181162
RemattedBlockLayer = RemattedBlockLayers[0]
11191163
layer_kwargs = {}
@@ -1150,6 +1194,25 @@ def __call__(
11501194
layer = RemattedBlockLayer(
11511195
config=cfg, mesh=mesh, name=f"layers_{lyr}", quant=self.quant, model_mode=self.model_mode, **layer_kwargs
11521196
)
1197+
is_moe = False
1198+
if cfg.decoder_block in (
1199+
DecoderBlockType.QWEN3_MOE,
1200+
DecoderBlockType.QWEN3_NEXT,
1201+
DecoderBlockType.QWEN3_5,
1202+
):
1203+
is_moe = True
1204+
1205+
current_forced_routed_experts = None
1206+
if is_moe and forced_routed_experts is not None:
1207+
current_forced_routed_experts = forced_routed_experts[:, :, moe_lyr_idx, :]
1208+
moe_lyr_idx += 1
1209+
elif is_moe:
1210+
moe_lyr_idx += 1
1211+
1212+
extra_kwargs = {}
1213+
if is_moe and current_forced_routed_experts is not None:
1214+
extra_kwargs["forced_routed_experts"] = current_forced_routed_experts
1215+
11531216
y, returned_cache = layer(
11541217
y,
11551218
decoder_segment_ids,
@@ -1160,6 +1223,7 @@ def __call__(
11601223
slot=slot,
11611224
kv_cache=kv_cache,
11621225
attention_metadata=attention_metadata,
1226+
**extra_kwargs,
11631227
**layer_call_kwargs,
11641228
)
11651229
if kv_caches is not None and returned_cache is not None:

0 commit comments

Comments
 (0)