Skip to content

Commit 58e4ddc

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 03cabc3 commit 58e4ddc

10 files changed

Lines changed: 496 additions & 35 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: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ def __call__(
856856
kv_caches: list[jax.Array] | None = None,
857857
attention_metadata=None,
858858
deepstack_visual_embeds: None | list[jnp.ndarray] = None,
859+
forced_routed_experts: jax.Array | None = None,
859860
):
860861
cfg = self.config
861862
mesh = self.mesh
@@ -877,6 +878,11 @@ def __call__(
877878
y = mhc_expand(y)
878879

879880
policy = self.get_remat_policy()
881+
supports_forced_routing = cfg.decoder_block in (
882+
DecoderBlockType.QWEN3_MOE,
883+
DecoderBlockType.QWEN3_NEXT,
884+
DecoderBlockType.QWEN3_5,
885+
)
880886
RemattedBlockLayers = self.set_remat_policy(self.decoder_layer, policy)
881887
# scan does not support kwargs in layer call, passing broadcast_args as positional arg
882888
broadcast_args = (
@@ -1075,6 +1081,17 @@ def __call__(
10751081
current_broadcast_args = list(broadcast_args)
10761082
current_in_axes_tuple = list(in_axes_tuple)
10771083

1084+
if supports_forced_routing and forced_routed_experts is not None:
1085+
# Transpose [B, L, N, E] -> [N, B, L, E] for scan
1086+
forced_experts_t = jnp.transpose(forced_routed_experts, (2, 0, 1, 3))
1087+
cycle_interval = cfg.inhomogeneous_layer_cycle_interval
1088+
forced_routed_experts_t = jnp.reshape(
1089+
forced_experts_t,
1090+
(scan_length, cycle_interval) + forced_experts_t.shape[1:],
1091+
)
1092+
else:
1093+
forced_routed_experts_t = None
1094+
10781095
if kv_caches is not None:
10791096
# Stack kv_caches for scan: [num_layers, ...]
10801097
stacked_kv_cache = jnp.stack(kv_caches, axis=0)
@@ -1088,6 +1105,14 @@ def __call__(
10881105
current_broadcast_args.extend([None, None, None, attention_metadata])
10891106
current_in_axes_tuple.extend([nn.broadcast] * 4)
10901107

1108+
if supports_forced_routing:
1109+
if forced_routed_experts_t is not None:
1110+
current_broadcast_args.append(forced_routed_experts_t)
1111+
current_in_axes_tuple.append(0)
1112+
else:
1113+
current_broadcast_args.append(None)
1114+
current_in_axes_tuple.append(nn.broadcast)
1115+
10911116
max_logging.info(f"DEBUG: len(current_broadcast_args)={len(current_broadcast_args)}")
10921117
max_logging.info(f"DEBUG: current_broadcast_args={[type(a) for a in current_broadcast_args]}")
10931118

@@ -1109,8 +1134,19 @@ def __call__(
11091134
kv_caches[i] = returned_kv_cache[i]
11101135
else:
11111136
# Fallback to old behavior if kv_caches is None (not vLLM RPA)
1112-
current_broadcast_args.append(None)
1113-
current_in_axes_tuple.append(nn.broadcast)
1137+
if supports_forced_routing:
1138+
current_broadcast_args.extend([None, None, None, None])
1139+
current_in_axes_tuple.extend([nn.broadcast] * 4)
1140+
1141+
if forced_routed_experts_t is not None:
1142+
current_broadcast_args.append(forced_routed_experts_t)
1143+
current_in_axes_tuple.append(0)
1144+
else:
1145+
current_broadcast_args.append(None)
1146+
current_in_axes_tuple.append(nn.broadcast)
1147+
else:
1148+
current_broadcast_args.append(None)
1149+
current_in_axes_tuple.append(nn.broadcast)
11141150

11151151
y, _ = self.scan_decoder_layers(
11161152
cfg,
@@ -1139,6 +1175,11 @@ def __call__(
11391175
global_layer_idx = global_layer_idx_offset + index
11401176
kv_cache = kv_caches[index] if kv_caches is not None else None
11411177
input_tokens = decoder_input_tokens if cfg.engram_layers else None
1178+
extra_kwargs = {}
1179+
if layer_prefix == "moe_layers" and forced_routed_experts is not None:
1180+
forced_experts = forced_routed_experts[:, :, index, :]
1181+
extra_kwargs["forced_routed_experts"] = forced_experts
1182+
11421183
y, kv_cache = layer(
11431184
config=cfg,
11441185
mesh=mesh,
@@ -1157,6 +1198,7 @@ def __call__(
11571198
kv_cache=kv_cache,
11581199
attention_metadata=attention_metadata,
11591200
decoder_input_tokens=input_tokens,
1201+
**extra_kwargs,
11601202
)
11611203
if kv_caches is not None and kv_cache is not None:
11621204
kv_caches[index] = kv_cache
@@ -1176,6 +1218,7 @@ def __call__(
11761218
slot=slot,
11771219
)
11781220
else:
1221+
moe_lyr_idx = 0
11791222
for lyr in range(cfg.num_decoder_layers):
11801223
RemattedBlockLayer = RemattedBlockLayers[0]
11811224
layer_kwargs = {}
@@ -1212,6 +1255,17 @@ def __call__(
12121255
layer = RemattedBlockLayer(
12131256
config=cfg, mesh=mesh, name=f"layers_{lyr}", quant=self.quant, model_mode=self.model_mode, **layer_kwargs
12141257
)
1258+
current_forced_routed_experts = None
1259+
if supports_forced_routing and forced_routed_experts is not None:
1260+
current_forced_routed_experts = forced_routed_experts[:, :, moe_lyr_idx, :]
1261+
moe_lyr_idx += 1
1262+
elif supports_forced_routing:
1263+
moe_lyr_idx += 1
1264+
1265+
extra_kwargs = {}
1266+
if supports_forced_routing and current_forced_routed_experts is not None:
1267+
extra_kwargs["forced_routed_experts"] = current_forced_routed_experts
1268+
12151269
y, returned_cache = layer(
12161270
y,
12171271
decoder_segment_ids,
@@ -1222,6 +1276,7 @@ def __call__(
12221276
slot=slot,
12231277
kv_cache=kv_cache,
12241278
attention_metadata=attention_metadata,
1279+
**extra_kwargs,
12251280
**layer_call_kwargs,
12261281
)
12271282
if kv_caches is not None and returned_cache is not None:

0 commit comments

Comments
 (0)