1use anyhow::{Ok, Result};
2use candle_core::{D, Device, Tensor};
3use candle_nn::{Embedding, Linear, Module, RmsNorm, VarBuilder, embedding, rms_norm};
4
5use crate::{
6 models::{
7 common::{
8 InferenceModel,
9 modules::{GateUpDownMLP, NaiveAttention},
10 },
11 minicpm4::config::MiniCPM4Config,
12 },
13 position_embed::rope::compute_default_rope_parameters,
14 utils::tensor_utils::prepare_causal_attention_mask,
15};
16
17pub struct MiniCPMLongRoPE {
18 short_factor: Vec<f32>,
19 long_factor: Vec<f32>,
20 original_max_position_embeddings: usize,
21 max_seq_len_cached: usize,
22 scaling_factor: f64,
23 inv_freq: Tensor,
24 cos_cached: Tensor,
25 sin_cached: Tensor,
26 device: Device,
27}
28impl MiniCPMLongRoPE {
29 pub fn new(cfg: &MiniCPM4Config, device: &Device) -> Result<Self> {
30 let head_dim = cfg.hidden_size / cfg.num_attention_heads;
31 let rope_theta = 10000.0;
32 let short_factor = cfg.rope_scaling.short_factor.clone();
33 let long_factor = cfg.rope_scaling.short_factor.clone();
34 let original_max_position_embeddings = cfg.rope_scaling.original_max_position_embeddings;
35 let max_position_embeddings = cfg.max_position_embeddings;
36 let scale = max_position_embeddings as f64 / original_max_position_embeddings as f64;
37 let scaling_factor =
38 (1.0 + scale.ln() / (original_max_position_embeddings as f64).ln()).sqrt();
39 let inv_freq = compute_default_rope_parameters(head_dim, rope_theta);
40 let inv_freq = Tensor::from_slice(&inv_freq, (1, inv_freq.len()), device)?;
41 let max_seq_len_cached = max_position_embeddings;
42 let t = Tensor::arange(0.0_f32, max_position_embeddings as f32, device)?
43 .reshape((max_position_embeddings, 1))?;
44 let ext_factors = Tensor::from_slice(&short_factor, (1, short_factor.len()), device)?;
47 let ext_factors = Tensor::ones_like(&ext_factors)?.div(&ext_factors)?;
48 let freqs = t.matmul(&ext_factors)?.broadcast_mul(&inv_freq)?;
50
51 let emb = Tensor::cat(&[&freqs, &freqs], D::Minus1)?;
52 let cos_cached = emb.cos()?.affine(scaling_factor, 0.0)?;
53 let sin_cached = emb.sin()?.affine(scaling_factor, 0.0)?;
54 Ok(Self {
55 short_factor,
56 long_factor,
57 original_max_position_embeddings,
58 max_seq_len_cached,
59 scaling_factor,
60 inv_freq,
61 cos_cached,
62 sin_cached,
63 device: device.clone(),
64 })
65 }
66 pub fn update_cos_sin_cache(&mut self, seqlen: usize) -> Result<()> {
67 self.max_seq_len_cached = seqlen;
68 let t = Tensor::arange(0.0_f32, seqlen as f32, &self.device)?.reshape((seqlen, 1))?;
69 let mut ext_factors = Tensor::from_slice(
70 &self.short_factor,
71 (1, self.short_factor.len()),
72 &self.device,
73 )?;
74 if seqlen > self.original_max_position_embeddings {
75 ext_factors =
76 Tensor::from_slice(&self.long_factor, (1, self.long_factor.len()), &self.device)?;
77 }
78 let ext_factors = Tensor::ones_like(&ext_factors)?.div(&ext_factors)?;
79 let freqs = t.matmul(&ext_factors)?.broadcast_mul(&self.inv_freq)?;
80 let emb = Tensor::cat(&[&freqs, &freqs], D::Minus1)?;
81 let cos_cached = emb.cos()?.affine(self.scaling_factor, 0.0)?;
82 let sin_cached = emb.sin()?.affine(self.scaling_factor, 0.0)?;
83 self.cos_cached = cos_cached;
84 self.sin_cached = sin_cached;
85 Ok(())
86 }
87 pub fn forward(&mut self, pos_offset: usize, seqlen: usize) -> Result<(Tensor, Tensor)> {
88 if pos_offset + seqlen > self.max_seq_len_cached {
89 self.update_cos_sin_cache(pos_offset + seqlen)?;
90 }
91 let cos = self.cos_cached.narrow(0, pos_offset, seqlen)?;
92 let sin = self.sin_cached.narrow(0, pos_offset, seqlen)?;
93
94 Ok((cos, sin))
95 }
96}
97
98pub struct MiniCPMDecoderLayer {
99 self_attn: NaiveAttention,
100 mlp: GateUpDownMLP,
101 input_layernorm: RmsNorm,
102 post_attention_layernorm: RmsNorm,
103 scale_depth: f32,
104 num_hidden_layers: usize,
105}
106
107impl MiniCPMDecoderLayer {
108 pub fn new(vb: VarBuilder, cfg: &MiniCPM4Config) -> Result<Self> {
109 let self_attn = NaiveAttention::new(
110 vb.pp("self_attn"),
111 cfg.hidden_size,
112 cfg.num_attention_heads,
113 cfg.num_key_value_heads,
114 None,
115 false,
116 None,
117 None,
118 None,
119 None,
120 )?;
121 let mlp = GateUpDownMLP::new(
122 vb.pp("mlp"),
123 cfg.hidden_size,
124 cfg.intermediate_size,
125 cfg.hidden_act,
126 false,
127 None,
128 None,
129 None,
130 )?;
131 let input_layernorm =
132 rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?;
133 let post_attention_layernorm = rms_norm(
134 cfg.hidden_size,
135 cfg.rms_norm_eps,
136 vb.pp("post_attention_layernorm"),
137 )?;
138 Ok(Self {
139 self_attn,
140 mlp,
141 input_layernorm,
142 post_attention_layernorm,
143 scale_depth: cfg.scale_depth,
144 num_hidden_layers: cfg.num_hidden_layers,
145 })
146 }
147
148 pub fn forward(
149 &self,
150 xs: &Tensor,
151 cos: &Tensor,
152 sin: &Tensor,
153 attention_mask: Option<&Tensor>,
154 ) -> Result<Tensor> {
155 let residual = xs.clone();
156 let xs = self.input_layernorm.forward(xs)?;
157 let xs = self
158 .self_attn
159 .forward(&xs, Some(cos), Some(sin), attention_mask, true)?;
160 let xs = (residual
161 + xs.affine(
162 self.scale_depth as f64 / (self.num_hidden_layers as f64).sqrt(),
163 0.0,
164 ))?;
165 let residual = &xs;
166 let xs = xs.apply(&self.post_attention_layernorm)?.apply(&self.mlp)?;
167 let xs = (residual
168 + xs.affine(
169 self.scale_depth as f64 / (self.num_hidden_layers as f64).sqrt(),
170 0.0,
171 )?)?;
172 Ok(xs)
173 }
174
175 pub fn forward_with_cache(
176 &mut self,
177 xs: &Tensor,
178 cos: &Tensor,
179 sin: &Tensor,
180 attention_mask: Option<&Tensor>,
181 ) -> Result<Tensor> {
182 let residual = xs.clone();
183 let xs = self.input_layernorm.forward(xs)?;
184 let xs =
185 self.self_attn
186 .forward_with_cache(&xs, Some(cos), Some(sin), attention_mask, true)?;
187 let xs = (residual
188 + xs.affine(
189 self.scale_depth as f64 / (self.num_hidden_layers as f64).sqrt(),
190 0.0,
191 ))?;
192 let residual = &xs;
193 let xs = xs.apply(&self.post_attention_layernorm)?.apply(&self.mlp)?;
194 let xs = (residual
195 + xs.affine(
196 self.scale_depth as f64 / (self.num_hidden_layers as f64).sqrt(),
197 0.0,
198 )?)?;
199 Ok(xs)
200 }
201 pub fn clear_kv_cache(&mut self) {
202 self.self_attn.clear_kv_cache();
203 }
204}
205
206pub struct MiniCPMModel {
207 cfg: MiniCPM4Config,
208 embed_tokens: Embedding,
209 layers: Vec<MiniCPMDecoderLayer>,
210 norm: RmsNorm,
211 rope_emb: MiniCPMLongRoPE,
212 lm_head: Linear,
213 stop_token_ids: Vec<u32>,
214}
215
216impl MiniCPMModel {
217 pub fn new(vb: VarBuilder, cfg: MiniCPM4Config) -> Result<Self> {
218 let vb = vb.pp("model");
219 let embed_tokens = embedding(cfg.vocab_size, cfg.hidden_size, vb.pp("embed_tokens"))?;
220 let mut layers = Vec::with_capacity(cfg.num_hidden_layers);
221 let vb_layers = vb.pp("layers");
222 for i in 0..cfg.num_hidden_layers {
223 let layer = MiniCPMDecoderLayer::new(vb_layers.pp(i), &cfg)?;
224 layers.push(layer);
225 }
226 let norm = rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("norm"))?;
227 let rope_emb = MiniCPMLongRoPE::new(&cfg, vb.device())?;
228 let lm_head = Linear::new(embed_tokens.embeddings().clone(), None);
229 let stop_token_ids = cfg.eos_token_id.clone();
230 Ok(Self {
231 cfg,
232 embed_tokens,
233 layers,
234 norm,
235 rope_emb,
236 lm_head,
237 stop_token_ids,
238 })
239 }
240
241 pub fn forward(&mut self, input_ids: &Tensor, seqlen_offset: usize) -> Result<Tensor> {
242 let (bs, seq_len) = input_ids.dims2()?;
243 let input_embeds = self
244 .embed_tokens
245 .forward(input_ids)?
246 .affine(self.cfg.scale_emb, 0.0)?;
247 let attention_mask: Option<Tensor> = {
248 if seq_len <= 1 {
249 None
250 } else {
251 Some(prepare_causal_attention_mask(
252 bs,
253 seq_len,
254 0,
255 input_ids.device(),
256 )?)
257 }
258 };
259
260 let (cos, sin) = self.rope_emb.forward(seqlen_offset, seq_len)?;
261 let mut hidden_states = input_embeds;
262 for decode_layer in &self.layers {
263 hidden_states =
264 decode_layer.forward(&hidden_states, &cos, &sin, attention_mask.as_ref())?;
265 }
266 hidden_states = self.norm.forward(&hidden_states)?;
267 let hidden_state = hidden_states.narrow(1, seq_len - 1, 1)?;
268 let hidden_state = hidden_state.affine(
269 1.0 / (self.cfg.hidden_size / self.cfg.dim_model_base) as f64,
270 0.0,
271 )?;
272 let logits = self.lm_head.forward(&hidden_state)?;
273 Ok(logits)
274 }
275
276 pub fn forward_with_cache(
277 &mut self,
278 input_ids: &Tensor,
279 seqlen_offset: usize,
280 ) -> Result<Tensor> {
281 let (bs, seq_len) = input_ids.dims2()?;
282 let input_embeds = self
283 .embed_tokens
284 .forward(input_ids)?
285 .affine(self.cfg.scale_emb, 0.0)?;
286 let attention_mask: Option<Tensor> = {
287 if seq_len <= 1 {
288 None
289 } else {
290 Some(prepare_causal_attention_mask(
291 bs,
292 seq_len,
293 0,
294 input_ids.device(),
295 )?)
296 }
297 };
298 let (cos, sin) = self.rope_emb.forward(seqlen_offset, seq_len)?;
299 let mut hidden_states = input_embeds;
300 for decode_layer in &mut self.layers {
301 hidden_states = decode_layer.forward_with_cache(
302 &hidden_states,
303 &cos,
304 &sin,
305 attention_mask.as_ref(),
306 )?;
307 }
308 hidden_states = self.norm.forward(&hidden_states)?;
309 let hidden_state = hidden_states.narrow(1, seq_len - 1, 1)?;
310 let hidden_state = hidden_state.affine(
311 1.0 / (self.cfg.hidden_size / self.cfg.dim_model_base) as f64,
312 0.0,
313 )?;
314 let logits = self.lm_head.forward(&hidden_state)?;
315 Ok(logits)
316 }
317
318 pub fn clear_kv_cache(&mut self) {
319 for layer in self.layers.iter_mut() {
320 layer.clear_kv_cache()
321 }
322 }
323}
324
325impl InferenceModel for MiniCPMModel {
326 fn forward_step(&mut self, input_ids: &Tensor, seqlen_offset: usize) -> Result<Tensor> {
327 self.forward_with_cache(input_ids, seqlen_offset)
328 }
329
330 fn clear_cache(&mut self) {
331 self.clear_kv_cache();
332 }
333
334 fn stop_token_ids(&self) -> Vec<u32> {
335 self.stop_token_ids.clone()
336 }
337}