Skip to content

Commit 1b071c3

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 1432864 commit 1b071c3

10 files changed

Lines changed: 497 additions & 33 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: 58 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,18 @@ def __call__(
10751081
current_broadcast_args = list(broadcast_args)
10761082
current_in_axes_tuple = list(in_axes_tuple)
10771083

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

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

@@ -1109,8 +1135,19 @@ def __call__(
11091135
kv_caches[i] = returned_kv_cache[i]
11101136
else:
11111137
# 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)
1138+
if supports_forced_routing:
1139+
current_broadcast_args.extend([None, None, None, None])
1140+
current_in_axes_tuple.extend([nn.broadcast] * 4)
1141+
1142+
if forced_routed_experts_t is not None:
1143+
current_broadcast_args.append(forced_routed_experts_t)
1144+
current_in_axes_tuple.append(0)
1145+
else:
1146+
current_broadcast_args.append(None)
1147+
current_in_axes_tuple.append(nn.broadcast)
1148+
else:
1149+
current_broadcast_args.append(None)
1150+
current_in_axes_tuple.append(nn.broadcast)
11141151

11151152
y, _ = self.scan_decoder_layers(
11161153
cfg,
@@ -1139,6 +1176,11 @@ def __call__(
11391176
global_layer_idx = global_layer_idx_offset + index
11401177
kv_cache = kv_caches[index] if kv_caches is not None else None
11411178
input_tokens = decoder_input_tokens if cfg.engram_layers else None
1179+
extra_kwargs = {}
1180+
if layer_prefix == "moe_layers" and forced_routed_experts is not None:
1181+
forced_experts = forced_routed_experts[:, :, index, :]
1182+
extra_kwargs["forced_routed_experts"] = forced_experts
1183+
11421184
y, kv_cache = layer(
11431185
config=cfg,
11441186
mesh=mesh,
@@ -1157,6 +1199,7 @@ def __call__(
11571199
kv_cache=kv_cache,
11581200
attention_metadata=attention_metadata,
11591201
decoder_input_tokens=input_tokens,
1202+
**extra_kwargs,
11601203
)
11611204
if kv_caches is not None and kv_cache is not None:
11621205
kv_caches[index] = kv_cache
@@ -1176,6 +1219,7 @@ def __call__(
11761219
slot=slot,
11771220
)
11781221
else:
1222+
moe_lyr_idx = 0
11791223
for lyr in range(cfg.num_decoder_layers):
11801224
RemattedBlockLayer = RemattedBlockLayers[0]
11811225
layer_kwargs = {}
@@ -1212,6 +1256,17 @@ def __call__(
12121256
layer = RemattedBlockLayer(
12131257
config=cfg, mesh=mesh, name=f"layers_{lyr}", quant=self.quant, model_mode=self.model_mode, **layer_kwargs
12141258
)
1259+
current_forced_routed_experts = None
1260+
if supports_forced_routing and forced_routed_experts is not None:
1261+
current_forced_routed_experts = forced_routed_experts[:, :, moe_lyr_idx, :]
1262+
moe_lyr_idx += 1
1263+
elif supports_forced_routing:
1264+
moe_lyr_idx += 1
1265+
1266+
extra_kwargs = {}
1267+
if supports_forced_routing and current_forced_routed_experts is not None:
1268+
extra_kwargs["forced_routed_experts"] = current_forced_routed_experts
1269+
12151270
y, returned_cache = layer(
12161271
y,
12171272
decoder_segment_ids,
@@ -1222,6 +1277,7 @@ def __call__(
12221277
slot=slot,
12231278
kv_cache=kv_cache,
12241279
attention_metadata=attention_metadata,
1280+
**extra_kwargs,
12251281
**layer_call_kwargs,
12261282
)
12271283
if kv_caches is not None and returned_cache is not None:

0 commit comments

Comments
 (0)