use anyhow::{Result, anyhow}; use candle_core::{D, IndexOp, Shape, Tensor}; use candle_nn::{ Conv2d, Embedding, LayerNorm, Linear, Module, RmsNorm, VarBuilder, embedding, linear, linear_no_bias, rms_norm, }; use num::integer::Roots; use crate::{ models::{ common::{ NaiveAttnGateUpDownMLPBlock, NaiveAttnTwoLinearMLPBlock, get_conv2d, get_layer_norm, }, paddleocr_vl::config::{ PaddleOCRVLConfig, PaddleOCRVLRopeScalingConfig, PaddleOCRVLVisionConfig, }, }, position_embed::rope::{Qwen2_5VLTextRotaryEmbedding, Qwen2_5VisionRotaryEmbedding}, utils::tensor_utils::{ get_vision_next_indices, interpolate_bilinear, masked_scatter_dim0, nonzero_index, prepare_causal_attention_mask, zero_index, }, }; pub struct Projector { merge_size: usize, pre_norm: LayerNorm, linear_1: Linear, linear_2: Linear, } impl Projector { pub fn new(vb: VarBuilder, config: &PaddleOCRVLConfig) -> Result { let merge_size = config.vision_config.spatial_merge_size; let hidden_size = config.vision_config.hidden_size * merge_size * merge_size; let pre_norm = get_layer_norm( vb.pp("pre_norm"), config.rms_norm_eps, config.vision_config.hidden_size, )?; let linear_1 = linear(hidden_size, hidden_size, vb.pp("linear_1"))?; let linear_2 = linear(hidden_size, config.hidden_size, vb.pp("linear_2"))?; Ok(Self { merge_size, pre_norm, linear_1, linear_2, }) } pub fn forward(&self, xs: &Tensor, image_grid_thw: &Tensor) -> Result { let img_num = image_grid_thw.dim(0)?; let mut processed_features = vec![]; let start = 0usize; for i in 0..img_num { let [t, h, w] = image_grid_thw.i(i)?.to_vec1::()?[..] else { return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); }; let end = start + (t * h * w) as usize; let xs_i = xs.i((start..end, ..))?; let xs_i = self.pre_norm.forward(&xs_i)?; let dim = xs_i.dim(1)?; let shape = Shape::from(vec![ t as usize, h as usize / self.merge_size, self.merge_size, w as usize / self.merge_size, self.merge_size, dim, ]); let xs_i = xs_i .reshape((t as usize, h as usize, w as usize, dim))? .reshape(shape)? .permute((0, 1, 3, 2, 4, 5))? .reshape(( (t * h * w) as usize / self.merge_size / self.merge_size, self.merge_size * self.merge_size * dim, ))?; let xs_i = self.linear_1.forward(&xs_i)?.gelu()?; let xs_i = self.linear_2.forward(&xs_i)?; processed_features.push(xs_i); } let xs = Tensor::cat(&processed_features, 0)?; Ok(xs) } } pub struct SiglipVisionEmbeddings { embed_dim: usize, patch_size: usize, patch_embedding: Conv2d, num_positions: usize, position_embedding: Embedding, packing_position_embedding: Embedding, } impl SiglipVisionEmbeddings { pub fn new(vb: VarBuilder, config: &PaddleOCRVLVisionConfig) -> Result { let embed_dim = config.hidden_size; let image_size = config.image_size; let patch_size = config.patch_size; let patch_embedding = get_conv2d( vb.pp("patch_embedding"), config.num_channels, embed_dim, patch_size, 0, patch_size, 1, 1, true, )?; let num_positions = (image_size / patch_size).pow(2); let position_embedding = embedding(num_positions, embed_dim, vb.pp("position_embedding"))?; let packing_position_embedding = embedding(32768, embed_dim, vb.pp("packing_position_embedding"))?; Ok(Self { embed_dim, patch_size, patch_embedding, num_positions, position_embedding, packing_position_embedding, }) } fn interpolate_pos_encoding( &self, h: usize, w: usize, is_after_patchify: bool, ) -> Result { let (new_height, new_width) = if is_after_patchify { (h, w) } else { (h / self.patch_size, w / self.patch_size) }; let sqrt_num_positions = self.num_positions.sqrt(); let patch_pos_embed = self .position_embedding .embeddings() .reshape((1, sqrt_num_positions, sqrt_num_positions, self.embed_dim))? .permute((0, 3, 1, 2))?; let patch_pos_embed = interpolate_bilinear(&patch_pos_embed, (new_height, new_width), Some(false))?; let patch_pos_embed = patch_pos_embed .permute((0, 2, 3, 1))? .reshape((1, (), self.embed_dim))?; Ok(patch_pos_embed) } pub fn forward( &self, pixel_values: &Tensor, position_ids: &Tensor, image_grid_thw: &Tensor, interpolate_pos_encoding: bool, ) -> Result { let (bs, seq_len, c, h, w) = pixel_values.dims5()?; let pixel_values = pixel_values.reshape((bs * seq_len, c, h, w))?; let patch_embeds = self.patch_embedding.forward(&pixel_values)?; // (bs*seq_len, c) let mut embeddings = patch_embeds.squeeze(D::Minus1)?.squeeze(D::Minus1)?; if interpolate_pos_encoding { let mut tmp_embeddings = vec![]; let img_num = image_grid_thw.dim(0)?; let mut start = 0usize; for i in 0..img_num { let [t, h, w] = image_grid_thw.i(i)?.to_vec1::()?[..] else { return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); }; let end = start + (t * h * w) as usize; let image_embeddings = embeddings.i(start..end)?; let position_embedding = self .interpolate_pos_encoding(h as usize, w as usize, true)? .squeeze(0)? .repeat((t as usize, 1usize))?; let image_embeddings = image_embeddings.add(&position_embedding)?; tmp_embeddings.push(image_embeddings); start = end; } embeddings = Tensor::cat(&tmp_embeddings, 0)?.unsqueeze(0)?; // add bs dim } else { let packing_pos_embed = self.packing_position_embedding.forward(position_ids)?; embeddings = embeddings.add(&packing_pos_embed)?.unsqueeze(0)?; } Ok(embeddings) } } pub struct SiglipEncoder { layers: Vec, rotary_pos_emb: Qwen2_5VisionRotaryEmbedding, } impl SiglipEncoder { pub fn new(vb: VarBuilder, config: &PaddleOCRVLVisionConfig) -> Result { let vb_layers = vb.pp("layers"); let mut layers = vec![]; for i in 0..config.num_hidden_layers { let layer_i = NaiveAttnTwoLinearMLPBlock::new( vb_layers.pp(i), config.hidden_size, config.num_attention_heads, None, None, true, "self_attn", Some("out_proj"), config.intermediate_size, config.hidden_act, true, "mlp", "fc1", "fc2", config.layer_norm_eps, "layer_norm1", "layer_norm2", )?; layers.push(layer_i); } let head_dim = config.hidden_size / config.num_attention_heads; let rotary_pos_emb = Qwen2_5VisionRotaryEmbedding::new(head_dim / 2, Some(10000.0)); Ok(Self { layers, rotary_pos_emb, }) } pub fn forward(&self, xs: &Tensor, image_grid_thw: &Tensor) -> Result { let mut split_hids = vec![]; let mut split_wids = vec![]; for i in 0..image_grid_thw.dim(0)? { let [t, h, w] = image_grid_thw.i(i)?.to_vec1::()?[..] else { return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); }; let pos_w: Vec = (0..h).flat_map(|_| 0u32..w).collect(); let pos_w = pos_w.repeat(t as usize); let pos_w = Tensor::new(pos_w, xs.device())?; let pos_h: Vec = (0..h).flat_map(|h| vec![h; w as usize]).collect(); let pos_h = pos_h.repeat(t as usize); let pos_h = Tensor::new(pos_h, xs.device())?; split_hids.push(pos_h); split_wids.push(pos_w); } let width_position_ids = Tensor::cat(&split_wids, 0)?; let height_position_ids = Tensor::cat(&split_hids, 0)?; let max_grid_size = image_grid_thw.i((.., 1..))?.max_all()?.to_scalar::()?; let rope_emb_max_grid = self .rotary_pos_emb .forward(max_grid_size as usize, xs.device())?; let rotary_pos_emb_h = rope_emb_max_grid.index_select(&height_position_ids, 0)?; let rotary_pos_emb_w = rope_emb_max_grid.index_select(&width_position_ids, 0)?; let rope_emb = Tensor::cat(&[rotary_pos_emb_h, rotary_pos_emb_w], 1)?.contiguous()?; let rope_emb = rope_emb.repeat((1, 2))?; let cos = rope_emb.cos()?; let sin = rope_emb.sin()?; let mut xs = xs.clone(); for layer in &self.layers { xs = layer.forward(&xs, Some(&cos), Some(&sin), None, false)?; } Ok(xs) } } pub struct SiglipVisionModel { embeddings: SiglipVisionEmbeddings, encoder: SiglipEncoder, post_layernorm: LayerNorm, } impl SiglipVisionModel { pub fn new(vb: VarBuilder, config: &PaddleOCRVLVisionConfig) -> Result { let vb = vb.pp("vision_model"); let embeddings = SiglipVisionEmbeddings::new(vb.pp("embeddings"), config)?; let encoder = SiglipEncoder::new(vb.pp("encoder"), config)?; let post_layernorm = get_layer_norm( vb.pp("post_layernorm"), config.layer_norm_eps, config.hidden_size, )?; Ok(Self { embeddings, encoder, post_layernorm, }) } pub fn forward( &self, pixel_values: &Tensor, image_grid_thw: &Tensor, position_ids: &Tensor, interpolate_pos_encoding: bool, ) -> Result { let xs = self.embeddings.forward( pixel_values, position_ids, image_grid_thw, interpolate_pos_encoding, )?; let xs = self.encoder.forward(&xs, image_grid_thw)?; let xs = self.post_layernorm.forward(&xs)?; Ok(xs) } } pub struct Ernie4_5Model { embed_tokens: Embedding, layers: Vec, norm: RmsNorm, rotary_emb: Qwen2_5VLTextRotaryEmbedding, rope_scaling: PaddleOCRVLRopeScalingConfig, } impl Ernie4_5Model { pub fn new(vb: VarBuilder, config: &PaddleOCRVLConfig) -> Result { let embed_tokens = embedding(config.vocab_size, config.hidden_size, vb.pp("embed_tokens"))?; let vb_layers = vb.pp("layers"); let mut layers = vec![]; for i in 0..config.num_hidden_layers { let layer_i = NaiveAttnGateUpDownMLPBlock::new( vb_layers.pp(i), config.hidden_size, config.num_attention_heads, Some(config.num_key_value_heads), Some(config.head_dim), config.use_bias, "self_attn", None, config.intermediate_size, config.hidden_act, config.use_bias, "mlp", config.rms_norm_eps, "input_layernorm", "post_attention_layernorm", )?; layers.push(layer_i); } let norm = rms_norm(config.hidden_size, config.rms_norm_eps, vb.pp("norm"))?; let rotary_emb = Qwen2_5VLTextRotaryEmbedding::new(config.head_dim, config.rope_theta as f32); Ok(Self { embed_tokens, layers, norm, rotary_emb, rope_scaling: config.rope_scaling.clone(), }) } 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, inputs_embeds.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(&prepare_causal_attention_mask( b_size, seq_len, 0, xs.device(), )?) } }; 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 PaddleOCRVLModel { mlp_ar: Projector, visual: SiglipVisionModel, model: Ernie4_5Model, pub cfg: PaddleOCRVLConfig, lm_head: Linear, rope_deltas: Option, } impl PaddleOCRVLModel { pub fn new(cfg: PaddleOCRVLConfig, vb: VarBuilder) -> Result { let mlp_ar = Projector::new(vb.pp("mlp_AR"), &cfg)?; let visual = SiglipVisionModel::new(vb.pp("visual"), &cfg.vision_config)?; let model = Ernie4_5Model::new(vb.pp("model"), &cfg)?; 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 { mlp_ar, 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 mask_ = mask .cloned() .unwrap_or(Tensor::ones_like(&total_input_ids)?); 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); 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 { 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 { 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.is_empty() { 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.is_empty() { 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)?; } Ok((position_ids.contiguous()?, mrope_position_deltas)) } else if let Some(mask) = mask { let mut position_ids = mask .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.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.dim(D::Minus1)? as u32 - 1], input_ids.device(), )?)? .contiguous()?; if mrope_position_deltas.rank() == 1 { mrope_position_deltas = mrope_position_deltas.unsqueeze(0)?; } 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>, image_mask: &Tensor, cache_position: Option<&Tensor>, seqlen_offset: usize, ) -> Result { let mut inputs_embeds = self.model.embed_tokens.forward(input_ids)?; if let Some(pixel_values) = pixel_values && let Some(image_grid_thw) = image_grid_thw { let pixel_values = pixel_values.unsqueeze(0)?; let mut siglip_position_ids = vec![]; let mut sample_indices = vec![]; let mut cu_seqlens = vec![0u32]; let img_num = image_grid_thw.dim(0)?; for i in 0..img_num { let [t, h, w] = image_grid_thw.i(i)?.to_vec1::()?[..] else { return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); }; let numel = h * w; let image_position_ids = Tensor::arange(0, numel, pixel_values.device())?.repeat(t as usize)?; siglip_position_ids.push(image_position_ids); let indices = Tensor::new(vec![i as u32; (numel * t) as usize], pixel_values.device())?; sample_indices.push(indices); cu_seqlens.push(cu_seqlens[cu_seqlens.len() - 1] + numel * t); } let siglip_position_ids = Tensor::cat(&siglip_position_ids, 0)?; let image_embed = self.visual .forward(&pixel_values, image_grid_thw, &siglip_position_ids, true)?; let image_embed = image_embed.squeeze(0)?; let image_embed = self.mlp_ar.forward(&image_embed, image_grid_thw)?; inputs_embeds = masked_scatter_dim0(&inputs_embeds, &image_embed, image_mask)?; } let position_ids; let 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, None, None, None)?; self.rope_deltas = Some(rope_deltas); } else { let (bs, seq_len, _) = inputs_embeds.dims3()?; let delta = if let Some(cache_position) = cache_position { cache_position .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(); } }