Skip to content

Commit 2139c05

Browse files
committed
Measure routing mismatch for Qwen and DeepSeek models
1 parent 39c1b73 commit 2139c05

10 files changed

Lines changed: 510 additions & 59 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: 112 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -793,9 +793,12 @@ def __call__(
793793
kv_caches: list[jax.Array] | None = None,
794794
attention_metadata=None,
795795
deepstack_visual_embeds: None | list[jnp.ndarray] = None,
796+
forced_routed_experts: jax.Array | None = None,
796797
):
797798
cfg = self.config
798799
mesh = self.mesh
800+
if cfg.use_batch_split_schedule and forced_routed_experts is not None:
801+
raise NotImplementedError("Forced routing mismatch measurement is not supported with batch split schedule.")
799802
assert decoder_input_tokens.ndim == 2 # [batch, len]
800803

801804
# [batch, length] -> [batch, length, emb_dim]
@@ -882,24 +885,44 @@ def __call__(
882885
}
883886
dense_layer = RemattedBlockLayers[0]
884887
moe_layer = RemattedBlockLayers[1]
885-
if cfg.engram_layers:
886-
original_dense_call = dense_layer.__call__
887-
original_moe_call = moe_layer.__call__
888-
dense_layer.__call__ = functools.partial(dense_layer.__call__, **layer_call_kwargs)
889-
moe_layer.__call__ = functools.partial(moe_layer.__call__, **layer_call_kwargs)
890888

889+
dense_broadcast_args = list(broadcast_args)
890+
dense_in_axes_tuple = [nn.broadcast] * len(broadcast_args)
891+
input_tokens = decoder_input_tokens if cfg.engram_layers else None
892+
dense_broadcast_args.extend([previous_chunk, slot, None, attention_metadata, input_tokens])
893+
dense_in_axes_tuple.extend([nn.broadcast] * 5)
894+
895+
moe_broadcast_args = list(broadcast_args)
896+
moe_in_axes_tuple = [nn.broadcast] * len(broadcast_args)
897+
moe_broadcast_args.extend([previous_chunk, slot, None, attention_metadata, input_tokens])
898+
moe_in_axes_tuple.extend([nn.broadcast] * 5)
899+
900+
num_moe_layers = cfg.num_decoder_layers - cfg.first_num_dense_layers
901+
902+
forced_routed_experts_t = None
903+
if forced_routed_experts is not None:
904+
forced_routed_experts_t = jnp.transpose(forced_routed_experts, (2, 0, 1, 3))
905+
moe_broadcast_args.append(forced_routed_experts_t)
906+
moe_in_axes_tuple.append(0)
907+
else:
908+
moe_broadcast_args.append(None)
909+
moe_in_axes_tuple.append(nn.broadcast)
910+
911+
if cfg.engram_layers:
891912
common_kwargs = {
892913
"dense_layer": dense_layer,
893914
"moe_layer": moe_layer,
894-
"original_dense_call": original_dense_call,
895-
"original_moe_call": original_moe_call,
896915
"layer_call_kwargs": layer_call_kwargs,
897916
"decoder_segment_ids": decoder_segment_ids,
898917
"decoder_positions": decoder_positions,
899918
"deterministic": deterministic,
900919
"model_mode": model_mode,
901920
"decoder_input_tokens": decoder_input_tokens,
902-
"broadcast_args": broadcast_args,
921+
"dense_broadcast_args": dense_broadcast_args,
922+
"dense_in_axes": dense_in_axes_tuple,
923+
"moe_broadcast_args": moe_broadcast_args,
924+
"moe_in_axes": moe_in_axes_tuple,
925+
"forced_routed_experts": forced_routed_experts_t,
903926
}
904927

905928
# Apply Dense Layers
@@ -922,23 +945,22 @@ def __call__(
922945
**common_kwargs,
923946
)
924947
else:
925-
dense_layer.__call__ = functools.partial(dense_layer.__call__, **layer_call_kwargs)
926948
y, _ = self.scan_decoder_layers(
927949
cfg,
928950
dense_layer,
929951
cfg.first_num_dense_layers,
930952
"dense_layers",
931953
mesh,
932-
in_axes_tuple=(nn.broadcast,) * len(broadcast_args),
954+
in_axes_tuple=tuple(dense_in_axes_tuple),
933955
model_mode=model_mode,
934-
)(y, *broadcast_args)
935-
moe_layer.__call__ = functools.partial(moe_layer.__call__, **layer_call_kwargs)
936-
num_moe_layers = cfg.num_decoder_layers - cfg.first_num_dense_layers
956+
)(y, *dense_broadcast_args)
937957

