Skip to content

Commit 995eea2

Browse files
Merge pull request #4337 from AI-Hypercomputer:dsv4-bug-fixes
PiperOrigin-RevId: 949857735
2 parents 436d7c4 + 2d1c867 commit 995eea2

9 files changed

Lines changed: 1014 additions & 24 deletions

File tree

src/maxtext/checkpoint_conversion/utils/param_mapping.py

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3870,6 +3870,278 @@ def reshape_vision_attn_out(input_tensor, target_shape):
38703870

38713871

38723872
# {maxtext model name: {maxtext weight name: hf weight name}}
3873+
3874+
3875+
def DEEPSEEKV4_MAXTEXT_TO_HF_PARAM_MAPPING(config, maxtext_config, scan_layers=False):
3876+
"""Maps MaxText parameter keys to HuggingFace parameter keys for DeepSeek V4."""
3877+
n_layers = config["num_hidden_layers"]
3878+
num_experts = config.get("n_routed_experts", 8)
3879+
3880+
mapping = {
3881+
"params-token_embedder-embedding": "model.embed_tokens.weight",
3882+
"params-decoder-decoder_norm-scale": "model.norm.weight",
3883+
"params-decoder-logits_dense-kernel": "head.weight",
3884+
"params-decoder-hc_head-hc_fn": "model.hc_head.hc_fn",
3885+
"params-decoder-hc_head-hc_base": "model.hc_head.hc_base",
3886+
"params-decoder-hc_head-hc_scale": "model.hc_head.hc_scale",
3887+
}
3888+
3889+
def add_layer_mapping(mt_layer_path, hf_layer_indices):
3890+
is_list = isinstance(hf_layer_indices, list)
3891+
3892+
def get_hf_key(subpath):
3893+
if subpath is None:
3894+
return None
3895+
if is_list:
3896+
return [f"model.layers.{idx}.{subpath}" for idx in hf_layer_indices]
3897+
else:
3898+
return f"model.layers.{hf_layer_indices}.{subpath}"
3899+
3900+
def get_hf_expert_keys(expert_subpath_template):
3901+
if is_list:
3902+
return [
3903+
[f"model.layers.{idx}.mlp.experts.{e}.{expert_subpath_template}" for idx in hf_layer_indices]
3904+
for e in range(num_experts)
3905+
]
3906+
else:
3907+
return [f"model.layers.{hf_layer_indices}.mlp.experts.{e}.{expert_subpath_template}" for e in range(num_experts)]
3908+
3909+
layer_map = {
3910+
f"{mt_layer_path}-pre_self_attention_layer_norm-scale": get_hf_key("input_layernorm.weight"),
3911+
f"{mt_layer_path}-post_self_attention_layer_norm-scale": get_hf_key("post_attention_layernorm.weight"),
3912+
# Attention
3913+
f"{mt_layer_path}-self_attention-wq_a-kernel": get_hf_key("self_attn.q_a_proj.weight"),
3914+
f"{mt_layer_path}-self_attention-q_norm-scale": get_hf_key("self_attn.q_a_norm.weight"),
3915+
f"{mt_layer_path}-self_attention-wq_b-kernel": get_hf_key("self_attn.q_b_proj.weight"),
3916+
f"{mt_layer_path}-self_attention-wkv-kernel": get_hf_key("self_attn.kv_proj.weight"),
3917+
f"{mt_layer_path}-self_attention-kv_norm-scale": get_hf_key("self_attn.kv_norm.weight"),
3918+
f"{mt_layer_path}-self_attention-sinks": get_hf_key("self_attn.sinks"),
3919+
f"{mt_layer_path}-self_attention-o_a_proj-kernel": get_hf_key("self_attn.o_a_proj.weight"),
3920+
f"{mt_layer_path}-self_attention-o_b_proj-kernel": get_hf_key("self_attn.o_b_proj.weight"),
3921+
# mHC Attention
3922+
f"{mt_layer_path}-mhc_attention-mhc_norm-scale": None,
3923+
f"{mt_layer_path}-mhc_attention-pre_alpha": get_hf_key("attn_hc.fn"),
3924+
f"{mt_layer_path}-mhc_attention-post_alpha": get_hf_key("attn_hc.fn"),
3925+
f"{mt_layer_path}-mhc_attention-res_alpha": get_hf_key("attn_hc.fn"),
3926+
f"{mt_layer_path}-mhc_attention-pre_beta": get_hf_key("attn_hc.base"),
3927+
f"{mt_layer_path}-mhc_attention-post_beta": get_hf_key("attn_hc.base"),
3928+
f"{mt_layer_path}-mhc_attention-res_beta": get_hf_key("attn_hc.base"),
3929+
f"{mt_layer_path}-mhc_attention-pre_alpha_scale": get_hf_key("attn_hc.scale"),
3930+
f"{mt_layer_path}-mhc_attention-post_alpha_scale": get_hf_key("attn_hc.scale"),
3931+
f"{mt_layer_path}-mhc_attention-res_alpha_scale": get_hf_key("attn_hc.scale"),
3932+
# mHC MLP
3933+
f"{mt_layer_path}-mhc_mlp-mhc_norm-scale": None,
3934+
f"{mt_layer_path}-mhc_mlp-pre_alpha": get_hf_key("ffn_hc.fn"),
3935+
f"{mt_layer_path}-mhc_mlp-post_alpha": get_hf_key("ffn_hc.fn"),
3936+
f"{mt_layer_path}-mhc_mlp-res_alpha": get_hf_key("ffn_hc.fn"),
3937+
f"{mt_layer_path}-mhc_mlp-pre_beta": get_hf_key("ffn_hc.base"),
3938+
f"{mt_layer_path}-mhc_mlp-post_beta": get_hf_key("ffn_hc.base"),
3939+
f"{mt_layer_path}-mhc_mlp-res_beta": get_hf_key("ffn_hc.base"),
3940+
f"{mt_layer_path}-mhc_mlp-pre_alpha_scale": get_hf_key("ffn_hc.scale"),
3941+
f"{mt_layer_path}-mhc_mlp-post_alpha_scale": get_hf_key("ffn_hc.scale"),
3942+
f"{mt_layer_path}-mhc_mlp-res_alpha_scale": get_hf_key("ffn_hc.scale"),
3943+
# MoE Block
3944+
f"{mt_layer_path}-mlp-MoeBlock_0-gate-kernel": get_hf_key("mlp.gate.weight"),
3945+
# Shared Experts
3946+
f"{mt_layer_path}-mlp-shared_experts-wi_0-kernel": get_hf_key("mlp.shared_experts.gate_proj.weight"),
3947+
f"{mt_layer_path}-mlp-shared_experts-wi_1-kernel": get_hf_key("mlp.shared_experts.up_proj.weight"),
3948+
f"{mt_layer_path}-mlp-shared_experts-wo-kernel": get_hf_key("mlp.shared_experts.down_proj.weight"),
3949+
# Stacked Experts
3950+
f"{mt_layer_path}-mlp-MoeBlock_0-wi_0": get_hf_expert_keys("w1.weight"),
3951+
f"{mt_layer_path}-mlp-MoeBlock_0-wi_1": get_hf_expert_keys("w3.weight"),
3952+
f"{mt_layer_path}-mlp-MoeBlock_0-wo": get_hf_expert_keys("w2.weight"),
3953+
}
3954+
3955+
if (is_list and hf_layer_indices[0] >= 3) or (not is_list and hf_layer_indices >= 3):
3956+
layer_map[f"{mt_layer_path}-mlp-MoeBlock_0-gate-bias"] = get_hf_key("mlp.gate.e_score_correction_bias")
3957+
3958+
first_idx = hf_layer_indices[0] if is_list else hf_layer_indices
3959+
if first_idx >= 2:
3960+
if first_idx % 2 == 0 or first_idx == 2:
3961+
layer_map.update(
3962+
{
3963+
f"{mt_layer_path}-self_attention-csa_compressor-kv_proj-kernel": get_hf_key(
3964+
"self_attn.compressor.kv_proj.weight"
3965+
),
3966+
f"{mt_layer_path}-self_attention-csa_compressor-gate_proj-kernel": get_hf_key(
3967+
"self_attn.compressor.gate_proj.weight"
3968+
),
3969+
f"{mt_layer_path}-self_attention-csa_compressor-position_bias": get_hf_key(
3970+
"self_attn.compressor.position_bias"
3971+
),
3972+
f"{mt_layer_path}-self_attention-csa_compressor-kv_norm-scale": get_hf_key(
3973+
"self_attn.compressor.kv_norm.weight"
3974+
),
3975+
f"{mt_layer_path}-self_attention-csa_compressor-indexer-gate_proj-kernel": get_hf_key(
3976+
"self_attn.compressor.indexer.gate_proj.weight"
3977+
),
3978+
f"{mt_layer_path}-self_attention-csa_compressor-indexer-kv_proj-kernel": get_hf_key(
3979+
"self_attn.compressor.indexer.kv_proj.weight"
3980+
),
3981+
f"{mt_layer_path}-self_attention-csa_compressor-indexer-q_proj-kernel": get_hf_key(
3982+
"self_attn.compressor.indexer.q_b_proj.weight"
3983+
),
3984+
f"{mt_layer_path}-self_attention-csa_compressor-indexer-weights_proj-kernel": get_hf_key(
3985+
"self_attn.compressor.indexer.scorer.weights_proj.weight"
3986+
),
3987+
f"{mt_layer_path}-self_attention-csa_compressor-indexer-position_bias": get_hf_key(
3988+
"self_attn.compressor.indexer.position_bias"
3989+
),
3990+
f"{mt_layer_path}-self_attention-csa_compressor-indexer-kv_norm-scale": get_hf_key(
3991+
"self_attn.compressor.indexer.kv_norm.weight"
3992+
),
3993+
}
3994+
)
3995+
else:
3996+
layer_map.update(
3997+
{
3998+
f"{mt_layer_path}-self_attention-hca_compressor-kv_proj-kernel": get_hf_key(
3999+
"self_attn.compressor.kv_proj.weight"
4000+
),
4001+
f"{mt_layer_path}-self_attention-hca_compressor-gate_proj-kernel": get_hf_key(
4002+
"self_attn.compressor.gate_proj.weight"
4003+
),
4004+
f"{mt_layer_path}-self_attention-hca_compressor-position_bias": get_hf_key(
4005+
"self_attn.compressor.position_bias"
4006+
),
4007+
f"{mt_layer_path}-self_attention-hca_compressor-kv_norm-scale": get_hf_key(
4008+
"self_attn.compressor.kv_norm.weight"
4009+
),
4010+
}
4011+
)
4012+
4013+
mapping.update(layer_map)
4014+
4015+
if not scan_layers:
4016+
for i in range(n_layers):
4017+
add_layer_mapping(f"params-decoder-layers_{i}", i)
4018+
else:
4019+
for i in range(3):
4020+
add_layer_mapping(f"params-decoder-layers_{i}", i)
4021+
add_layer_mapping("params-decoder-scanned_blocks-layers_0", list(range(3, n_layers, 2)))
4022+
add_layer_mapping("params-decoder-scanned_blocks-layers_1", list(range(4, n_layers, 2)))
4023+
4024+
for i in range(3):
4025+
mapping[f"Tid2EidVar-decoder-layers_{i}-mlp-MoeBlock_0-tid2eid"] = f"model.layers.{i}.mlp.gate.tid2eid"
4026+
4027+
return mapping
4028+
4029+
4030+
def DEEPSEEKV4_MAXTEXT_TO_HF_PARAM_HOOK_FN(config, maxtext_config, scan_layers=False, saving_to_hf=False):
4031+
"""Returns hook functions for transforming weights between MaxText and HuggingFace for DeepSeek V4."""
4032+
4033+
def transpose(input_tensor, target_shape=None):
4034+
return np.transpose(input_tensor)
4035+
4036+
def transpose_stack(input_tensor, target_shape=None):
4037+
# input_tensor is a list of tensors
4038+
stacked = np.stack(input_tensor, axis=0) # [E, out, in]
4039+
return np.transpose(stacked, (0, 2, 1)) # [E, in, out]
4040+
4041+
def ones_norm(input_tensor, target_shape=None):
4042+
return np.ones(target_shape, dtype=np.float32)
4043+
4044+
def identity(input_tensor, target_shape=None):
4045+
return input_tensor
4046+
4047+
# Reshaping functions for wq_b, wkv, o_a_proj
4048+
def reshape_transpose_wq_b(input_tensor, target_shape=None):
4049+
# HF: [n_heads * q_head_dim, kv_lora_rank]
4050+
# MaxText: [kv_lora_rank, n_heads, q_head_dim]
4051+
tensor = np.transpose(input_tensor) # [kv_lora_rank, n_heads * q_head_dim]
4052+
return tensor.reshape(target_shape)
4053+
4054+
def reshape_transpose_wkv(input_tensor, target_shape=None):
4055+
# HF: [n_kv_heads * (q_head_dim + v_head_dim), kv_lora_rank]
4056+
# MaxText: [kv_lora_rank, n_kv_heads, q_head_dim + v_head_dim]
4057+
tensor = np.transpose(input_tensor)
4058+
return tensor.reshape(target_shape)
4059+
4060+
def reshape_transpose_o_a(input_tensor, target_shape=None):
4061+
# HF: [n_heads * v_head_dim, kv_lora_rank] (e.g. [8192, 4096])
4062+
# MaxText: [n_heads, v_head_dim, kv_lora_rank] (e.g. [8, 4096, 1024])
4063+
# We must reshape first and then permute (transpose) to get correct ordering.
4064+
num_heads = target_shape[0]
4065+
embed_dim = target_shape[1]
4066+
kv_lora_rank = target_shape[2]
4067+
tensor = input_tensor.reshape((num_heads, kv_lora_rank, embed_dim))
4068+
return np.transpose(tensor, (0, 2, 1))
4069+
4070+
# Functions for mHC split
4071+
def mhc_split_fn_pre(input_tensor, target_shape=None):
4072+
return np.transpose(input_tensor[0:4, :])
4073+
4074+
def mhc_split_fn_post(input_tensor, target_shape=None):
4075+
return np.transpose(input_tensor[4:8, :])
4076+
4077+
def mhc_split_fn_res(input_tensor, target_shape=None):
4078+
return np.transpose(input_tensor[8:24, :])
4079+
4080+
def mhc_split_base_pre(input_tensor, target_shape=None):
4081+
return input_tensor[0:4]
4082+
4083+
def mhc_split_base_post(input_tensor, target_shape=None):
4084+
return input_tensor[4:8]
4085+
4086+
def mhc_split_base_res(input_tensor, target_shape=None):
4087+
return input_tensor[8:24].reshape(target_shape)
4088+
4089+
def mhc_split_scale_pre(input_tensor, target_shape=None):
4090+
return np.array([input_tensor[0]]).reshape(target_shape)
4091+
4092+
def mhc_split_scale_post(input_tensor, target_shape=None):
4093+
return np.array([input_tensor[1]]).reshape(target_shape)
4094+
4095+
def mhc_split_scale_res(input_tensor, target_shape=None):
4096+
return np.array([input_tensor[2]]).reshape(target_shape)
4097+
4098+
mapping = {}
4099+
4100+
# Base mapping logic from original file
4101+
for key, hf_key in DEEPSEEKV4_MAXTEXT_TO_HF_PARAM_MAPPING(config, maxtext_config, scan_layers).items():
4102+
if hf_key is None:
4103+
mapping[key] = ones_norm
4104+
elif "token_embedder-embedding" in key:
4105+
mapping[key] = identity
4106+
elif "-wkv-kernel" in key:
4107+
mapping[key] = reshape_transpose_wkv
4108+
elif "-wq_b-kernel" in key:
4109+
mapping[key] = reshape_transpose_wq_b
4110+
elif "-o_a_proj-kernel" in key:
4111+
mapping[key] = reshape_transpose_o_a
4112+
elif "mhc" in key:
4113+
if "pre_alpha" in key and "scale" not in key:
4114+
mapping[key] = mhc_split_fn_pre
4115+
elif "post_alpha" in key and "scale" not in key:
4116+
mapping[key] = mhc_split_fn_post
4117+
elif "res_alpha" in key and "scale" not in key:
4118+
mapping[key] = mhc_split_fn_res
4119+
elif "pre_beta" in key:
4120+
mapping[key] = mhc_split_base_pre
4121+
elif "post_beta" in key:
4122+
mapping[key] = mhc_split_base_post
4123+
elif "res_beta" in key:
4124+
mapping[key] = mhc_split_base_res
4125+
elif "pre_alpha_scale" in key:
4126+
mapping[key] = mhc_split_scale_pre
4127+
elif "post_alpha_scale" in key:
4128+
mapping[key] = mhc_split_scale_post
4129+
elif "res_alpha_scale" in key:
4130+
mapping[key] = mhc_split_scale_res
4131+
elif "position_bias" in key:
4132+
mapping[key] = identity
4133+
elif "hc_head-hc_fn" in key:
4134+
mapping[key] = transpose
4135+
elif "hc_head-hc_base" in key or "hc_head-hc_scale" in key:
4136+
mapping[key] = identity
4137+
elif isinstance(hf_key, list):
4138+
mapping[key] = transpose if not saving_to_hf else transpose_stack
4139+
elif "-kernel" in key or "-embedding" in key or "-sinks" in key:
4140+
mapping[key] = transpose
4141+
4142+
return mapping
4143+
4144+
38734145
PARAM_MAPPING = {
38744146
"gemma2-2b": GEMMA2_MAXTEXT_TO_HF_PARAM_MAPPING,
38754147
"gemma2-9b": GEMMA2_MAXTEXT_TO_HF_PARAM_MAPPING,
@@ -3908,6 +4180,7 @@ def reshape_vision_attn_out(input_tensor, target_shape):
39084180
"deepseek2-16b": DEEPSEEK_MAXTEXT_TO_HF_PARAM_MAPPING,
39094181
"deepseek3-671b": DEEPSEEK_MAXTEXT_TO_HF_PARAM_MAPPING,
39104182
"deepseek3.2-671b": DEEPSEEK_MAXTEXT_TO_HF_PARAM_MAPPING,
4183+
"deepseek4-284b": DEEPSEEKV4_MAXTEXT_TO_HF_PARAM_MAPPING,
39114184
"gpt-oss-20b": GPT_OSS_MAXTEXT_TO_HF_PARAM_MAPPING,
39124185
"gpt-oss-120b": GPT_OSS_MAXTEXT_TO_HF_PARAM_MAPPING,
39134186
"qwen3-omni-30b-a3b": QWEN3_OMNI_MOE_MAXTEXT_TO_HF_PARAM_MAPPING,
@@ -3960,6 +4233,8 @@ def reshape_vision_attn_out(input_tensor, target_shape):
39604233
"deepseek2-16b": DEEPSEEK_MAXTEXT_TO_HF_PARAM_HOOK_FN,
39614234
"deepseek3-671b": DEEPSEEK_MAXTEXT_TO_HF_PARAM_HOOK_FN,
39624235
"deepseek3.2-671b": DEEPSEEK_MAXTEXT_TO_HF_PARAM_HOOK_FN,
4236+
"deepseek4-tiny": DEEPSEEKV4_MAXTEXT_TO_HF_PARAM_HOOK_FN,
4237+
"deepseek4-284b": DEEPSEEKV4_MAXTEXT_TO_HF_PARAM_HOOK_FN,
39634238
"gpt-oss-20b": GPT_OSS_TO_HF_PARAM_HOOK_FN,
39644239
"gpt-oss-120b": GPT_OSS_TO_HF_PARAM_HOOK_FN,
39654240
"qwen3-omni-30b-a3b": QWEN3_OMNI_MOE_MAXTEXT_TO_HF_PARAM_HOOK_FN,

src/maxtext/configs/models/deepseek4-284b.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ num_experts_per_tok: 6
4949
mlp_activations_limit: 10
5050
shared_experts: 1
5151
routed_score_func: "sqrtsoftplus"
52+
norm_topk_prob: true
53+
routed_bias: true
54+
routed_scaling_factor: 1.5
55+
5256

5357
# --- Attention configuration ---
5458
attention_type: 'compressed'
@@ -62,3 +66,4 @@ rope_type: "default"
6266
rope_max_timescale: 10000 # Main RoPE theta
6367
compressed_rope_max_timescale: 160000 # Compressed RoPE theta
6468
max_position_embeddings: 1048576
69+
original_max_position_embeddings: 65536
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright 2023–2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# Test Model config for DeepSeek-V4-Flash 284B (https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash)
15+
16+
base_emb_dim: 4096
17+
base_num_query_heads: 64
18+
base_num_kv_heads: 1
19+
base_num_decoder_layers: 7
20+
base_mlp_dim: 2048
21+
base_moe_mlp_dim: 2048
22+
vocab_size: 129280
23+
head_dim: 512
24+
25+
# --- Standard Defaults ---
26+
enable_dropout: false
27+
logits_via_embedding: false
28+
normalization_layer_epsilon: 1.0e-6
29+
30+
# --- V4 Specific Architectural Keys ---
31+
decoder_block: "deepseek4"
32+
mhc_expansion_rate: 4
33+
first_num_hash_layers: 3
34+
indexer_head_dim: 128
35+
indexer_n_heads: 64
36+
indexer_topk: 512
37+
38+
# Note: Layers (0, 1, 2) are prefix layers as `first_num_hash_layers=3`.
39+
# The 6th layer (MTP module with compress_ratio=0) has been explicitly dropped for now.
40+
# This leaves exactly 7 layers: 3 prefix [0,0,4] + 4 scanned.
41+
# `compress_ratio=0` uses sliding window attention. In this case, layer (0, 1).
42+
# This is a tiny version of deepseek4 with fewer layers and less experts for debugging.
43+
compress_ratios: [0, 0, 4, 128, 4, 128, 4]
44+
45+
# --- MoE configuration ---
46+
mlp_activations: ["silu", "linear"]
47+
num_experts: 8
48+
num_experts_per_tok: 3
49+
mlp_activations_limit: 10
50+
shared_experts: 1
51+
routed_score_func: "sqrtsoftplus"
52+
routed_bias: true
53+
routed_scaling_factor: 1.5
54+
55+
56+
# --- Attention configuration ---
57+
attention_type: 'compressed'
58+
attention: 'dot_product'
59+
q_lora_rank: 1024
60+
o_groups: 8
61+
o_lora_rank: 1024
62+
sliding_window_size: 128
63+
64+
# --- RoPE ---
65+
rope_type: "default"
66+
rope_max_timescale: 10000 # Main RoPE theta
67+
compressed_rope_max_timescale: 160000 # Compressed RoPE theta
68+
max_position_embeddings: 1048576
69+
original_max_position_embeddings: 65536
70+

src/maxtext/configs/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ class ProfilerType(str, Enum):
228228
"deepseek3-test",
229229
"deepseek3-tiny",
230230
"deepseek3.2-671b",
231+
"deepseek4-tiny",
231232
"deepseek4-284b",
232233
"deepseek-custom",
233234
"kimi-k2-1t",

src/maxtext/layers/attention_compressed.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def __call__(
322322

323323
# Skip causal mask generation during decoding (seq_len == 1) or if no blocks were pooled
324324
if seq_len == 1 or compressed_len == 0:
325-
return compressed_kv, None
325+
return compressed_kv, jnp.zeros((batch_size, 1, seq_len, compressed_len), dtype=self.dtype)
326326

327327
# Construct a causal mask preventing early queries from attending to future compressed blocks
328328
entry_indices = jnp.arange(compressed_len)

0 commit comments

Comments
 (0)