metrale_model_arch/weight_loader/deepseek_v4/
mtp.rs1use anyhow::Result;
17use metrale_cache::kv_cache::KvCacheDtype;
18use metrale_config::ModelConfig;
19use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
20use metrale_model_weights::weights::WeightStore;
21
22use metrale_model_layers::layer::TransformerLayer;
23use metrale_model_layers::layers::qwen3_attention::HcHeadWeights;
24use metrale_model_layers::weight_map::DenseWeight;
25use metrale_model_layers::weight_map::quant_helpers::dense_auto;
26
27#[allow(dead_code)]
30pub struct DeepseekV4MtpModule {
31 pub body: Box<dyn TransformerLayer>,
33 pub enorm: DenseWeight,
35 pub hnorm: DenseWeight,
37 pub e_proj: DenseWeight,
40 pub h_proj: DenseWeight,
43 pub norm: DenseWeight,
45 pub hc_head: Option<HcHeadWeights>,
51}
52
53pub fn load_v4_mtp_module(
59 store: &WeightStore,
60 config: &ModelConfig,
61 gpu: &dyn GpuBackend,
62 layer_kv_dtypes: &[KvCacheDtype],
63) -> Result<Option<DeepseekV4MtpModule>> {
64 if config.num_mtp_modules == 0 {
65 return Ok(None);
66 }
67 if !store.contains("mtp.0.enorm.weight") {
68 tracing::info!(
69 "DeepSeek-V4: num_mtp_modules={} but no mtp.0.* tensors in checkpoint — MTP disabled",
70 config.num_mtp_modules
71 );
72 return Ok(None);
73 }
74
75 let prefix = "mtp.0";
76 let ap = format!("{prefix}.attn");
77 let null = DenseWeight {
78 weight: DevicePtr::NULL,
79 };
80
81 let input_norm = dense_auto(store, &format!("{prefix}.attn_norm.weight"), gpu)?;
85 let post_attn_norm = dense_auto(store, &format!("{prefix}.ffn_norm.weight"), gpu)?;
86 let wq_a = dense_auto(store, &format!("{ap}.wq_a.weight"), gpu)?;
87 let wq_b = dense_auto(store, &format!("{ap}.wq_b.weight"), gpu)?;
88 let q_a_norm = dense_auto(store, &format!("{ap}.q_norm.weight"), gpu)?;
89 let wkv_a = dense_auto(store, &format!("{ap}.wkv.weight"), gpu)?;
90 let kv_a_norm = dense_auto(store, &format!("{ap}.kv_norm.weight"), gpu)?;
91 let wo_a = dense_auto(store, &format!("{ap}.wo_a.weight"), gpu)?;
92 let wo_b = dense_auto(store, &format!("{ap}.wo_b.weight"), gpu)?;
93
94 let hc_head = if config.hc_mult > 0 {
97 let hc = config.hc_mult;
98 let hc_dim = hc * config.hidden_size;
99 let head_fn = super::assemble::load_hc_f32(
100 store,
101 &[format!("{prefix}.hc_head_fn")],
102 hc * hc_dim,
103 gpu,
104 )?;
105 let head_base =
106 super::assemble::load_hc_f32(store, &[format!("{prefix}.hc_head_base")], hc, gpu)?;
107 let head_scale =
108 super::assemble::load_hc_f32(store, &[format!("{prefix}.hc_head_scale")], 1, gpu)?;
109 Some(HcHeadWeights {
110 hc_fn: head_fn,
111 hc_base: head_base,
112 hc_scale: head_scale,
113 lowrank: None,
115 })
116 } else {
117 None
118 };
119
120 let mut yarn_inv_freq = DevicePtr::NULL;
121 yarn_inv_freq = super::compute::ensure_yarn_inv_freq(&mut yarn_inv_freq, config, gpu)?;
122
123 let body = super::assemble::assemble_layer(
127 config.num_hidden_layers,
128 prefix,
129 true,
130 input_norm,
131 post_attn_norm,
132 wq_a,
133 None,
134 wq_b,
135 None,
136 q_a_norm,
137 wkv_a,
138 None,
139 null,
140 kv_a_norm,
141 wo_b,
142 None,
143 null,
144 null,
145 null,
146 null,
147 null,
148 null,
149 yarn_inv_freq,
150 wo_a,
151 hc_head.clone(),
152 store,
153 config,
154 gpu,
155 layer_kv_dtypes,
156 )?;
157
158 let enorm = dense_auto(store, &format!("{prefix}.enorm.weight"), gpu)?;
162 let hnorm = dense_auto(store, &format!("{prefix}.hnorm.weight"), gpu)?;
163 let norm = dense_auto(store, &format!("{prefix}.norm.weight"), gpu)?;
164 let e_proj = dense_auto(store, &format!("{prefix}.e_proj.weight"), gpu)?;
165 let h_proj = dense_auto(store, &format!("{prefix}.h_proj.weight"), gpu)?;
166
167 tracing::info!(
168 "DeepSeek-V4 MTP module loaded: reused V4 body (MLA + mHC + 256-expert MoE) \
169 + combiner (enorm/hnorm + e_proj/h_proj) + final norm"
170 );
171
172 Ok(Some(DeepseekV4MtpModule {
173 body,
174 enorm,
175 hnorm,
176 e_proj,
177 h_proj,
178 norm,
179 hc_head,
180 }))
181}