Files
aha/src/models/qwen2_5vl/model.rs
T
2025-09-25 12:09:25 +08:00

1211 lines
48 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Self> {
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<Tensor> {
// 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<Self> {
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<Tensor> {
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<Self> {
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<Tensor> {
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<Self> {
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<Tensor> {
// 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<Self> {
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<Tensor> {
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<usize>,
window_size: usize,
spatial_merge_unit: usize,
patch_embed: Qwen2_5VisionPatchEmbed,
rotary_pos_emb: Qwen2_5VisionRotaryEmbedding,
blocks: Vec<Qwen2_5VLVisionBlock>,
merger: Qwen2_5VLPatchMerger,
dtype: DType,
}
impl Qwen2_5VLVisionModel {
pub fn new(cfg: &Qwen2_5VLConfig, vb: VarBuilder) -> Result<Self> {
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<Tensor> {
let mut pos_ids = Vec::new();
for i in 0..grid_thw.dim(0)? {
let [t, h, w] = grid_thw.i(i)?.to_vec1::<u32>()?[..] 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::<u32>()?;
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::<u32>()?[..] 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::<u8>()?;
let indices: Vec<u32> = 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::<i64>()?);
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<Tensor> {
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::<u32>()? as usize;
let end = cu_seqlens.i(i)?.to_scalar::<u32>()? 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<Tensor> {
// 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::<u32>()?;
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<Self> {
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<Tensor> {
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<Self> {
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<Tensor> {
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<Self> {
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<Tensor> {
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<Qwen2_5VLTextDecoderLayer>,
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<Self> {
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<Tensor> {
// 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<Tensor> {
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<Tensor>,
}
impl Qwen2_5VLModel {
pub fn new(cfg: Qwen2_5VLConfig, vb: VarBuilder) -> Result<Self> {
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<Vec<f32>>,
) -> Result<(Tensor, Tensor)> {
let spatial_merge_size = self.cfg.vision_config.spatial_merge_size;
let mut mrope_position_deltas: Vec<i64> = 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::<u32>()? != 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<Tensor> = 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::<u32>()?;
let vision_indices_vec = indeices.to_vec1::<u32>()?;
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::<u32>()?;
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::<u32>()?;
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::<u32>()?
+ 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::<u32>()?
+ 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::<u32>()? 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::<u32>()? != 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<Vec<f32>>,
) -> Result<Tensor> {
// 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::<u32>()?;
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::<u32>()?;
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::<u32>()? == 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();
}
}