1use std::sync::Arc;
9
10use candle::{DType, Device, IndexOp, Result, Tensor, D};
11use candle_nn::{embedding, linear_b, rms_norm, Embedding, Linear, Module, RmsNorm, VarBuilder};
12
13use super::config::TextConfig;
14
15#[derive(Debug, Clone)]
24pub struct RotaryEmbedding {
25 cos: Tensor,
27 sin: Tensor,
29 mrope_section: Vec<usize>,
31 head_dim: usize,
32}
33
34impl RotaryEmbedding {
35 pub fn new(cfg: &TextConfig, device: &Device, dtype: DType) -> Result<Self> {
36 let dim = cfg.head_dim;
37 let max_seq_len = cfg.max_position_embeddings;
38
39 let inv_freq: Vec<f32> = (0..dim)
41 .step_by(2)
42 .map(|i| 1f32 / (cfg.rope_theta as f32).powf(i as f32 / dim as f32))
43 .collect();
44 let inv_freq_len = inv_freq.len();
45 let inv_freq = Tensor::from_vec(inv_freq, (1, inv_freq_len), device)?;
46
47 let t = Tensor::arange(0u32, max_seq_len as u32, device)?
49 .to_dtype(DType::F32)?
50 .reshape((max_seq_len, 1))?;
51 let freqs = t.matmul(&inv_freq)?;
52 let sin = freqs.sin()?.to_dtype(dtype)?;
53 let cos = freqs.cos()?.to_dtype(dtype)?;
54
55 Ok(Self {
56 cos,
57 sin,
58 mrope_section: cfg.mrope_section.clone(),
59 head_dim: dim,
60 })
61 }
62
63 pub fn apply_multimodal_rotary_emb(
75 &self,
76 q: &Tensor,
77 k: &Tensor,
78 position_ids: &Tensor,
79 ) -> Result<(Tensor, Tensor)> {
80 let (three, _batch, _seq_len) = position_ids.dims3()?;
82 assert_eq!(three, 3, "position_ids must have 3 dimensions");
83
84 let (cos_3d, sin_3d) = self.compute_3d_rope_embeddings(position_ids)?;
87 let (cos, sin) = self.apply_mrope_sections(&cos_3d, &sin_3d)?;
93 let cos = cos.unsqueeze(1)?;
97 let sin = sin.unsqueeze(1)?;
98
99 let q_embed = self.apply_rope_to_tensor(q, &cos, &sin)?;
101 let k_embed = self.apply_rope_to_tensor(k, &cos, &sin)?;
102
103 Ok((q_embed, k_embed))
104 }
105
106 fn compute_3d_rope_embeddings(&self, position_ids: &Tensor) -> Result<(Tensor, Tensor)> {
110 let (three, batch, seq_len) = position_ids.dims3()?;
111 let half_dim = self.head_dim / 2;
112
113 let mut cos_parts = Vec::new();
115 let mut sin_parts = Vec::new();
116
117 for dim_idx in 0..three {
118 let pos = position_ids.i(dim_idx)?; let pos_flat = pos.flatten_all()?; let cos_gathered = self.cos.index_select(&pos_flat, 0)?; let sin_gathered = self.sin.index_select(&pos_flat, 0)?;
124
125 let cos_dim = cos_gathered.reshape((batch, seq_len, half_dim))?;
127 let sin_dim = sin_gathered.reshape((batch, seq_len, half_dim))?;
128
129 let cos_full = Tensor::cat(&[&cos_dim, &cos_dim], D::Minus1)?;
131 let sin_full = Tensor::cat(&[&sin_dim, &sin_dim], D::Minus1)?;
132
133 cos_parts.push(cos_full);
134 sin_parts.push(sin_full);
135 }
136
137 let cos_3d = Tensor::stack(&cos_parts, 0)?;
139 let sin_3d = Tensor::stack(&sin_parts, 0)?;
140
141 Ok((cos_3d, sin_3d))
142 }
143
144 fn apply_mrope_sections(&self, cos_3d: &Tensor, sin_3d: &Tensor) -> Result<(Tensor, Tensor)> {
159 let mut sections_repeated: Vec<usize> = Vec::new();
165 sections_repeated.extend_from_slice(&self.mrope_section);
166 sections_repeated.extend_from_slice(&self.mrope_section);
167 let mut cos_parts = Vec::new();
171 let mut sin_parts = Vec::new();
172 let mut offset = 0;
173
174 for (i, &sec_size) in sections_repeated.iter().enumerate() {
175 let dim_idx = i % 3; let cos_slice = cos_3d.i(dim_idx)?.narrow(D::Minus1, offset, sec_size)?;
178 let sin_slice = sin_3d.i(dim_idx)?.narrow(D::Minus1, offset, sec_size)?;
179 cos_parts.push(cos_slice);
180 sin_parts.push(sin_slice);
181 offset += sec_size;
182 }
183
184 let cos = Tensor::cat(&cos_parts, D::Minus1)?;
186 let sin = Tensor::cat(&sin_parts, D::Minus1)?;
187
188 Ok((cos, sin))
189 }
190
191 fn apply_rope_to_tensor(&self, x: &Tensor, cos: &Tensor, sin: &Tensor) -> Result<Tensor> {
195 let x = x.contiguous()?;
196
197 let head_dim = x.dim(D::Minus1)?;
199 let half_dim = head_dim / 2;
200
201 let x1 = x.narrow(D::Minus1, 0, half_dim)?;
202 let x2 = x.narrow(D::Minus1, half_dim, half_dim)?;
203
204 let x_rotated = Tensor::cat(&[&x2.neg()?, &x1], D::Minus1)?;
206
207 x.broadcast_mul(cos)? + x_rotated.broadcast_mul(sin)?
209 }
210
211 pub fn apply_multimodal_rotary_emb_with_export(
213 &self,
214 q: &Tensor,
215 k: &Tensor,
216 position_ids: &Tensor,
217 ) -> Result<(Tensor, Tensor, std::collections::HashMap<String, Tensor>)> {
218 use std::collections::HashMap;
219 let mut tensors: HashMap<String, Tensor> = HashMap::new();
220
221 let (three, _batch, _seq_len) = position_ids.dims3()?;
222 assert_eq!(three, 3, "position_ids must have 3 dimensions");
223
224 tensors.insert("position_ids".to_string(), position_ids.clone());
226
227 let (cos_3d, sin_3d) = self.compute_3d_rope_embeddings(position_ids)?;
229 tensors.insert("cos_3d".to_string(), cos_3d.clone());
230 tensors.insert("sin_3d".to_string(), sin_3d.clone());
231
232 let (cos, sin) = self.apply_mrope_sections(&cos_3d, &sin_3d)?;
234 tensors.insert("cos_after_mrope".to_string(), cos.clone());
235 tensors.insert("sin_after_mrope".to_string(), sin.clone());
236
237 let seq_len = cos.dim(1)?;
239 if seq_len > 947 {
240 tensors.insert("cos_pos947".to_string(), cos.i((.., 947, ..))?.squeeze(1)?);
241 tensors.insert("sin_pos947".to_string(), sin.i((.., 947, ..))?.squeeze(1)?);
242 }
243
244 let cos = cos.unsqueeze(1)?;
246 let sin = sin.unsqueeze(1)?;
247
248 let q_embed = self.apply_rope_to_tensor(q, &cos, &sin)?;
250 let k_embed = self.apply_rope_to_tensor(k, &cos, &sin)?;
251
252 Ok((q_embed, k_embed, tensors))
253 }
254}
255
256#[derive(Debug, Clone)]
258pub struct ImageGrid {
259 pub grid_h: usize,
261 pub grid_w: usize,
263}
264
265pub fn compute_mrope_position_ids_multi(
289 input_ids: &Tensor,
290 image_token_id: u32,
291 image_grids: &[ImageGrid],
292 device: &Device,
293) -> Result<Tensor> {
294 let (batch, seq_len) = input_ids.dims2()?;
295 let input_ids_vec: Vec<u32> = input_ids.flatten_all()?.to_vec1()?;
296
297 let mut pos_t = vec![0i64; batch * seq_len];
299 let mut pos_h = vec![0i64; batch * seq_len];
300 let mut pos_w = vec![0i64; batch * seq_len];
301
302 for b in 0..batch {
303 let batch_start = b * seq_len;
304
305 let mut image_ranges: Vec<(usize, usize)> = Vec::new(); let mut in_image = false;
308 let mut image_start = 0usize;
309
310 for s in 0..seq_len {
311 let token_id = input_ids_vec[batch_start + s];
312 if token_id == image_token_id {
313 if !in_image {
314 in_image = true;
315 image_start = s;
316 }
317 } else if in_image {
318 image_ranges.push((image_start, s));
319 in_image = false;
320 }
321 }
322 if in_image {
324 image_ranges.push((image_start, seq_len));
325 }
326
327 if image_ranges.len() != image_grids.len() {
329 return Err(candle::Error::Msg(format!(
330 "Mismatch: found {} image ranges but {} grids provided",
331 image_ranges.len(),
332 image_grids.len()
333 )));
334 }
335
336 let mut current_pos = 0i64;
338 let mut range_idx = 0usize;
339
340 for s in 0..seq_len {
341 let idx = batch_start + s;
342
343 if range_idx < image_ranges.len() && s == image_ranges[range_idx].0 {
345 let (img_start, img_end) = image_ranges[range_idx];
347 let grid = &image_grids[range_idx];
348 let num_vision_tokens = grid.grid_h * grid.grid_w;
349
350 let actual_tokens = img_end - img_start;
352 if actual_tokens != num_vision_tokens {
353 return Err(candle::Error::Msg(format!(
354 "Image {} has {} tokens but grid {}x{} = {} expected",
355 range_idx, actual_tokens, grid.grid_h, grid.grid_w, num_vision_tokens
356 )));
357 }
358
359 let offset = current_pos;
361 for vision_idx in 0..num_vision_tokens {
362 let token_s = img_start + vision_idx;
363 let token_idx = batch_start + token_s;
364
365 let t_pos = 0i64; let h_pos = (vision_idx / grid.grid_w) as i64;
367 let w_pos = (vision_idx % grid.grid_w) as i64;
368
369 pos_t[token_idx] = t_pos + offset;
370 pos_h[token_idx] = h_pos + offset;
371 pos_w[token_idx] = w_pos + offset;
372 }
373
374 let max_h = (grid.grid_h - 1) as i64;
376 let max_w = (grid.grid_w - 1) as i64;
377 current_pos = offset + max_h.max(max_w) + 1;
378
379 range_idx += 1;
380 continue;
381 }
382
383 if range_idx > 0 {
385 let prev_range = image_ranges[range_idx - 1];
386 if s >= prev_range.0 && s < prev_range.1 {
387 continue;
388 }
389 }
390 if range_idx < image_ranges.len() {
391 let curr_range = image_ranges[range_idx];
392 if s >= curr_range.0 && s < curr_range.1 {
393 continue;
394 }
395 }
396
397 pos_t[idx] = current_pos;
399 pos_h[idx] = current_pos;
400 pos_w[idx] = current_pos;
401 current_pos += 1;
402 }
403 }
404
405 let pos_t = Tensor::from_vec(pos_t, (batch, seq_len), device)?;
407 let pos_h = Tensor::from_vec(pos_h, (batch, seq_len), device)?;
408 let pos_w = Tensor::from_vec(pos_w, (batch, seq_len), device)?;
409
410 Tensor::stack(&[pos_t, pos_h, pos_w], 0)
411}
412
413pub fn compute_mrope_position_ids(
423 input_ids: &Tensor,
424 image_token_id: u32,
425 grid_h: usize,
426 grid_w: usize,
427 device: &Device,
428) -> Result<Tensor> {
429 let (batch, seq_len) = input_ids.dims2()?;
430 let input_ids_vec: Vec<u32> = input_ids.flatten_all()?.to_vec1()?;
431
432 let mut pos_t = vec![0i64; batch * seq_len];
434 let mut pos_h = vec![0i64; batch * seq_len];
435 let mut pos_w = vec![0i64; batch * seq_len];
436
437 for b in 0..batch {
438 let batch_start = b * seq_len;
440 let mut first_image_pos = None;
441 for s in 0..seq_len {
442 if input_ids_vec[batch_start + s] == image_token_id {
443 first_image_pos = Some(s);
444 break;
445 }
446 }
447
448 let num_vision_tokens = grid_h * grid_w;
450
451 let text_before = first_image_pos.unwrap_or(seq_len);
453 for s in 0..text_before {
454 let idx = batch_start + s;
455 pos_t[idx] = s as i64;
456 pos_h[idx] = s as i64;
457 pos_w[idx] = s as i64;
458 }
459
460 let offset = text_before as i64;
462 let mut vision_idx = 0usize;
463 let mut max_vision_pos = offset - 1; for s in text_before..seq_len {
466 let idx = batch_start + s;
467 let token_id = input_ids_vec[idx];
468
469 if token_id == image_token_id && vision_idx < num_vision_tokens {
470 let t_pos = 0i64; let h_pos = (vision_idx / grid_w) as i64;
473 let w_pos = (vision_idx % grid_w) as i64;
474
475 pos_t[idx] = t_pos + offset;
476 pos_h[idx] = h_pos + offset;
477 pos_w[idx] = w_pos + offset;
478
479 max_vision_pos = max_vision_pos
481 .max(pos_t[idx])
482 .max(pos_h[idx])
483 .max(pos_w[idx]);
484
485 vision_idx += 1;
486 } else {
487 max_vision_pos += 1;
489 pos_t[idx] = max_vision_pos;
490 pos_h[idx] = max_vision_pos;
491 pos_w[idx] = max_vision_pos;
492 }
493 }
494 }
495
496 let pos_t = Tensor::from_vec(pos_t, (batch, seq_len), device)?;
498 let pos_h = Tensor::from_vec(pos_h, (batch, seq_len), device)?;
499 let pos_w = Tensor::from_vec(pos_w, (batch, seq_len), device)?;
500
501 Tensor::stack(&[pos_t, pos_h, pos_w], 0)
502}
503
504#[derive(Debug, Clone)]
509pub struct VideoGrid {
510 pub grid_t: usize,
512 pub grid_h: usize,
514 pub grid_w: usize,
516}
517
518pub fn compute_mrope_position_ids_video(
542 input_ids: &Tensor,
543 video_token_id: u32,
544 video_grid: &VideoGrid,
545 second_per_grid_t: f32,
546 tokens_per_second: usize,
547 device: &Device,
548) -> Result<Tensor> {
549 let (batch, seq_len) = input_ids.dims2()?;
550 let input_ids_vec: Vec<u32> = input_ids.flatten_all()?.to_vec1()?;
551
552 let grid_t = video_grid.grid_t;
553 let grid_h = video_grid.grid_h;
554 let grid_w = video_grid.grid_w;
555 let num_vision_tokens = grid_t * grid_h * grid_w;
556
557 let mut pos_t = vec![0i64; batch * seq_len];
559 let mut pos_h = vec![0i64; batch * seq_len];
560 let mut pos_w = vec![0i64; batch * seq_len];
561
562 for b in 0..batch {
563 let batch_start = b * seq_len;
564
565 let mut video_start = None;
567 let mut video_end = None;
568 let mut in_video = false;
569
570 for s in 0..seq_len {
571 let token_id = input_ids_vec[batch_start + s];
572 if token_id == video_token_id {
573 if !in_video {
574 in_video = true;
575 video_start = Some(s);
576 }
577 } else if in_video {
578 video_end = Some(s);
579 break;
580 }
581 }
582 if in_video && video_end.is_none() {
584 video_end = Some(seq_len);
585 }
586
587 if let (Some(start), Some(end)) = (video_start, video_end) {
589 let actual_tokens = end - start;
590 if actual_tokens != num_vision_tokens {
591 return Err(candle::Error::Msg(format!(
592 "Video has {} tokens but grid {}x{}x{} = {} expected",
593 actual_tokens, grid_t, grid_h, grid_w, num_vision_tokens
594 )));
595 }
596 }
597
598 let mut current_pos = 0i64;
600 let video_range = video_start.zip(video_end);
601
602 for s in 0..seq_len {
603 let idx = batch_start + s;
604
605 if let Some((v_start, v_end)) = video_range {
607 if s == v_start {
608 let offset = current_pos;
610
611 for vision_idx in 0..num_vision_tokens {
612 let token_s = v_start + vision_idx;
613 let token_idx = batch_start + token_s;
614
615 let frame_index = vision_idx / (grid_h * grid_w);
619 let t_pos = (frame_index as f32
620 * second_per_grid_t
621 * tokens_per_second as f32) as i64;
622 let spatial_idx = vision_idx % (grid_h * grid_w);
623 let h_pos = (spatial_idx / grid_w) as i64;
624 let w_pos = (spatial_idx % grid_w) as i64;
625
626 pos_t[token_idx] = t_pos + offset;
627 pos_h[token_idx] = h_pos + offset;
628 pos_w[token_idx] = w_pos + offset;
629 }
630
631 let max_t =
634 ((grid_t - 1) as f32 * second_per_grid_t * tokens_per_second as f32) as i64;
635 let max_h = (grid_h - 1) as i64;
636 let max_w = (grid_w - 1) as i64;
637 current_pos = offset + max_t.max(max_h).max(max_w) + 1;
638
639 continue;
640 }
641
642 if s > v_start && s < v_end {
644 continue;
645 }
646 }
647
648 pos_t[idx] = current_pos;
650 pos_h[idx] = current_pos;
651 pos_w[idx] = current_pos;
652 current_pos += 1;
653 }
654 }
655
656 let pos_t = Tensor::from_vec(pos_t, (batch, seq_len), device)?;
658 let pos_h = Tensor::from_vec(pos_h, (batch, seq_len), device)?;
659 let pos_w = Tensor::from_vec(pos_w, (batch, seq_len), device)?;
660
661 Tensor::stack(&[pos_t, pos_h, pos_w], 0)
662}
663
664struct Mlp {
666 gate_proj: Linear,
667 up_proj: Linear,
668 down_proj: Linear,
669 act_fn: candle_nn::Activation,
670}
671
672impl Mlp {
673 fn new(cfg: &TextConfig, vb: VarBuilder) -> Result<Self> {
674 let hidden_sz = cfg.hidden_size;
675 let intermediate_sz = cfg.intermediate_size;
676 let gate_proj = linear_b(hidden_sz, intermediate_sz, cfg.use_bias, vb.pp("gate_proj"))?;
677 let up_proj = linear_b(hidden_sz, intermediate_sz, cfg.use_bias, vb.pp("up_proj"))?;
678 let down_proj = linear_b(intermediate_sz, hidden_sz, cfg.use_bias, vb.pp("down_proj"))?;
679 Ok(Self {
680 gate_proj,
681 up_proj,
682 down_proj,
683 act_fn: cfg.hidden_act,
684 })
685 }
686
687 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
688 let lhs = self.gate_proj.forward(xs)?.apply(&self.act_fn)?;
689 let rhs = self.up_proj.forward(xs)?;
690 self.down_proj.forward(&(lhs * rhs)?)
691 }
692
693 fn forward_with_export(
695 &self,
696 xs: &Tensor,
697 ) -> Result<(Tensor, std::collections::HashMap<String, Tensor>)> {
698 use std::collections::HashMap;
699 let mut tensors: HashMap<String, Tensor> = HashMap::new();
700
701 let gate_out = self.gate_proj.forward(xs)?;
703 tensors.insert("gate_proj_out".to_string(), gate_out.clone());
704
705 let gate_act = gate_out.apply(&self.act_fn)?;
707 tensors.insert("gate_act_out".to_string(), gate_act.clone());
708
709 let up_out = self.up_proj.forward(xs)?;
711 tensors.insert("up_proj_out".to_string(), up_out.clone());
712
713 let mul_out = (&gate_act * &up_out)?;
715 tensors.insert("gate_up_mul".to_string(), mul_out.clone());
716
717 let output = self.down_proj.forward(&mul_out)?;
719 tensors.insert("down_proj_out".to_string(), output.clone());
720
721 Ok((output, tensors))
722 }
723}
724
725struct Attention {
727 q_proj: Linear,
728 k_proj: Linear,
729 v_proj: Linear,
730 o_proj: Linear,
731 num_heads: usize,
732 num_kv_heads: usize,
733 num_kv_groups: usize,
734 head_dim: usize,
735 rotary_emb: Arc<RotaryEmbedding>,
736 kv_cache: Option<(Tensor, Tensor)>,
737 softmax_scale: f64,
738}
739
740impl Attention {
741 fn new(rotary_emb: Arc<RotaryEmbedding>, cfg: &TextConfig, vb: VarBuilder) -> Result<Self> {
742 let hidden_sz = cfg.hidden_size;
743 let num_heads = cfg.num_attention_heads;
744 let num_kv_heads = cfg.num_key_value_heads;
745 let head_dim = cfg.head_dim;
746 let num_kv_groups = num_heads / num_kv_heads;
747
748 let q_proj = linear_b(
749 hidden_sz,
750 num_heads * head_dim,
751 cfg.use_bias,
752 vb.pp("q_proj"),
753 )?;
754 let k_proj = linear_b(
755 hidden_sz,
756 num_kv_heads * head_dim,
757 cfg.use_bias,
758 vb.pp("k_proj"),
759 )?;
760 let v_proj = linear_b(
761 hidden_sz,
762 num_kv_heads * head_dim,
763 cfg.use_bias,
764 vb.pp("v_proj"),
765 )?;
766 let o_proj = linear_b(
767 num_heads * head_dim,
768 hidden_sz,
769 cfg.use_bias,
770 vb.pp("o_proj"),
771 )?;
772
773 Ok(Self {
774 q_proj,
775 k_proj,
776 v_proj,
777 o_proj,
778 num_heads,
779 num_kv_heads,
780 num_kv_groups,
781 head_dim,
782 rotary_emb,
783 kv_cache: None,
784 softmax_scale: 1.0 / (head_dim as f64).sqrt(),
785 })
786 }
787
788 fn forward_with_mrope(
790 &mut self,
791 xs: &Tensor,
792 attention_mask: Option<&Tensor>,
793 position_ids: &Tensor,
794 ) -> Result<Tensor> {
795 let (b_sz, q_len, _) = xs.dims3()?;
796
797 let query_states = self.q_proj.forward(xs)?;
798 let key_states = self.k_proj.forward(xs)?;
799 let value_states = self.v_proj.forward(xs)?;
800
801 let query_states = query_states
802 .reshape((b_sz, q_len, self.num_heads, self.head_dim))?
803 .transpose(1, 2)?;
804 let key_states = key_states
805 .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))?
806 .transpose(1, 2)?;
807 let value_states = value_states
808 .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))?
809 .transpose(1, 2)?;
810
811 let (query_states, key_states) = self.rotary_emb.apply_multimodal_rotary_emb(
813 &query_states,
814 &key_states,
815 position_ids,
816 )?;
817
818 self.compute_attention(
819 query_states,
820 key_states,
821 value_states,
822 attention_mask,
823 b_sz,
824 q_len,
825 )
826 }
827
828 fn compute_attention(
830 &mut self,
831 query_states: Tensor,
832 key_states: Tensor,
833 value_states: Tensor,
834 attention_mask: Option<&Tensor>,
835 b_sz: usize,
836 q_len: usize,
837 ) -> Result<Tensor> {
838 let (key_states, value_states) = match &self.kv_cache {
840 None => (key_states, value_states),
841 Some((prev_k, prev_v)) => {
842 let key_states = Tensor::cat(&[prev_k, &key_states], 2)?;
843 let value_states = Tensor::cat(&[prev_v, &value_states], 2)?;
844 (key_states, value_states)
845 }
846 };
847 self.kv_cache = Some((key_states.clone(), value_states.clone()));
848
849 let key_states = crate::utils::repeat_kv(key_states, self.num_kv_groups)?.contiguous()?;
851 let value_states =
852 crate::utils::repeat_kv(value_states, self.num_kv_groups)?.contiguous()?;
853
854 let attn_output = {
856 let attn_weights =
858 (query_states.matmul(&key_states.transpose(2, 3)?)? * self.softmax_scale)?;
859
860 let attn_weights = match attention_mask {
862 None => attn_weights,
863 Some(mask) => attn_weights.broadcast_add(mask)?,
864 };
865 let original_dtype = attn_weights.dtype();
867 let attn_weights = if original_dtype != DType::F32 {
868 let attn_weights = attn_weights.to_dtype(DType::F32)?;
869 let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?;
870 attn_weights.to_dtype(original_dtype)?
871 } else {
872 candle_nn::ops::softmax_last_dim(&attn_weights)?
873 };
874 attn_weights.matmul(&value_states)?
876 };
877
878 attn_output
880 .transpose(1, 2)?
881 .contiguous()?
882 .reshape((b_sz, q_len, self.num_heads * self.head_dim))?
883 .apply(&self.o_proj)
884 }
885
886 pub fn forward_with_mrope_export(
889 &mut self,
890 xs: &Tensor,
891 attention_mask: Option<&Tensor>,
892 position_ids: &Tensor,
893 ) -> Result<(Tensor, std::collections::HashMap<String, Tensor>)> {
894 use std::collections::HashMap;
895 let mut tensors: HashMap<String, Tensor> = HashMap::new();
896
897 let (b_sz, q_len, _) = xs.dims3()?;
898
899 let query_states = self.q_proj.forward(xs)?;
901 let key_states = self.k_proj.forward(xs)?;
902 let value_states = self.v_proj.forward(xs)?;
903
904 let query_states = query_states
907 .reshape((b_sz, q_len, self.num_heads, self.head_dim))?
908 .transpose(1, 2)?;
909 let key_states = key_states
910 .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))?
911 .transpose(1, 2)?;
912 let value_states = value_states
913 .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))?
914 .transpose(1, 2)?;
915
916 tensors.insert("q_pre_rope".to_string(), query_states.clone());
917 tensors.insert("k_pre_rope".to_string(), key_states.clone());
918 tensors.insert("v".to_string(), value_states.clone());
919
920 let (query_states, key_states, rope_tensors) = self
922 .rotary_emb
923 .apply_multimodal_rotary_emb_with_export(&query_states, &key_states, position_ids)?;
924
925 for (k, v) in rope_tensors {
927 tensors.insert(format!("rope_{}", k), v);
928 }
929
930 tensors.insert("q_post_rope".to_string(), query_states.clone());
931 tensors.insert("k_post_rope".to_string(), key_states.clone());
932
933 let key_states_repeated =
936 crate::utils::repeat_kv(key_states.clone(), self.num_kv_groups)?.contiguous()?;
937 let value_states_repeated =
938 crate::utils::repeat_kv(value_states.clone(), self.num_kv_groups)?.contiguous()?;
939
940 tensors.insert("k_repeated".to_string(), key_states_repeated.clone());
941 tensors.insert("v_repeated".to_string(), value_states_repeated.clone());
942
943 let attn_weights_pre =
945 (query_states.matmul(&key_states_repeated.transpose(2, 3)?)? * self.softmax_scale)?;
946 let seq_len = attn_weights_pre.dim(2)?;
949 let attn_last_row = attn_weights_pre.narrow(2, seq_len - 1, 1)?;
950 tensors.insert("attn_weights_last_row".to_string(), attn_last_row);
951
952 let attn_weights_masked = match attention_mask {
954 None => attn_weights_pre,
955 Some(mask) => attn_weights_pre.broadcast_add(mask)?,
956 };
957
958 let original_dtype = attn_weights_masked.dtype();
960 let attn_weights = if original_dtype != DType::F32 {
961 let attn_weights = attn_weights_masked.to_dtype(DType::F32)?;
962 let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?;
963 attn_weights.to_dtype(original_dtype)?
964 } else {
965 candle_nn::ops::softmax_last_dim(&attn_weights_masked)?
966 };
967 let attn_softmax_last_row = attn_weights.narrow(2, seq_len - 1, 1)?;
969 tensors.insert(
970 "attn_weights_softmax_last_row".to_string(),
971 attn_softmax_last_row,
972 );
973
974 let attn_output = attn_weights.matmul(&value_states_repeated)?;
976 tensors.insert("attn_output_pre_transpose".to_string(), attn_output.clone());
977
978 let attn_output = attn_output.transpose(1, 2)?.contiguous()?.reshape((
980 b_sz,
981 q_len,
982 self.num_heads * self.head_dim,
983 ))?;
984
985 let output = self.o_proj.forward(&attn_output)?;
987 tensors.insert("attn_output".to_string(), output.clone());
988
989 Ok((output, tensors))
990 }
991
992 fn clear_kv_cache(&mut self) {
993 self.kv_cache = None;
994 }
995}
996
997struct DecoderLayer {
999 self_attn: Attention,
1000 mlp: Mlp,
1001 input_layernorm: RmsNorm,
1002 post_attention_layernorm: RmsNorm,
1003}
1004
1005impl DecoderLayer {
1006 fn new(rotary_emb: Arc<RotaryEmbedding>, cfg: &TextConfig, vb: VarBuilder) -> Result<Self> {
1007 let self_attn = Attention::new(rotary_emb, cfg, vb.pp("self_attn"))?;
1008 let mlp = Mlp::new(cfg, vb.pp("mlp"))?;
1009 let input_layernorm =
1010 rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?;
1011 let post_attention_layernorm = rms_norm(
1012 cfg.hidden_size,
1013 cfg.rms_norm_eps,
1014 vb.pp("post_attention_layernorm"),
1015 )?;
1016 Ok(Self {
1017 self_attn,
1018 mlp,
1019 input_layernorm,
1020 post_attention_layernorm,
1021 })
1022 }
1023
1024 fn forward_with_mrope(
1026 &mut self,
1027 xs: &Tensor,
1028 attention_mask: Option<&Tensor>,
1029 position_ids: &Tensor,
1030 ) -> Result<Tensor> {
1031 let residual = xs;
1032 let xs = self.input_layernorm.forward(xs)?;
1033 let xs = self
1034 .self_attn
1035 .forward_with_mrope(&xs, attention_mask, position_ids)?;
1036 let xs = (xs + residual)?;
1037 let residual = &xs;
1038 let xs = self
1039 .mlp
1040 .forward(&xs.apply(&self.post_attention_layernorm)?)?;
1041 residual + xs
1042 }
1043
1044 fn forward_with_mrope_export(
1046 &mut self,
1047 xs: &Tensor,
1048 attention_mask: Option<&Tensor>,
1049 position_ids: &Tensor,
1050 ) -> Result<(Tensor, std::collections::HashMap<String, Tensor>)> {
1051 use std::collections::HashMap;
1052 let mut tensors: HashMap<String, Tensor> = HashMap::new();
1053
1054 let residual = xs;
1055 tensors.insert("layer_input".to_string(), xs.clone());
1056
1057 let xs = self.input_layernorm.forward(xs)?;
1058 tensors.insert("post_input_layernorm".to_string(), xs.clone());
1059
1060 let (attn_out, attn_tensors) =
1061 self.self_attn
1062 .forward_with_mrope_export(&xs, attention_mask, position_ids)?;
1063
1064 for (k, v) in attn_tensors {
1066 tensors.insert(format!("attn_{}", k), v);
1067 }
1068
1069 let xs = (attn_out + residual)?;
1070 tensors.insert("post_attn_residual".to_string(), xs.clone());
1071
1072 let residual = &xs;
1073 let post_norm = xs.apply(&self.post_attention_layernorm)?;
1074 tensors.insert("post_attention_layernorm".to_string(), post_norm.clone());
1075
1076 let (mlp_out, mlp_tensors) = self.mlp.forward_with_export(&post_norm)?;
1078
1079 for (k, v) in mlp_tensors {
1081 tensors.insert(format!("mlp_{}", k), v);
1082 }
1083
1084 tensors.insert("mlp_output".to_string(), mlp_out.clone());
1085
1086 let output = (residual + mlp_out)?;
1087 tensors.insert("layer_output".to_string(), output.clone());
1088
1089 Ok((output, tensors))
1090 }
1091
1092 fn clear_kv_cache(&mut self) {
1093 self.self_attn.clear_kv_cache();
1094 }
1095}
1096
1097pub struct TextModel {
1099 embed_tokens: Embedding,
1100 layers: Vec<DecoderLayer>,
1101 norm: RmsNorm,
1102 lm_head: Linear,
1103 pub dtype: DType,
1104 pub hidden_size: usize,
1105 device: Device,
1106}
1107
1108impl TextModel {
1109 pub fn new(cfg: &TextConfig, vb: VarBuilder) -> Result<Self> {
1110 let vb_m = vb.pp("model");
1111
1112 let embed_tokens = embedding(cfg.vocab_size, cfg.hidden_size, vb_m.pp("embed_tokens"))?;
1113
1114 let rotary_emb = Arc::new(RotaryEmbedding::new(cfg, vb.device(), vb.dtype())?);
1115
1116 let mut layers = Vec::with_capacity(cfg.num_hidden_layers);
1117 let vb_l = vb_m.pp("layers");
1118 for layer_idx in 0..cfg.num_hidden_layers {
1119 let layer = DecoderLayer::new(rotary_emb.clone(), cfg, vb_l.pp(layer_idx))?;
1120 layers.push(layer);
1121 }
1122
1123 let norm = rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb_m.pp("norm"))?;
1124
1125 let lm_head = if cfg.tie_word_embeddings {
1126 Linear::new(embed_tokens.embeddings().clone(), None)
1127 } else {
1128 linear_b(cfg.hidden_size, cfg.vocab_size, false, vb.pp("lm_head"))?
1129 };
1130
1131 Ok(Self {
1132 embed_tokens,
1133 layers,
1134 norm,
1135 lm_head,
1136 dtype: vb.dtype(),
1137 hidden_size: cfg.hidden_size,
1138 device: vb.device().clone(),
1139 })
1140 }
1141
1142 pub fn embed_tokens(&self, input_ids: &Tensor) -> Result<Tensor> {
1144 self.embed_tokens.forward(input_ids)
1145 }
1146
1147 fn prepare_causal_attention_mask(
1149 &self,
1150 b_size: usize,
1151 tgt_len: usize,
1152 seqlen_offset: usize,
1153 ) -> Result<Tensor> {
1154 let mask: Vec<f32> = (0..tgt_len)
1155 .flat_map(|i| (0..tgt_len).map(move |j| if i < j { f32::NEG_INFINITY } else { 0f32 }))
1156 .collect();
1157 let mask = Tensor::from_slice(&mask, (tgt_len, tgt_len), &self.device)?;
1158 let mask = if seqlen_offset > 0 {
1159 let mask0 = Tensor::zeros((tgt_len, seqlen_offset), DType::F32, &self.device)?;
1160 Tensor::cat(&[&mask0, &mask], D::Minus1)?
1161 } else {
1162 mask
1163 };
1164 mask.expand((b_size, 1, tgt_len, tgt_len + seqlen_offset))?
1165 .to_dtype(self.dtype)
1166 }
1167
1168 pub fn forward_embeds_with_mrope(
1173 &mut self,
1174 mut xs: Tensor,
1175 position_ids: &Tensor,
1176 ) -> Result<Tensor> {
1177 let (b_sz, seq_len, _) = xs.dims3()?;
1178
1179 let attention_mask = if seq_len <= 1 {
1181 None
1182 } else {
1183 Some(self.prepare_causal_attention_mask(b_sz, seq_len, 0)?)
1184 };
1185
1186 for layer in self.layers.iter_mut() {
1187 xs = layer.forward_with_mrope(&xs, attention_mask.as_ref(), position_ids)?;
1188 }
1189
1190 xs = xs.apply(&self.norm)?;
1191
1192 self.lm_head
1194 .forward(&xs)?
1195 .i((.., seq_len - 1, ..))?
1196 .contiguous()
1197 }
1198
1199 pub fn clear_kv_cache(&mut self) {
1201 for layer in self.layers.iter_mut() {
1202 layer.clear_kv_cache();
1203 }
1204 }
1205
1206 pub fn forward_embeds_with_mrope_export(
1211 &mut self,
1212 mut xs: Tensor,
1213 position_ids: &Tensor,
1214 ) -> Result<(Tensor, std::collections::HashMap<String, Tensor>)> {
1215 use std::collections::HashMap;
1216
1217 let mut tensors: HashMap<String, Tensor> = HashMap::new();
1218 let (b_sz, seq_len, _) = xs.dims3()?;
1219
1220 let attention_mask = if seq_len <= 1 {
1222 None
1223 } else {
1224 let mask = self.prepare_causal_attention_mask(b_sz, seq_len, 0)?;
1225 tensors.insert("causal_mask".to_string(), mask.clone());
1226 Some(mask)
1227 };
1228
1229 tensors.insert("layer0_input".to_string(), xs.clone());
1230
1231 for (i, layer) in self.layers.iter_mut().enumerate() {
1234 if i == 1 {
1235 let (layer_out, layer_tensors) =
1237 layer.forward_with_mrope_export(&xs, attention_mask.as_ref(), position_ids)?;
1238 xs = layer_out;
1239 for (k, v) in layer_tensors {
1241 tensors.insert(format!("layer1_{}", k), v);
1242 }
1243 } else {
1244 xs = layer.forward_with_mrope(&xs, attention_mask.as_ref(), position_ids)?;
1245 }
1246 tensors.insert(format!("layer_{}_output", i), xs.clone());
1248 }
1249
1250 xs = xs.apply(&self.norm)?;
1252 tensors.insert("final_hidden_state".to_string(), xs.clone());
1253
1254 let logits = self.lm_head.forward(&xs)?;
1256 tensors.insert("logits".to_string(), logits.clone());
1257
1258 Ok((logits, tensors))
1259 }
1260}