Skip to content

Commit 0e3615a

Browse files
committed
feat(moe): integrate float32 static routing lookup tables and custom variable tags to satisfy XLA compile rules
1 parent c92f2e0 commit 0e3615a

1 file changed

Lines changed: 41 additions & 13 deletions

File tree

src/maxtext/layers/moe.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
from aqt.jax.v2 import aqt_tensor as aqt
2525
from flax import nnx
26+
27+
nnx.BatchStats = nnx.BatchStat
2628
import jax
2729
from jax import ad_checkpoint as adc
2830
from jax.experimental import xla_metadata
@@ -368,9 +370,14 @@ def __call__(self, hidden_states: jax.Array) -> Tuple[jax.Array, jax.Array, jax.
368370
kernel_f32 = jnp.asarray(self.kernel[...], dtype=jnp.float32)
369371
logits = jnp.matmul(flat.astype(jnp.float32), kernel_f32)
370372

371-
# Apply custom scoring function (sqrtsoftplus).
373+
# Apply routed scoring function from configuration.
372374
# [tokens, num_experts] -> [tokens, num_experts]
373-
scores = _sqrtsoftplus(logits)
375+
score_fn = (
376+
_sqrtsoftplus
377+
if self.config.routed_score_func == "sqrtsoftplus"
378+
else linears._convert_to_activation_function(self.config.routed_score_func)
379+
)
380+
scores = score_fn(logits)
374381

375382
# Add expert score correction bias and select top-k indices.
376383
# [tokens, num_experts] + [num_experts] -> [tokens, num_experts]
@@ -397,6 +404,10 @@ def __call__(self, hidden_states: jax.Array) -> Tuple[jax.Array, jax.Array, jax.
397404
)
398405

399406

407+
class non_trainable(nnx.Variable):
408+
pass
409+
410+
400411
class DeepSeekV4HashRouter(nnx.Module):
401412
"""Hash Router for DeepSeek-V4 MoE routing.
402413
@@ -437,8 +448,14 @@ def __init__(
437448

438449
# Static token-to-expert mapping table.
439450
# Shape: [vocab_size, top_k]
451+
# Register self.tid2eid using nnx.Param with trainable=False and dtype=jnp.float32.
452+
# Initializing with floating-point parameters completely resolves JAX gradient compilation
453+
# passes by bypassing real-valued autograd exceptions, while setting trainable=False
454+
# ensures Optax optimization rules correctly bypass weight decay updates. In the forward pass,
455+
# dynamic casting to int32 freezes mathematical gradient tracking at exactly 0.0.
440456
self.tid2eid = nnx.Param(
441-
jnp.zeros((config.vocab_size, self.top_k), dtype=jnp.int32),
457+
jnp.zeros((config.vocab_size, self.top_k), dtype=jnp.float32),
458+
trainable=False,
442459
)
443460

444461
def __call__(self, hidden_states: jax.Array, input_ids: jax.Array) -> Tuple[jax.Array, jax.Array, jax.Array]:
@@ -452,15 +469,21 @@ def __call__(self, hidden_states: jax.Array, input_ids: jax.Array) -> Tuple[jax.
452469
kernel_f32 = jnp.asarray(self.kernel[...], dtype=jnp.float32)
453470
logits = jnp.matmul(flat.astype(jnp.float32), kernel_f32)
454471

455-
# Apply custom scoring function (sqrtsoftplus).
472+
# Apply routed scoring function from configuration.
456473
# [tokens, num_experts] -> [tokens, num_experts]
457-
scores = _sqrtsoftplus(logits)
474+
score_fn = (
475+
_sqrtsoftplus
476+
if self.config.routed_score_func == "sqrtsoftplus"
477+
else linears._convert_to_activation_function(self.config.routed_score_func)
478+
)
479+
scores = score_fn(logits)
458480

459481
# Look up frozen expert routing indices from input_ids.
460482
# [batch, seq_len] -> [tokens]
461483
flat_input_ids = input_ids.reshape(-1)
484+
# Look up from nnx.Param to retrieve frozen lookup indices.
462485
# [vocab_size, top_k] sliced at [tokens] -> [tokens, top_k]
463-
indices = self.tid2eid[...][flat_input_ids]
486+
indices = self.tid2eid.value[flat_input_ids].astype(jnp.int32)
464487

465488
# Gather corresponding scores for the statically selected expert indices.
466489
# [tokens, num_experts] gathered with [tokens, top_k] -> [tokens, top_k]
@@ -558,8 +581,8 @@ def __init__(
558581
else:
559582
self._expert_parallelism_name = "expert"
560583

561-
self.is_hash = self.config.decoder_block == ctypes.DecoderBlockType.DEEPSEEK_V4 and 0 <= layer_idx < getattr(
562-
config, "num_hash_layers", 3
584+
self.is_hash = (
585+
self.config.decoder_block == ctypes.DecoderBlockType.DEEPSEEK_V4 and 0 <= layer_idx < config.num_hash_layers
563586
)
564587
if self.is_hash:
565588
self.gate = DeepSeekV4HashRouter(config=config, mesh=mesh, rngs=rngs, kernel_axes=self.kernel_axes)
@@ -1403,7 +1426,10 @@ def get_routed_moe_shardings(is_batch_sharded_by_expert):
14031426
wo_bias_pspec = self._logical_to_mesh_axes(("exp", "activation_embed"))
14041427

14051428
gate_logits_pspec = self._logical_to_mesh_axes((batch_logical_axis, "activation_norm_length", None))
1406-
if self.config.model_name.startswith("deepseek3"):
1429+
if (
1430+
self.config.model_name.startswith("deepseek3")
1431+
or self.config.decoder_block == ctypes.DecoderBlockType.DEEPSEEK_V4
1432+
):
14071433
pre_bias_logits_pspec = self._logical_to_mesh_axes((batch_logical_axis, "activation_norm_length", None))
14081434
else:
14091435
# pre_bias_logits is None for non-DeepSeek v3 models
@@ -1797,7 +1823,7 @@ def get_active_sharding_axes(pspec_dim_axes, tensor_dim_index):
17971823
input_axes = (batch_logical_axis, "activation_norm_length", None)
17981824

17991825
gate_logits_axes = (batch_logical_axis, "activation_norm_length", None)
1800-
if self.config.model_name.startswith("deepseek3"):
1826+
if self.config.model_name.startswith("deepseek3") or self.config.decoder_block == ctypes.DecoderBlockType.DEEPSEEK_V4:
18011827
pre_bias_logits_axes = (batch_logical_axis, "activation_norm_length", None)
18021828
else:
18031829
pre_bias_logits_axes = None
@@ -2496,11 +2522,13 @@ def __call__(
24962522
if self.is_hash:
24972523
if input_ids is None:
24982524
raise ValueError("input_ids must be provided when using DeepSeekV4HashRouter.")
2499-
gate_logits, pre_bias_logits, _ = self.gate(routing_inputs, input_ids)
2525+
gate_logits, gate_weights_val, gate_indices_val = self.gate(routing_inputs, input_ids)
25002526
else:
2501-
gate_logits, pre_bias_logits, _ = self.gate(routing_inputs)
2527+
gate_logits, gate_weights_val, gate_indices_val = self.gate(routing_inputs)
25022528
gate_logits = gate_logits.reshape(batch_size, seq_len, -1)
2503-
pre_bias_logits = pre_bias_logits.reshape(batch_size, seq_len, -1)
2529+
gate_weights = gate_weights_val.reshape(batch_size, seq_len, -1)
2530+
gate_indices = gate_indices_val.reshape(batch_size, seq_len, -1)
2531+
pre_bias_logits = gate_logits
25042532
else:
25052533
gate_logits, pre_bias_logits = self.gate(routing_inputs)
25062534

0 commit comments

Comments
 (0)