938958
# If batch-split schedule is used and initialization is complete,
939959
# as detected by immutable params, use deepseek_batchsplit custom
940960
# scan with initialized parameters.
941961
if cfg.use_batch_split_schedule and not self.is_mutable_collection("params"):
962+
if forced_routed_experts is not None:
963+
raise NotImplementedError("Forced routing mismatch measurement is not supported with batch split schedule.")
942964
# old version of batch-split that fully uses qwix quantization.
943965
if cfg.use_qwix_quantization and not cfg.use_manual_quantization:
944966
y = deepseek_batchsplit_fp8.scan_batch_split_layers(
@@ -971,9 +993,9 @@ def __call__(
971993
num_moe_layers,
972994
"moe_layers",
973995
mesh,
974-
in_axes_tuple=(nn.broadcast,) * len(broadcast_args),
996+
in_axes_tuple=tuple(moe_in_axes_tuple),
975997
model_mode=model_mode,
976-
)(y, *broadcast_args)
998+
)(y, *moe_broadcast_args)
977999
elif cfg.decoder_block == DecoderBlockType.GEMMA3:
9781000
bidirectional_mask_value = multimodal_input.bidirectional_mask if multimodal_input is not None else None
9791001
y = self._apply_gemma3_scanned_blocks(
@@ -1012,19 +1034,35 @@ def __call__(
10121034
current_broadcast_args = list(broadcast_args)
10131035
current_in_axes_tuple = list(in_axes_tuple)
10141036

1037+
if forced_routed_experts is not None:
1038+
# Transpose [B, L, N, E] -> [N, B, L, E] for scan
1039+
forced_routed_experts_t = jnp.transpose(forced_routed_experts, (2, 0, 1, 3))
1040+
cycle_interval = cfg.inhomogeneous_layer_cycle_interval
1041+
reshaped_forced = jnp.reshape(
1042+
forced_routed_experts_t,
1043+
(scan_length, cycle_interval) + forced_routed_experts_t.shape[1:]
1044+
)
1045+
else:
1046+
reshaped_forced = None
1047+
10151048
if kv_caches is not None:
10161049
# Stack kv_caches for scan: [num_layers, ...]
10171050
stacked_kv_cache = jnp.stack(kv_caches, axis=0)
10181051

10191052
# We pass (y, stacked_kv_cache, 0) as the carry
10201053
carry = (y, stacked_kv_cache, 0)
10211054

1022-
# We don't pass kv_cache as a scanned argument anymore
1023-
10241055
# Pass None for previous_chunk, slot, kv_cache to align with __call__ signature
10251056
current_broadcast_args.extend([None, None, None, attention_metadata])
10261057
current_in_axes_tuple.extend([nn.broadcast] * 4)
10271058

1059+
if reshaped_forced is not None:
1060+
current_broadcast_args.append(reshaped_forced)
1061+
current_in_axes_tuple.append(0)
1062+
else:
1063+
current_broadcast_args.append(None)
1064+
current_in_axes_tuple.append(nn.broadcast)
1065+
10281066
max_logging.info(f"DEBUG: len(current_broadcast_args)={len(current_broadcast_args)}")
10291067
max_logging.info(f"DEBUG: current_broadcast_args={[type(a) for a in current_broadcast_args]}")
10301068

@@ -1046,8 +1084,15 @@ def __call__(
10461084
kv_caches[i] = returned_kv_cache[i]
10471085
else:
10481086
# Fallback to old behavior if kv_caches is None (not vLLM RPA)
1049-
current_broadcast_args.append(None)
1050-
current_in_axes_tuple.append(nn.broadcast)
1087+
current_broadcast_args.extend([None, None, None, None])
1088+
current_in_axes_tuple.extend([nn.broadcast] * 4)
1089+
1090+
if reshaped_forced is not None:
1091+
current_broadcast_args.append(reshaped_forced)
1092+
current_in_axes_tuple.append(0)
1093+
else:
1094+
current_broadcast_args.append(None)
1095+
current_in_axes_tuple.append(nn.broadcast)
10511096

10521097
y, _ = self.scan_decoder_layers(
10531098
cfg,
@@ -1076,6 +1121,10 @@ def __call__(
10761121
global_layer_idx = global_layer_idx_offset + index
10771122
kv_cache = kv_caches[index] if kv_caches is not None else None
10781123
input_tokens = decoder_input_tokens if cfg.engram_layers else None
1124+
extra_kwargs = {}
1125+
if layer_prefix == "moe_layers" and forced_routed_experts is not None:
1126+
extra_kwargs["forced_routed_experts"] = forced_routed_experts[:, :, index, :]
1127+
10791128
y, kv_cache = layer(
10801129
config=cfg,
10811130
mesh=mesh,
@@ -1094,6 +1143,7 @@ def __call__(
10941143
kv_cache=kv_cache,
10951144
attention_metadata=attention_metadata,
10961145
decoder_input_tokens=input_tokens,
1146+
**extra_kwargs,
10971147
)
10981148
if kv_caches is not None and kv_cache is not None:
10991149
kv_caches[index] = kv_cache
@@ -1113,6 +1163,7 @@ def __call__(
11131163
slot=slot,
11141164
)
11151165
else:
1166+
moe_lyr_idx = 0
11161167
for lyr in range(cfg.num_decoder_layers):
11171168
RemattedBlockLayer = RemattedBlockLayers[0]
11181169
layer_kwargs = {}
@@ -1149,6 +1200,30 @@ def __call__(
11491200
layer = RemattedBlockLayer(
11501201
config=cfg, mesh=mesh, name=f"layers_{lyr}", quant=self.quant, model_mode=self.model_mode, **layer_kwargs
11511202
)
1203+
1204+
is_moe = False
1205+
if cfg.decoder_block in (
1206+
DecoderBlockType.MIXTRAL,
1207+
DecoderBlockType.QWEN3_MOE,
1208+
DecoderBlockType.QWEN3_NEXT,
1209+
DecoderBlockType.QWEN3_5,
1210+
DecoderBlockType.QWEN3_CUSTOM_MOE,
1211+
):
1212+
is_moe = True
1213+
elif cfg.decoder_block == DecoderBlockType.LLAMA4:
1214+
is_moe = llama4.determine_is_moe_layer(lyr, self.config.interleave_moe_layer_step)
1215+
1216+
current_forced_routed_experts = None
1217+
if is_moe and forced_routed_experts is not None:
1218+
current_forced_routed_experts = forced_routed_experts[:, :, moe_lyr_idx, :]
1219+
moe_lyr_idx += 1
1220+
elif is_moe:
1221+
moe_lyr_idx += 1
1222+
1223+
extra_kwargs = {}
1224+
if is_moe and current_forced_routed_experts is not None:
1225+
extra_kwargs["forced_routed_experts"] = current_forced_routed_experts
1226+
11521227
y, returned_cache = layer(
11531228
y,
11541229
decoder_segment_ids,
@@ -1159,6 +1234,7 @@ def __call__(
11591234
slot=slot,
11601235
kv_cache=kv_cache,
11611236
attention_metadata=attention_metadata,
1237+
**extra_kwargs,
11621238
**layer_call_kwargs,
11631239
)
11641240
if kv_caches is not None and returned_cache is not None:
@@ -1466,10 +1542,12 @@ def _apply_single_engram_layer(self, y, current_idx, layer_type, **kwargs):
14661542
"""Applies a single, unscanned Engram layer."""
14671543
layer = kwargs["dense_layer"] if layer_type == "dense" else kwargs["moe_layer"]
14681544
layer_prefix = "dense_layers" if layer_type == "dense" else "moe_layers"
1469-
original_call = kwargs["original_dense_call"] if layer_type == "dense" else kwargs["original_moe_call"]
1470-
layer_call_kwargs = kwargs["layer_call_kwargs"]
14711545

1472-
layer.__call__ = original_call
1546+
extra_kwargs = {}
1547+
if layer_type == "moe" and kwargs.get("forced_routed_experts") is not None:
1548+
moe_idx = current_idx - self.config.first_num_dense_layers
1549+
extra_kwargs["forced_routed_experts"] = kwargs["forced_routed_experts"][moe_idx]
1550+
14731551
y, _ = layer(
14741552
config=self.config,
14751553
mesh=self.mesh,
@@ -1483,27 +1561,35 @@ def _apply_single_engram_layer(self, y, current_idx, layer_type, **kwargs):
14831561
kwargs["decoder_positions"],
14841562
kwargs["deterministic"],
14851563
kwargs["model_mode"],
1564+
previous_chunk=kwargs["layer_call_kwargs"]["previous_chunk"],
1565+
slot=kwargs["layer_call_kwargs"]["slot"],
14861566
decoder_input_tokens=kwargs["decoder_input_tokens"],
1487-
**layer_call_kwargs,
1567+
**extra_kwargs,
14881568
)
1489-
layer.__call__ = functools.partial(original_call, **layer_call_kwargs)
14901569
return y
14911570

14921571
def _apply_scanned_chunk(self, y, current_idx, next_boundary, layer_type, **kwargs):
14931572
"""Applies a contiguous chunk of layers using the scan operation."""
14941573
layer = kwargs["dense_layer"] if layer_type == "dense" else kwargs["moe_layer"]
14951574
layer_prefix = "dense_layers" if layer_type == "dense" else "moe_layers"
1496-
broadcast_args = kwargs["broadcast_args"]
1575+
broadcast_args = kwargs["dense_broadcast_args"] if layer_type == "dense" else list(kwargs["moe_broadcast_args"])
1576+
in_axes_tuple = kwargs["dense_in_axes"] if layer_type == "dense" else list(kwargs["moe_in_axes"])
14971577
scan_length = next_boundary - current_idx
14981578

14991579
if scan_length > 0:
1580+
if layer_type == "moe" and broadcast_args[-1] is not None:
1581+
moe_start = current_idx - self.config.first_num_dense_layers
1582+
moe_end = next_boundary - self.config.first_num_dense_layers
1583+
sliced_forced = broadcast_args[-1][moe_start:moe_end]
1584+
broadcast_args[-1] = sliced_forced
1585+
15001586
y, _ = self.scan_decoder_layers(
15011587
self.config,
15021588
layer,
15031589
scan_length,
15041590
f"{layer_prefix}_{current_idx}_{next_boundary - 1}",
15051591
self.mesh,
1506-
in_axes_tuple=(nn.broadcast,) * len(broadcast_args),
1592+
in_axes_tuple=tuple(in_axes_tuple),
15071593
model_mode=kwargs["model_mode"],
15081594
)(y, *broadcast_args)
15091595
return y

0 commit comments

Comments
 (0)