Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions src/maxtext/layers/mhc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from jax.sharding import Mesh
from maxtext.common.common_types import Array, Config
from maxtext.common.common_types import HyperConnectionType
from maxtext.layers.initializers import default_bias_init, default_scalar_init, nd_dense_init, variable_to_logically_partitioned
from maxtext.layers import nnx_wrappers
from maxtext.layers.initializers import default_bias_init, default_scalar_init, nd_dense_init
from maxtext.layers import linears
from maxtext.layers.normalizations import RMSNorm


Expand Down Expand Up @@ -313,4 +313,43 @@ def __call__(
return res_out + post_out, metadata


class DeepSeek4HyperHead(nnx.Module):
"""DeepSeek V4 Hyper Head."""

def __init__(
self,
config: Config,
mesh: Mesh,
rngs: nnx.Rngs,
):
self.config = config
self.mesh = mesh
self.rngs = rngs
self.k = config.mhc_expansion_rate
self.dim = config.emb_dim
self.dtype = config.dtype
self.weight_dtype = config.weight_dtype

# tid2eid layers
self.tid2eid = nnx.Sequential(
*[
linears.DenseGeneral(
in_features_shape=self.dim,
out_features_shape=self.dim,
dtype=self.dtype,
weight_dtype=self.weight_dtype,
rngs=self.rngs,
)
Comment on lines +336 to +342

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The DenseGeneral layers inside DeepSeek4HyperHead are initialized without specifying logical kernel_axes, shard_mode, matmul_precision, and parameter_memory_host_offload. Without these, the weight matrices will not be sharded (replicated instead), which can lead to high memory usage and potential Out-Of-Memory (OOM) errors during large-scale training. Additionally, the layers will not respect the user's configuration for sharding, precision, and offloading. Specifying these parameters ensures proper FSDP sharding and consistency with the rest of the model.

            linears.DenseGeneral(
                in_features_shape=self.dim,
                out_features_shape=self.dim,
                dtype=self.dtype,
                weight_dtype=self.weight_dtype,
                kernel_axes=("embed", None),
                shard_mode=config.shard_mode,
                matmul_precision=config.matmul_precision,
                parameter_memory_host_offload=config.parameter_memory_host_offload,
                rngs=self.rngs,
            )

for _ in range(config.first_num_hash_layers)
]
)

def __call__(self, x: Array) -> Array:
# x shape: [batch, seq, expansion_rate, emb]
# Reduce expansion_rate dimension
x = jnp.sum(x, axis=2, dtype=x.dtype)

# Apply tid2eid layers
x = self.tid2eid(x)

return x
16 changes: 13 additions & 3 deletions src/maxtext/layers/nnx_decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,13 @@ def __init__(
self.is_gemma4 = self.config.decoder_block == DecoderBlockType.GEMMA4
self.is_gemma4_small = self.config.decoder_block == DecoderBlockType.GEMMA4_SMALL

if config.mhc_expansion_rate > 1 and config.decoder_block == DecoderBlockType.DEEPSEEK4:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Accessing config.mhc_expansion_rate directly will raise an AttributeError for any model configuration that does not define mhc_expansion_rate (such as standard LLaMA or Gemma models). Using getattr(config, "mhc_expansion_rate", 1) prevents this crash and safely defaults to 1 when the attribute is missing.

Suggested change
if config.mhc_expansion_rate > 1 and config.decoder_block == DecoderBlockType.DEEPSEEK4:
if getattr(config, "mhc_expansion_rate", 1) > 1 and config.decoder_block == DecoderBlockType.DEEPSEEK4:

self.hc_head = mhc.DeepSeek4HyperHead(
config=config,
mesh=self.mesh,
rngs=self.rngs,
)

self._init_decoder_layers(decoder_block_classes, rngs, mesh)

def _init_decoder_layers(self, decoder_block_classes, rngs, mesh):
Expand Down Expand Up @@ -1967,13 +1974,16 @@ def pure_layer_fn(graphdef_in, state_in, y_in, kv_in):

assert isinstance(y, jax.Array)

# After the final transformer layer, `y` holds the raw, un-normalized hidden state.
# After the final transformer layer, `y` holds the raw, un-normalized hidden state.
if getattr(cfg, "mhc_expansion_rate", 1) > 1:
# (batch, length, mhc_expansion_rate, emb_dim) --> (batch, length, emb_dim)
hidden_state = mhc_reduce(y)
if cfg.decoder_block == DecoderBlockType.DEEPSEEK4:
hidden_state = self.hc_head(y)
else:
# (batch, length, mhc_expansion_rate, emb_dim) --> (batch, length, emb_dim)
hidden_state = mhc_reduce(y)
else:
hidden_state = y

# When invoking from vLLM with RPA attention, logit computation is deferred to a later stage.
if cfg.attention in ("vllm_rpa", "vllm_batched_rpa"):
logits = None
Expand Down
Loading