use crate::{ models::qwen2_5vl::config::{Qwen2_5VLConfig, RopeScaling}, position_embed::rope::{ apply_rotary_pos_emb, apply_rotary_pos_emb_vision, Qwen2_5VLTextRotaryEmbedding, Qwen2_5VisionRotaryEmbedding }, utils::tensor_utils::{ get_equal_mask, get_vision_next_indices, masked_scatter_dim0, nonzero_index, repeat_kv, safe_arg_sort_last_dim, zero_index }, }; use anyhow::{Result, anyhow}; use candle_core::{D, DType, Device, IndexOp, Tensor}; use candle_nn::{ Activation, Init, Linear, Module, RmsNorm, VarBuilder, linear, linear_no_bias, rms_norm, }; pub struct Qwen2_5VisionPatchEmbed { conv3d_weight: Tensor, } impl Qwen2_5VisionPatchEmbed { pub fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let patch_size = cfg.vision_config.patch_size; let temporal_patch_size = cfg.vision_config.temporal_patch_size; let in_channels = cfg.vision_config.in_chans; let embed_dim = cfg.vision_config.hidden_size; // conv3d weight key: visual.patch_embed.proj.weight, value: Tensor[dims 1280, 3, 2, 14, 14; bf16, cuda:0] // (1280, 3, 2, 14, 14) -> (1280, 1176) -> (1176, 1280) let conv3d_weight = vb .get_with_hints( ( embed_dim, in_channels, temporal_patch_size, patch_size, patch_size, ), "proj.weight", Init::Const(1.), )? .flatten(1, 4)? .t()?; Ok(Self { conv3d_weight }) } pub fn forward(&self, hidden_states: &Tensor) -> Result { // hidden_states shape: (grid_t*grid_h*grid_w, c*temporal_patch_size*patch_size*patch_size) // ((), 1176) matmul (1176, 1280) -> ((), 1280) let hidden_states = hidden_states.matmul(&self.conv3d_weight)?; Ok(hidden_states) } } pub struct Qwen2_5VLPatchMerger { hidden_size: usize, ln_q: RmsNorm, mlp_0: Linear, mlp_2: Linear, } impl Qwen2_5VLPatchMerger { pub fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let hidden_size = cfg.vision_config.hidden_size * (cfg.vision_config.spatial_merge_size.pow(2)); let ln_q = rms_norm( cfg.vision_config.hidden_size, cfg.rms_norm_eps, vb.pp("ln_q"), )?; let mlp_0 = linear(hidden_size, hidden_size, vb.pp("mlp.0"))?; let mlp_2 = linear( hidden_size, cfg.vision_config.out_hidden_size, vb.pp("mlp.2"), )?; Ok(Self { hidden_size, ln_q, mlp_0, mlp_2, }) } } impl Module for Qwen2_5VLPatchMerger { fn forward(&self, xs: &Tensor) -> candle_core::Result { let xs = xs.apply(&self.ln_q)?.reshape(((), self.hidden_size))?; let xs = xs.apply(&self.mlp_0)?.gelu()?.apply(&self.mlp_2)?; Ok(xs) } } #[derive(Debug, Clone)] struct Qwen2_5VLVisionMLP { gate_proj: Linear, up_proj: Linear, down_proj: Linear, act_fn: Activation, } impl Qwen2_5VLVisionMLP { fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let hidden_sz = cfg.vision_config.hidden_size; let intermediate_sz = cfg.vision_config.intermediate_size; let gate_proj = linear(hidden_sz, intermediate_sz, vb.pp("gate_proj"))?; let up_proj = linear(hidden_sz, intermediate_sz, vb.pp("up_proj"))?; let down_proj = linear(intermediate_sz, hidden_sz, vb.pp("down_proj"))?; Ok(Self { gate_proj, up_proj, down_proj, act_fn: cfg.hidden_act, }) } } impl Module for Qwen2_5VLVisionMLP { fn forward(&self, xs: &Tensor) -> candle_core::Result { let lhs = xs.apply(&self.gate_proj)?.apply(&self.act_fn)?; let rhs = xs.apply(&self.up_proj)?; (lhs * rhs)?.apply(&self.down_proj) } } #[derive(Debug, Clone)] struct Qwen2_5VLVisionAttention { qkv: Linear, proj: Linear, num_heads: usize, scale: Tensor, } impl Qwen2_5VLVisionAttention { fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let hidden_size = cfg.vision_config.hidden_size; let num_heads = cfg.vision_config.num_heads; let head_dim = hidden_size / num_heads; let qkv = linear(hidden_size, hidden_size * 3, vb.pp("qkv"))?; let proj = linear(hidden_size, hidden_size, vb.pp("proj"))?; let scale = Tensor::new(vec![1f32 / (head_dim as f32).sqrt()], vb.device())? .to_dtype(vb.dtype())?; Ok(Self { qkv, proj, num_heads, scale, }) } fn forward( &self, xs: &Tensor, cos: &Tensor, sin: &Tensor, attention_mask: &Tensor, ) -> Result { // xs: (seq_len, hidden_size) let seq_length = xs.dim(0)?; // (seq_len, hidden_size) -> (seq_len, hidden_size*3) // -> (seq_len, 3, num_heads, head_dim) -> (3, seq_len, num_heads, head_dim) let qkv_states = xs .apply(&self.qkv)? .reshape((seq_length, 3, self.num_heads, ()))? .permute((1, 0, 2, 3))?; // (seq_len, num_heads, head_dim) let query_states = qkv_states.i(0)?.contiguous()?; let key_states = qkv_states.i(1)?.contiguous()?; let value_states = qkv_states.i(2)?.contiguous()?; let (query_states, key_states) = apply_rotary_pos_emb_vision(&query_states, &key_states, cos, sin)?; // (seq_len, num_heads, head_dim) -> (num_heads, seq_len, head_dim) let query_states = query_states.transpose(0, 1)?.contiguous()?; let key_states = key_states.transpose(0, 1)?.contiguous()?; let value_states = value_states.transpose(0, 1)?.contiguous()?; let attn_output = { let attn_weights = query_states .matmul(&key_states.transpose(D::Minus2, D::Minus1)?)? .broadcast_mul(&self.scale)?; let attn_weights = attn_weights.broadcast_add(&attention_mask)?; let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?; attn_weights.matmul(&value_states)? }; // (num_heads, seq_len, head_dim) -> (seq_len, num_heads, head_dim) -> (seq_len, hidden_size) let attn_output = attn_output .transpose(0, 1)? .reshape((seq_length, ()))? .contiguous()?; let attn_ouput = attn_output.apply(&self.proj)?; Ok(attn_ouput) } } #[derive(Debug, Clone)] struct Qwen2_5VLVisionBlock { attn: Qwen2_5VLVisionAttention, mlp: Qwen2_5VLVisionMLP, norm1: RmsNorm, norm2: RmsNorm, } impl Qwen2_5VLVisionBlock { fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let attn = Qwen2_5VLVisionAttention::new(cfg, vb.pp("attn"))?; let mlp = Qwen2_5VLVisionMLP::new(cfg, vb.pp("mlp"))?; let norm1 = rms_norm( cfg.vision_config.hidden_size, cfg.rms_norm_eps, vb.pp("norm1"), )?; let norm2 = rms_norm( cfg.vision_config.hidden_size, cfg.rms_norm_eps, vb.pp("norm2"), )?; Ok(Self { attn, mlp, norm1, norm2, }) } fn forward( &self, xs: &Tensor, cos: &Tensor, sin: &Tensor, attention_mask: &Tensor, ) -> Result { let residual = xs; let xs = self.norm1.forward(xs)?; let xs = self.attn.forward(&xs, cos, sin, attention_mask)?; let xs = (xs + residual)?; let residual = &xs; let xs = xs.apply(&self.norm2)?.apply(&self.mlp)?; let xs = (residual + xs)?; Ok(xs) } } pub struct Qwen2_5VLVisionModel { spatial_merge_size: usize, patch_size: usize, fullatt_block_indexes: Vec, window_size: usize, spatial_merge_unit: usize, patch_embed: Qwen2_5VisionPatchEmbed, rotary_pos_emb: Qwen2_5VisionRotaryEmbedding, blocks: Vec, merger: Qwen2_5VLPatchMerger, dtype: DType, } impl Qwen2_5VLVisionModel { pub fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let spatial_merge_size = cfg.vision_config.spatial_merge_size; let patch_size = cfg.vision_config.patch_size; let fullatt_block_indexes = cfg.vision_config.fullatt_block_indexes.clone(); let window_size = cfg.vision_config.window_size; let spatial_merge_unit = spatial_merge_size * spatial_merge_size; let head_dim = cfg.vision_config.hidden_size / cfg.vision_config.num_heads; let patch_embed = Qwen2_5VisionPatchEmbed::new(cfg, vb.pp("patch_embed"))?; let rotary_pos_emb = Qwen2_5VisionRotaryEmbedding::new(head_dim / 2, None); let mut blocks = Vec::new(); let vb_blocks = vb.pp("blocks"); for i in 0..cfg.vision_config.depth { let block = Qwen2_5VLVisionBlock::new(cfg, vb_blocks.pp(i))?; blocks.push(block); } let merger = Qwen2_5VLPatchMerger::new(cfg, vb.pp("merger"))?; let dtype = vb.dtype(); Ok(Self { spatial_merge_size, patch_size, fullatt_block_indexes, window_size, spatial_merge_unit, patch_embed, rotary_pos_emb, blocks, merger, dtype, }) } pub fn rot_pos_emb(&self, grid_thw: &Tensor) -> Result { let mut pos_ids = Vec::new(); for i in 0..grid_thw.dim(0)? { let [t, h, w] = grid_thw.i(i)?.to_vec1::()?[..] else { return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); }; // hpos_ids shape (h, w) let hpos_ids = Tensor::arange(0, h, grid_thw.device())? .unsqueeze(1)? .expand((h as usize, w as usize))?; let hpos_ids = hpos_ids.reshape(( h as usize / self.spatial_merge_size, self.spatial_merge_size, w as usize / self.spatial_merge_size, self.spatial_merge_size, ))?; let hpos_ids = hpos_ids.permute((0, 2, 1, 3))?.flatten_all()?; let wpos_ids = Tensor::arange(0, w, grid_thw.device())? .unsqueeze(0)? .expand((h as usize, w as usize))?; let wpos_ids = wpos_ids.reshape(( h as usize / self.spatial_merge_size, self.spatial_merge_size, w as usize / self.spatial_merge_size, self.spatial_merge_size, ))?; let wpos_ids = wpos_ids.permute((0, 2, 1, 3))?.flatten_all()?; // thw_pos_ids shape (h*w, 2) let thw_pos_ids = Tensor::stack(&[&hpos_ids, &wpos_ids], D::Minus1)?.repeat((t as usize, 1))?; pos_ids.push(thw_pos_ids); } let pos_ids = Tensor::cat(&pos_ids, 0)?.contiguous()?; let max_grid_size = grid_thw.i((.., 1..))?.max_all()?.to_scalar::()?; let rotary_pos_emb_full = self .rotary_pos_emb .forward(max_grid_size as usize, grid_thw.device())?; // contiguous()一定要加!!!很重要!!!!,不然index_select出来的是错的 // 找错找了半天,都是泪啊,做维度索引操作后contiguous顺手写上总没错 // 第一列是h维度的索引 let pos_ids_h = pos_ids.i((.., 0))?.contiguous()?; // 第二列是w维度的索引 let pos_ids_w = pos_ids.i((.., 1))?.contiguous()?; let rotary_pos_emb_h = rotary_pos_emb_full.index_select(&pos_ids_h, 0)?; let rotary_pos_emb_w = rotary_pos_emb_full.index_select(&pos_ids_w, 0)?; // 每个patch融合h索引和w索引两个的位置编码信息 let rotary_pos_emb = Tensor::cat(&[rotary_pos_emb_h, rotary_pos_emb_w], 1)?.contiguous()?; Ok(rotary_pos_emb) } pub fn get_window_index(&self, grid_thw: &Tensor) -> Result<(Tensor, Tensor)> { let mut window_index = Vec::new(); let mut cu_window_seqlens = vec![0]; let mut window_index_id = 0_i64; let vit_merger_window_size = (self.window_size / self.spatial_merge_size / self.patch_size) as u32; for i in 0..grid_thw.dim(0)? { let [grid_t, grid_h, grid_w] = grid_thw.i(i)?.to_vec1::()?[..] else { return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); }; let llm_grid_h = grid_h / self.spatial_merge_size as u32; let llm_grid_w = grid_w / self.spatial_merge_size as u32; // 因为后续需要使用-100来做填充,所以需要int类型 // candle好像不支持i32, DType里面都没有定义i32, 所以这里使用i64 let mut index = Tensor::arange( window_index_id, window_index_id + (grid_t * llm_grid_h * llm_grid_w) as i64, grid_thw.device(), )? .reshape((grid_t as usize, llm_grid_h as usize, llm_grid_w as usize))? .contiguous()?; // python transformers 中实现如下 // let pad_h = (vit_merger_window_size - llm_grid_h % vit_merger_window_size); // 后面加上 % vit_merger_window_size,保证llm_grid_h能整除vit_merger_window_size时不需要pad // 按理说能整除应该是不需要pad的,transformers中这样实现不知道是不是有什么其他原因 let pad_h = (vit_merger_window_size - llm_grid_h % vit_merger_window_size) % vit_merger_window_size; let pad_w = (vit_merger_window_size - llm_grid_w % vit_merger_window_size) % vit_merger_window_size; let num_window_h = (llm_grid_h + pad_h) / vit_merger_window_size; let num_window_w = (llm_grid_w + pad_w) / vit_merger_window_size; if pad_h > 0 { let pad_h_t = Tensor::new(vec![-100_i64], grid_thw.device())? .broadcast_as((grid_t as usize, pad_h as usize, llm_grid_w as usize))? .contiguous()?; index = Tensor::cat(&[&index, &pad_h_t], 1)?; } if pad_w > 0 { let pad_w_t = Tensor::new(vec![-100_i64], grid_thw.device())? .broadcast_as(( grid_t as usize, (llm_grid_h + pad_h) as usize, pad_w as usize, ))? .contiguous()?; index = Tensor::cat(&[&index, &pad_w_t], 2)?; } let index_padded = index .reshape(( grid_t as usize, num_window_h as usize, vit_merger_window_size as usize, num_window_w as usize, vit_merger_window_size as usize, ))? .permute((0, 1, 3, 2, 4))?; let index_padded = index_padded .reshape(( grid_t as usize, (num_window_h * num_window_w) as usize, vit_merger_window_size as usize, vit_merger_window_size as usize, ))? .contiguous()?; let is_pad = Tensor::new(vec![-100_i64], grid_thw.device())?; let seqlens = index_padded .broadcast_ne(&is_pad)? .sum((2, 3))? .flatten_all()?; let index_padded = index_padded.flatten_all()?; let not_pad = index_padded.broadcast_ne(&is_pad)?.to_vec1::()?; let indices: Vec = not_pad .iter() .enumerate() .filter_map(|(idx, &val)| if val != 0 { Some(idx as u32) } else { None }) .collect(); let indices_tensor = Tensor::from_slice(&indices, indices.len(), grid_thw.device())?; let index_new = index_padded.gather(&indices_tensor, 0)?; window_index.push(index_new); let seq_len_last = cu_window_seqlens[cu_window_seqlens.len() - 1]; // cumsum方法i64类型执行会报错,先转成F64计算后再转回i64 let cu_seqlens_tmp = seqlens .to_dtype(candle_core::DType::F64)? .cumsum(0)? .to_dtype(candle_core::DType::I64)? .broadcast_mul(&Tensor::new( vec![self.spatial_merge_unit as i64], grid_thw.device(), )?)? .broadcast_add(&Tensor::new(vec![seq_len_last], grid_thw.device())?)?; cu_window_seqlens.extend_from_slice(&cu_seqlens_tmp.to_vec1::()?); window_index_id += (grid_t * llm_grid_h * llm_grid_w) as i64; } let window_index_tensor = Tensor::cat(&window_index, 0)?; let cu_window_seqlens_tensor = Tensor::from_slice( &cu_window_seqlens, cu_window_seqlens.len(), grid_thw.device(), )? .to_dtype(candle_core::DType::U32)?; Ok((window_index_tensor, cu_window_seqlens_tensor)) } pub fn get_attention_mask( &self, cu_seqlens: &Tensor, seq_len: usize, device: &Device, dtype: DType, ) -> Result { let mut attention_mask = Tensor::new(f32::NEG_INFINITY, device)?.broadcast_as((1, seq_len, seq_len))?; for i in 1..cu_seqlens.dim(0)? { let start = cu_seqlens.i(i - 1)?.to_scalar::()? as usize; let end = cu_seqlens.i(i)?.to_scalar::()? as usize; let block_size = end - start; let zeros = Tensor::zeros((1, block_size, block_size), candle_core::DType::F32, device)?; attention_mask = attention_mask.slice_assign(&[(0..1), (start..end), (start..end)], &zeros)?; } let attention_mask = attention_mask.to_dtype(dtype)?.contiguous()?; Ok(attention_mask) } pub fn forward(&self, hidden_states: &Tensor, grid_thw: &Tensor) -> Result { // hidden_states: (seq_len, hidden_size) // grid_thw: (num_images_or_videos, 3), temporal, height, width let hidden_states = hidden_states.to_dtype(self.dtype)?; let hidden_states = self.patch_embed.forward(&hidden_states)?; let rotary_pos_emb = self.rot_pos_emb(grid_thw)?; let (window_index, cu_window_seqlens) = self.get_window_index(grid_thw)?; let seq_len = hidden_states.dim(0)?; let hidden_states = hidden_states .reshape(( seq_len / self.spatial_merge_unit, self.spatial_merge_unit, (), ))? .contiguous()?; let hidden_states = hidden_states.index_select(&window_index, 0)?; let mut hidden_states = hidden_states.reshape((seq_len, ()))?; let rotary_pos_emb = rotary_pos_emb.reshape(( seq_len / self.spatial_merge_unit, self.spatial_merge_unit, (), ))?; let rotary_pos_emb = rotary_pos_emb.index_select(&window_index, 0)?; let rotary_pos_emb = rotary_pos_emb.reshape((seq_len, ()))?; let emb = Tensor::cat(&[&rotary_pos_emb, &rotary_pos_emb], D::Minus1)?; let cos = emb.cos()?.to_dtype(hidden_states.dtype())?; let sin = emb.sin()?.to_dtype(hidden_states.dtype())?; let cu_seqlens = grid_thw.i((.., 1))?.mul(&grid_thw.i((.., 2))?)?; let grid_t = grid_thw.i((.., 0))?.to_vec1::()?; let cu_seqlens_full = match cu_seqlens.rank() { 1 => cu_seqlens.repeat(grid_t[0] as usize)?, 2 => { let mut cu_seqlens_repeat = Vec::new(); for (index, t) in grid_t.iter().enumerate() { cu_seqlens_repeat.push(cu_seqlens.i(index)?.repeat(t.clone() as usize)?); } Tensor::cat(&cu_seqlens_repeat, 0)?.flatten_all()? } _ => { return Err(anyhow!(format!("create cu_seqlens error"))); } }; let cu_seqlens = cu_seqlens_full .to_dtype(DType::F64)? .cumsum(0)? .to_dtype(DType::U32)?; let pad_zero = Tensor::from_vec(vec![0_u32], 1, hidden_states.device())?; let cu_seqlens = Tensor::cat(&[&pad_zero, &cu_seqlens], D::Minus1)?; let attention_mask_window = self.get_attention_mask( &cu_window_seqlens, seq_len, hidden_states.device(), hidden_states.dtype(), )?; let attention_mask_full = self.get_attention_mask( &cu_seqlens, seq_len, hidden_states.device(), hidden_states.dtype(), )?; let mut attention_mask = attention_mask_window.clone(); for (layer_num, block) in self.blocks.iter().enumerate() { if self.fullatt_block_indexes.contains(&layer_num) { attention_mask = attention_mask_full.clone(); } else { attention_mask = attention_mask_window.clone(); } hidden_states = block.forward(&hidden_states, &cos, &sin, &attention_mask)?; } let hidden_states = self.merger.forward(&hidden_states)?; let reverse_indices = safe_arg_sort_last_dim(&window_index, true)?; let hidden_states = hidden_states.index_select(&reverse_indices, 0)?; Ok(hidden_states) } } #[derive(Debug, Clone)] struct Qwen2_5VLTextMLP { gate_proj: Linear, up_proj: Linear, down_proj: Linear, act_fn: Activation, } impl Qwen2_5VLTextMLP { fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let hidden_sz = cfg.hidden_size; let intermediate_sz = cfg.intermediate_size; let gate_proj = linear_no_bias(hidden_sz, intermediate_sz, vb.pp("gate_proj"))?; let up_proj = linear_no_bias(hidden_sz, intermediate_sz, vb.pp("up_proj"))?; let down_proj = linear_no_bias(intermediate_sz, hidden_sz, vb.pp("down_proj"))?; Ok(Self { gate_proj, up_proj, down_proj, act_fn: cfg.hidden_act, }) } } impl Module for Qwen2_5VLTextMLP { fn forward(&self, xs: &Tensor) -> candle_core::Result { let lhs = xs.apply(&self.gate_proj)?.apply(&self.act_fn)?; let rhs = xs.apply(&self.up_proj)?; (lhs * rhs)?.apply(&self.down_proj) } } #[derive(Debug, Clone)] struct Qwen2_5VLTextAttention { q_proj: Linear, k_proj: Linear, v_proj: Linear, o_proj: Linear, num_heads: usize, num_kv_heads: usize, num_kv_groups: usize, head_dim: usize, hidden_size: usize, kv_cache: Option<(Tensor, Tensor)>, } impl Qwen2_5VLTextAttention { fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let hidden_size = cfg.hidden_size; let num_heads = cfg.num_attention_heads; let num_kv_heads = cfg.num_key_value_heads; let num_kv_groups = num_heads / num_kv_heads; let head_dim = hidden_size / num_heads; let q_proj = linear(hidden_size, num_heads * head_dim, vb.pp("q_proj"))?; let k_proj = linear(hidden_size, num_kv_heads * head_dim, vb.pp("k_proj"))?; let v_proj = linear(hidden_size, num_kv_heads * head_dim, vb.pp("v_proj"))?; let o_proj = linear_no_bias(hidden_size, hidden_size, vb.pp("o_proj"))?; Ok(Self { q_proj, k_proj, v_proj, o_proj, num_heads, num_kv_heads, num_kv_groups, head_dim, hidden_size, kv_cache: None, }) } fn forward( &mut self, xs: &Tensor, cos: &Tensor, sin: &Tensor, attention_mask: Option<&Tensor>, ) -> Result { let (b_sz, q_len, _) = xs.dims3()?; let query_states = self.q_proj.forward(xs)?; let key_states = self.k_proj.forward(xs)?; let value_states = self.v_proj.forward(xs)?; let query_states = query_states .reshape((b_sz, q_len, self.num_heads, self.head_dim))? .transpose(1, 2)?; let key_states = key_states .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))? .transpose(1, 2)?; let value_states = value_states .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))? .transpose(1, 2)?; let (query_states, key_states) = apply_rotary_pos_emb(&query_states, &key_states, cos, sin)?; let (key_states, value_states) = match &self.kv_cache { None => (key_states, value_states), Some((prev_k, prev_v)) => { let key_states = Tensor::cat(&[prev_k, &key_states], 2)?; let value_states = Tensor::cat(&[prev_v, &value_states], 2)?; (key_states, value_states) } }; self.kv_cache = Some((key_states.clone(), value_states.clone())); let key_states = repeat_kv(key_states, self.num_kv_groups)?.contiguous()?; let value_states = repeat_kv(value_states, self.num_kv_groups)?.contiguous()?; let query_states = query_states.contiguous()?; let attn_output = { let scale = 1f64 / f64::sqrt(self.head_dim as f64); #[cfg(not(feature = "flash-attn"))] { let attn_weights = query_states.matmul(&key_states.transpose(D::Minus2, D::Minus1)?)?; let attn_weights = (attn_weights * scale)?; let attn_weights = match attention_mask { None => attn_weights, Some(mask) => attn_weights.broadcast_add(mask)?, }; let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?; let attn_weights = attn_weights.matmul(&value_states)?; attn_weights } #[cfg(feature = "flash-attn")] { // use flash-attn, // flash-attn shape: (bs, seq_len, num_head, head_dim) let query_states = query_states.transpose(1, 2)?; let key_states = key_states.transpose(1, 2)?; let value_states = value_states.transpose(1, 2)?; let attn_output = candle_flash_attn::flash_attn( &query_states, &key_states, &value_states, scale as f32, attention_mask.is_some(), )? .transpose(1, 2)?; attn_output } }; let attn_output = attn_output .transpose(1, 2)? .contiguous()? .reshape((b_sz, q_len, self.hidden_size))?; let attn_output = attn_output.apply(&self.o_proj)?; Ok(attn_output) } fn clear_kv_cache(&mut self) { self.kv_cache = None } } #[derive(Debug, Clone)] struct Qwen2_5VLTextDecoderLayer { self_attn: Qwen2_5VLTextAttention, mlp: Qwen2_5VLTextMLP, input_layernorm: RmsNorm, post_attention_layernorm: RmsNorm, } impl Qwen2_5VLTextDecoderLayer { fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let self_attn = Qwen2_5VLTextAttention::new(cfg, vb.pp("self_attn"))?; let mlp = Qwen2_5VLTextMLP::new(cfg, vb.pp("mlp"))?; let input_layernorm = rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?; let post_attention_layernorm = rms_norm( cfg.hidden_size, cfg.rms_norm_eps, vb.pp("post_attention_layernorm"), )?; Ok(Self { self_attn, mlp, input_layernorm, post_attention_layernorm, }) } fn forward( &mut self, xs: &Tensor, cos: &Tensor, sin: &Tensor, attention_mask: Option<&Tensor>, ) -> Result { let residual = xs; let xs = self.input_layernorm.forward(xs)?; let xs = self.self_attn.forward(&xs, cos, sin, attention_mask)?; let xs = (xs + residual)?; let residual = &xs; let xs = xs.apply(&self.post_attention_layernorm)?.apply(&self.mlp)?; let xs = (residual + xs)?; Ok(xs) } fn clear_kv_cache(&mut self) { self.self_attn.clear_kv_cache() } } #[derive(Debug, Clone)] pub struct Qwen2_5VLTextModel { pub embed_tokens: candle_nn::Embedding, layers: Vec, norm: RmsNorm, rotary_emb: Qwen2_5VLTextRotaryEmbedding, dtype: DType, sliding_window: usize, device: Device, rope_scaling: RopeScaling, } impl Qwen2_5VLTextModel { pub fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result { let embed_tokens = candle_nn::embedding(cfg.vocab_size, cfg.hidden_size, vb.pp("embed_tokens"))?; let head_dim = cfg.hidden_size / cfg.num_attention_heads; let rotary_emb = Qwen2_5VLTextRotaryEmbedding::new(head_dim, cfg.rope_theta); let mut layers = Vec::with_capacity(cfg.num_hidden_layers); let vb_l = vb.pp("layers"); for layer_idx in 0..cfg.num_hidden_layers { let layer = Qwen2_5VLTextDecoderLayer::new(cfg, vb_l.pp(layer_idx))?; layers.push(layer) } let norm = rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("norm"))?; let sliding_window = cfg.sliding_window; let rope_scaling = cfg.rope_scaling.clone(); Ok(Self { embed_tokens, layers, norm, rotary_emb, dtype: vb.dtype(), sliding_window, device: vb.device().clone(), rope_scaling, }) } fn prepare_causal_attention_mask( &self, b_size: usize, tgt_len: usize, seqlen_offset: usize, ) -> Result { // Sliding window mask? let mask: Vec<_> = (0..tgt_len) .flat_map(|i| { (0..tgt_len).map(move |j| { if i < j || j + self.sliding_window < i { f32::NEG_INFINITY } else { 0. } }) }) .collect(); let mask = Tensor::from_slice(&mask, (tgt_len, tgt_len), &self.device)?; let mask = if seqlen_offset > 0 { let mask0 = Tensor::zeros((tgt_len, seqlen_offset), self.dtype, &self.device)?; Tensor::cat(&[&mask0, &mask], D::Minus1)? } else { mask }; let mask = mask .expand((b_size, 1, tgt_len, tgt_len + seqlen_offset))? .to_dtype(self.dtype)?; Ok(mask) } pub fn forward( &mut self, inputs_embeds: &Tensor, seqlen_offset: usize, position_ids: Option<&Tensor>, ) -> Result { let (b_size, seq_len, _) = inputs_embeds.dims3()?; let position_ids = match position_ids { Some(ids) => ids.clone(), None => Tensor::arange( seqlen_offset as u32, (seq_len + seqlen_offset) as u32, inputs_embeds.device(), )? .unsqueeze(0)? .unsqueeze(0)? .broadcast_as((3, b_size, seq_len))?, }; let (cos, sin) = self.rotary_emb.forward( &position_ids, self.dtype, self.rope_scaling.mrope_section.clone(), )?; let mut xs = inputs_embeds.clone(); let attention_mask: Option<&Tensor> = { if seq_len <= 1 { None } else { Some(&self.prepare_causal_attention_mask(b_size, seq_len, seqlen_offset)?) } }; for layer in self.layers.iter_mut() { xs = layer.forward(&xs, &cos, &sin, attention_mask)?; } let xs = xs.apply(&self.norm)?; Ok(xs) } pub fn clear_kv_cache(&mut self) { for layer in self.layers.iter_mut() { layer.clear_kv_cache() } } } pub struct Qwen2_5VLModel { visual: Qwen2_5VLVisionModel, model: Qwen2_5VLTextModel, pub cfg: Qwen2_5VLConfig, lm_head: Linear, rope_deltas: Option, } impl Qwen2_5VLModel { pub fn new(cfg: Qwen2_5VLConfig, vb: VarBuilder) -> Result { let visual = Qwen2_5VLVisionModel::new(&cfg, vb.pp("visual"))?; let model = Qwen2_5VLTextModel::new(&cfg, vb.pp("model"))?; let vocab_size = cfg.vocab_size; let lm_head = if cfg.tie_word_embeddings { Linear::new(model.embed_tokens.embeddings().clone(), None) } else { linear_no_bias(cfg.hidden_size, vocab_size, vb.pp("lm_head"))? }; Ok(Self { visual, model, cfg, lm_head, rope_deltas: None, }) } pub fn get_rope_index( &self, input_ids: &Tensor, image_grid_thw: Option<&Tensor>, video_grid_thw: Option<&Tensor>, mask: Option<&Tensor>, second_per_grid_ts: Option>, ) -> Result<(Tensor, Tensor)> { let spatial_merge_size = self.cfg.vision_config.spatial_merge_size; let mut mrope_position_deltas: Vec = Vec::new(); if image_grid_thw.is_some() || video_grid_thw.is_some() { let total_input_ids = input_ids.clone(); let mut mask_; if mask.is_none() { mask_ = Tensor::ones_like(&total_input_ids)?; } else { mask_ = mask.unwrap().clone(); } let mut position_ids = Tensor::ones( (3, input_ids.dim(0)?, input_ids.dim(1)?), input_ids.dtype(), input_ids.device(), )?; let mut image_index = 0; let mut video_index = 0; for i in 0..total_input_ids.dim(0)? { let mut input_ids_i = total_input_ids.i(i)?; let mask_i = mask_.i(i)?; // 推理时, attention_mask如果是全1向量,取非0索引的操作没必要 if mask_i.sum_all()?.to_scalar::()? != mask_i.dim(0)? as u32 { let nonzero_idx = nonzero_index(&mask_i)?; input_ids_i = input_ids_i.gather(&nonzero_idx, 0)?; } let mut text_start = 0; let mut text_end = 0; let mut thw = vec![]; let mut second_per_grid_t = 0_f32; let mut llm_pos_ids_list: Vec = Vec::new(); // vision start的下一个索引 let vision_indices = get_vision_next_indices(&input_ids_i, self.cfg.vision_start_token_id as u32); match vision_indices { Ok(indeices) => { let vision_tokens = input_ids_i.gather(&indeices, 0)?.to_vec1::()?; let vision_indices_vec = indeices.to_vec1::()?; for (j, &token) in vision_tokens.iter().enumerate() { if token == self.cfg.image_token_id as u32 { thw = image_grid_thw.unwrap().i(image_index)?.to_vec1::()?; image_index += 1; text_end = vision_indices_vec[j]; second_per_grid_t = 0.0; } if token == self.cfg.video_token_id as u32 { thw = video_grid_thw.unwrap().i(video_index)?.to_vec1::()?; text_end = vision_indices_vec[j]; second_per_grid_t = match second_per_grid_ts { None => 1.0, Some(ref vec) => vec[video_index], }; video_index += 1; } let llm_grid_t = thw[0]; let llm_grid_h = thw[1] / spatial_merge_size as u32; let llm_grid_w = thw[2] / spatial_merge_size as u32; let text_len = text_end - text_start; let start_idx = if llm_pos_ids_list.len() > 0 { llm_pos_ids_list[llm_pos_ids_list.len() - 1] .max_all()? .to_scalar::()? + 1 } else { 0 }; let pos_ids = Tensor::arange( start_idx, start_idx + text_len, input_ids_i.device(), )? .unsqueeze(0)? .broadcast_as((3usize, text_len as usize))?; llm_pos_ids_list.push(pos_ids); let range_tensor = Tensor::arange(0, llm_grid_t, input_ids_i.device())? .unsqueeze(D::Minus1)?; let expanded_range = range_tensor.broadcast_as(( llm_grid_t as usize, (llm_grid_h * llm_grid_w) as usize, ))?; let time_tensor = expanded_range .broadcast_mul(&Tensor::new( vec![ (second_per_grid_t * self.cfg.vision_config.tokens_per_second as f32) as u32, ], input_ids_i.device(), )?)? .broadcast_add(&Tensor::new( vec![start_idx + text_len], input_ids_i.device(), )?)?; let t_index = time_tensor.flatten_all()?; let h_index = Tensor::arange( start_idx + text_len, start_idx + text_len + llm_grid_h, input_ids_i.device(), )? .unsqueeze(0)? .unsqueeze(D::Minus1)? .broadcast_as(( llm_grid_t as usize, llm_grid_h as usize, llm_grid_w as usize, ))? .flatten_all()?; let w_index = Tensor::arange( start_idx + text_len, start_idx + text_len + llm_grid_w, input_ids_i.device(), )? .unsqueeze(0)? .unsqueeze(0)? .broadcast_as(( llm_grid_t as usize, llm_grid_h as usize, llm_grid_w as usize, ))? .flatten_all()?; let thw_index = Tensor::stack(&[t_index, h_index, w_index], 0)?; llm_pos_ids_list.push(thw_index); text_start = text_end + llm_grid_t * llm_grid_h * llm_grid_w; } } Err(e) => { println!("get vision_indices err: {}", e); } }; if text_start < input_ids_i.dim(0)? as u32 { let start_idx = if llm_pos_ids_list.len() > 0 { llm_pos_ids_list[llm_pos_ids_list.len() - 1] .max_all()? .to_scalar::()? + 1 } else { 0 }; let text_len = input_ids_i.dim(0)? as u32 - text_start; let pos_ids = Tensor::arange(start_idx, start_idx + text_len, input_ids_i.device())? .unsqueeze(0)? .broadcast_as((3usize, text_len as usize))?; llm_pos_ids_list.push(pos_ids); } let llm_position = Tensor::cat(&llm_pos_ids_list, 1)?.reshape((3, 1, ()))?; position_ids = position_ids .slice_assign(&[(0..3), (i..i + 1), (0..input_ids.dim(1)?)], &llm_position)?; let position_deltas = llm_position.max_all()?.to_scalar::()? as i64 + 1 - input_ids_i.dim(0)? as i64; mrope_position_deltas.push(position_deltas); } let mut mrope_position_deltas = Tensor::new(mrope_position_deltas, input_ids.device())?; if mrope_position_deltas.rank() == 1 { mrope_position_deltas = mrope_position_deltas.unsqueeze(0)?; } return Ok((position_ids.contiguous()?, mrope_position_deltas)); } else { if mask.is_some() { let mut position_ids = mask .unwrap() .to_dtype(candle_core::DType::F64)? .cumsum(D::Minus1)? .to_dtype(candle_core::DType::U32)? .broadcast_sub(&Tensor::new(vec![1_u32], input_ids.device())?)?; for i in 0..position_ids.dim(0)? { let mut position_ids_i = position_ids.i(i)?; let mask_i = mask.unwrap().i(i)?; // 如果有pad, 将填充位置置为1 // 当bs>1, 可能存在不同序列长度,需要添加pad使seq_len长度一致 if mask_i.sum_all()?.to_scalar::()? != mask_i.dim(0)? as u32 { let zero_indices = zero_index(&mask_i)?; let replace_1 = Tensor::ones( zero_indices.dim(0)?, candle_core::DType::U32, input_ids.device(), )?; position_ids_i = position_ids_i .scatter(&zero_indices, &replace_1, 0)? .unsqueeze(0)?; position_ids = position_ids.slice_assign( &[(i..i + 1), (0..position_ids.dim(1)?)], &position_ids_i, )?; } } position_ids = position_ids .unsqueeze(0)? .broadcast_as((3, input_ids.dim(0)?, input_ids.dim(1)?))? .contiguous()?; let mut mrope_position_deltas = position_ids .max(0)? .max(D::Minus1)? .broadcast_sub(&Tensor::new( vec![mask.unwrap().dim(D::Minus1)? as u32 - 1], input_ids.device(), )?)? .contiguous()?; if mrope_position_deltas.rank() == 1 { mrope_position_deltas = mrope_position_deltas.unsqueeze(0)?; } return Ok((position_ids, mrope_position_deltas)); } else { let position_ids = Tensor::arange(0_u32, input_ids.dim(D::Minus1)? as u32, input_ids.device())? .unsqueeze(0)? .unsqueeze(0)? .broadcast_as((3, input_ids.dim(0)?, input_ids.dim(D::Minus1)?))? .contiguous()?; let mrope_position_deltas = Tensor::zeros( (input_ids.dim(0)?, 1), input_ids.dtype(), input_ids.device(), )?; Ok((position_ids, mrope_position_deltas)) } } } pub fn forward( &mut self, input_ids: &Tensor, pixel_values: Option<&Tensor>, image_grid_thw: Option<&Tensor>, pixel_values_video: Option<&Tensor>, video_grid_thw: Option<&Tensor>, mask: &Tensor, cache_position: Option<&Tensor>, seqlen_offset: usize, second_per_grid_ts: Option>, ) -> Result { // input_ids shape: (bs, seq_len) let mut inputs_embeds = self.model.embed_tokens.forward(&input_ids)?; // inputs_embeds shape: (bs, seq_len, hidden_dim) if pixel_values.is_some() && image_grid_thw.is_some() { // image_embed shape: (seq_len, hidden_dim) let image_embed = self .visual .forward(pixel_values.unwrap(), image_grid_thw.unwrap())?; let vision_mask = get_equal_mask(&input_ids, self.cfg.image_token_id as u32)?; let n_image_tokens = vision_mask.sum_all()?.to_scalar::()?; if n_image_tokens as usize != image_embed.dim(0)? { return Err(anyhow!(format!( "n_image_token num: {} not equal to image_embed len: {}", n_image_tokens, image_embed.dim(0)? ))); } inputs_embeds = masked_scatter_dim0(&inputs_embeds, &image_embed, &vision_mask)?; } if pixel_values_video.is_some() && video_grid_thw.is_some() { let video_embed = self .visual .forward(pixel_values_video.unwrap(), video_grid_thw.unwrap())?; let vision_mask = get_equal_mask(&input_ids, self.cfg.video_token_id as u32)?; let n_video_tokens = vision_mask.sum_all()?.to_scalar::()?; if n_video_tokens as usize != video_embed.dim(0)? { return Err(anyhow!(format!( "n_image_token num: {} not equal to image_embed len: {}", n_video_tokens, video_embed.dim(0)? ))); } inputs_embeds = masked_scatter_dim0(&inputs_embeds, &video_embed, &vision_mask)?; } let mut position_ids; let mut rope_deltas; if (cache_position.is_some() && cache_position.unwrap().i(0)?.to_scalar::()? == 0) || self.rope_deltas.is_none() { (position_ids, rope_deltas) = self.get_rope_index( input_ids, image_grid_thw, video_grid_thw, Some(mask), second_per_grid_ts, )?; self.rope_deltas = Some(rope_deltas); } else { let (bs, seq_len, _) = inputs_embeds.dims3()?; let delta = if cache_position.is_some() { cache_position .unwrap() .i(0)? .to_dtype(self.rope_deltas.as_ref().unwrap().dtype())? .broadcast_add(&self.rope_deltas.as_ref().unwrap())? .contiguous()? .to_dtype(candle_core::DType::U32)? } else { Tensor::zeros(1, inputs_embeds.dtype(), inputs_embeds.device())? }; position_ids = Tensor::arange(0u32, seq_len as u32, input_ids.device())? .unsqueeze(0)? .broadcast_as((bs, seq_len))? .broadcast_add(&delta)? .unsqueeze(0)? .broadcast_as((3, bs, seq_len))? .contiguous()?; } let outputs = self .model .forward(&inputs_embeds, seqlen_offset, Some(&position_ids))?; let seq_len = outputs.dim(1)?; let hidden_state = outputs.narrow(1, seq_len - 1, 1)?; let logits = self.lm_head.forward(&hidden_state)?; Ok(logits) } pub fn clear_kv_cache(&mut self) { self.model.clear_kv_cache(); } }