add LFM2-1.2B, LFM2.5-1.2B-Instruct
This commit is contained in:
@@ -247,6 +247,8 @@ struct ErrorResponse {
|
||||
fn which_model_to_id(which_model: WhichModel) -> &'static str {
|
||||
match which_model {
|
||||
WhichModel::MiniCPM4_0_5B => "minicpm4-0.5b",
|
||||
WhichModel::LFM2_1_2B => "lfm2-1.2b",
|
||||
WhichModel::LFM2_5_1_2BInstruct => "lfm2.5-1.2b-instruct",
|
||||
WhichModel::Qwen2_5vl3B => "qwen2.5vl-3b",
|
||||
WhichModel::Qwen2_5vl7B => "qwen2.5vl-7b",
|
||||
WhichModel::Qwen3_0_6B => "qwen3-0.6b",
|
||||
@@ -297,6 +299,7 @@ fn which_model_to_owner(which_model: WhichModel) -> &'static str {
|
||||
WhichModel::VoxCPM | WhichModel::VoxCPM1_5 => "OpenBMB",
|
||||
WhichModel::GlmASRNano2512 | WhichModel::GlmOCR => "ZhipuAI",
|
||||
WhichModel::FunASRNano2512 => "FunAudioLLM",
|
||||
WhichModel::LFM2_1_2B | WhichModel::LFM2_5_1_2BInstruct => "LiquidAI",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//! MiniCPM4-0.5B exec implementation for CLI `run` subcommand
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::{Ok, Result};
|
||||
|
||||
use crate::exec::ExecModel;
|
||||
use crate::models::GenerateModel;
|
||||
use crate::models::lfm2::generate::Lfm2GenerateModel;
|
||||
use crate::utils::get_file_path;
|
||||
|
||||
pub struct Lfm2Exec;
|
||||
|
||||
impl ExecModel for Lfm2Exec {
|
||||
fn run(input: &[String], output: Option<&str>, weight_path: &str) -> Result<()> {
|
||||
let input_text = &input[0];
|
||||
let target_text = if input_text.starts_with("file://") {
|
||||
// let path = &input[7..];
|
||||
let path = get_file_path(input_text)?;
|
||||
std::fs::read_to_string(path)?
|
||||
} else {
|
||||
input_text.to_string()
|
||||
};
|
||||
|
||||
let i_start = Instant::now();
|
||||
let mut model = Lfm2GenerateModel::init(weight_path, None, None)?;
|
||||
let i_duration = i_start.elapsed();
|
||||
println!("Time elapsed in load model is: {:?}", i_duration);
|
||||
|
||||
let message = format!(
|
||||
r#"{{
|
||||
"temperature": 0.3,
|
||||
"top_p": 0.8,
|
||||
"model": "lfm2",
|
||||
"messages": [
|
||||
{{
|
||||
"role": "user",
|
||||
"content": "{}"
|
||||
}}
|
||||
]
|
||||
}}"#,
|
||||
target_text.replace('"', "\\\"")
|
||||
);
|
||||
let mes = serde_json::from_str(&message)?;
|
||||
|
||||
let i_start = Instant::now();
|
||||
let result = model.generate(mes)?;
|
||||
let i_duration = i_start.elapsed();
|
||||
println!("Time elapsed in generate is: {:?}", i_duration);
|
||||
|
||||
// Print result
|
||||
println!("Result: {:?}", result);
|
||||
|
||||
if let Some(out) = output {
|
||||
std::fs::write(out, format!("{:?}", result))?;
|
||||
println!("Output saved to: {}", out);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ pub mod qwen3vl;
|
||||
pub mod rmbg2_0;
|
||||
pub mod voxcpm;
|
||||
pub mod voxcpm1_5;
|
||||
pub mod lfm2;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
|
||||
+10
@@ -244,6 +244,8 @@ struct ModelInfo {
|
||||
fn run_list(args: ListArgs) -> anyhow::Result<()> {
|
||||
let models = [
|
||||
WhichModel::MiniCPM4_0_5B,
|
||||
WhichModel::LFM2_1_2B,
|
||||
WhichModel::LFM2_5_1_2BInstruct,
|
||||
WhichModel::Qwen2_5vl3B,
|
||||
WhichModel::Qwen2_5vl7B,
|
||||
WhichModel::Qwen3_0_6B,
|
||||
@@ -461,6 +463,14 @@ fn run_run(args: RunArgs) -> anyhow::Result<()> {
|
||||
use aha::exec::minicpm4::MiniCPM4Exec;
|
||||
MiniCPM4Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||
}
|
||||
WhichModel::LFM2_1_2B => {
|
||||
use aha::exec::lfm2::Lfm2Exec;
|
||||
Lfm2Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||
}
|
||||
WhichModel::LFM2_5_1_2BInstruct => {
|
||||
use aha::exec::lfm2::Lfm2Exec;
|
||||
Lfm2Exec::run(&input, output.as_deref(), &weight_path)?;
|
||||
}
|
||||
WhichModel::Qwen2_5vl3B => {
|
||||
use aha::exec::qwen2_5vl::Qwen2_5vlExec;
|
||||
Qwen2_5vlExec::run(&input, output.as_deref(), &weight_path)?;
|
||||
|
||||
@@ -411,6 +411,145 @@ impl QKVCatAttention {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QKNormAttention {
|
||||
q_proj: Linear,
|
||||
k_proj: Linear,
|
||||
v_proj: Linear,
|
||||
o_proj: Linear,
|
||||
q_norm: RmsNorm,
|
||||
k_norm: RmsNorm,
|
||||
num_attention_heads: usize,
|
||||
num_key_value_heads: usize,
|
||||
num_kv_groups: usize,
|
||||
head_dim: usize,
|
||||
scaling: f64,
|
||||
kv_cache: Option<(Tensor, Tensor)>,
|
||||
}
|
||||
|
||||
impl QKNormAttention {
|
||||
pub fn new(
|
||||
vb: VarBuilder,
|
||||
hidden_size: usize,
|
||||
num_attention_heads: usize,
|
||||
head_dim: Option<usize>,
|
||||
num_key_value_heads: Option<usize>,
|
||||
attention_bias: bool,
|
||||
rms_norm_eps: f64,
|
||||
q_proj_pp_name: Option<&str>,
|
||||
k_proj_pp_name: Option<&str>,
|
||||
v_proj_pp_name: Option<&str>,
|
||||
o_proj_pp_name: Option<&str>,
|
||||
q_norm_pp_name: Option<&str>,
|
||||
k_norm_pp_name: Option<&str>,
|
||||
) -> Result<Self> {
|
||||
let head_dim = head_dim.unwrap_or(hidden_size / num_attention_heads);
|
||||
let num_key_value_heads = num_key_value_heads.unwrap_or(num_attention_heads);
|
||||
let num_kv_groups = num_attention_heads / num_key_value_heads;
|
||||
let scaling = 1f64 / f64::sqrt(head_dim as f64);
|
||||
let q_proj_pp_name = q_proj_pp_name.unwrap_or("q_proj");
|
||||
let k_proj_pp_name = k_proj_pp_name.unwrap_or("k_proj");
|
||||
let v_proj_pp_name = v_proj_pp_name.unwrap_or("v_proj");
|
||||
let o_proj_pp_name = o_proj_pp_name.unwrap_or("o_proj");
|
||||
let q_norm_pp_name = q_norm_pp_name.unwrap_or("q_norm");
|
||||
let k_norm_pp_name = k_norm_pp_name.unwrap_or("k_norm");
|
||||
let q_proj = linear_b(
|
||||
hidden_size,
|
||||
num_attention_heads * head_dim,
|
||||
attention_bias,
|
||||
vb.pp(q_proj_pp_name),
|
||||
)?;
|
||||
let k_proj = linear_b(
|
||||
hidden_size,
|
||||
num_key_value_heads * head_dim,
|
||||
attention_bias,
|
||||
vb.pp(k_proj_pp_name),
|
||||
)?;
|
||||
let v_proj = linear_b(
|
||||
hidden_size,
|
||||
num_key_value_heads * head_dim,
|
||||
attention_bias,
|
||||
vb.pp(v_proj_pp_name),
|
||||
)?;
|
||||
let o_proj = linear_b(
|
||||
num_attention_heads * head_dim,
|
||||
hidden_size,
|
||||
attention_bias,
|
||||
vb.pp(o_proj_pp_name),
|
||||
)?;
|
||||
let q_norm = rms_norm(head_dim, rms_norm_eps, vb.pp(q_norm_pp_name))?;
|
||||
let k_norm = rms_norm(head_dim, rms_norm_eps, vb.pp(k_norm_pp_name))?;
|
||||
Ok(Self {
|
||||
q_proj,
|
||||
k_proj,
|
||||
v_proj,
|
||||
o_proj,
|
||||
q_norm,
|
||||
k_norm,
|
||||
num_attention_heads,
|
||||
num_key_value_heads,
|
||||
num_kv_groups,
|
||||
head_dim,
|
||||
scaling,
|
||||
kv_cache: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub 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)?.reshape((
|
||||
b_sz,
|
||||
q_len,
|
||||
self.num_attention_heads,
|
||||
self.head_dim,
|
||||
))?;
|
||||
let query_states = self.q_norm.forward(&query_states)?.transpose(1, 2)?;
|
||||
let key_states = self.k_proj.forward(xs)?.reshape((
|
||||
b_sz,
|
||||
q_len,
|
||||
self.num_key_value_heads,
|
||||
self.head_dim,
|
||||
))?;
|
||||
let key_states = self.k_norm.forward(&key_states)?.transpose(1, 2)?;
|
||||
let value_states = self.v_proj.forward(xs)?;
|
||||
let value_states = value_states
|
||||
.reshape((b_sz, q_len, self.num_key_value_heads, self.head_dim))?
|
||||
.transpose(1, 2)?;
|
||||
let (query_states, key_states) =
|
||||
apply_rotary_pos_emb(&query_states, &key_states, cos, sin, false)?;
|
||||
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 attn_output = eager_attention_forward(
|
||||
&query_states,
|
||||
&key_states,
|
||||
&value_states,
|
||||
Some(self.num_kv_groups),
|
||||
attention_mask,
|
||||
self.scaling,
|
||||
)?;
|
||||
let attn_output =
|
||||
attn_output.reshape((b_sz, q_len, self.num_attention_heads * self.head_dim))?;
|
||||
let attn_output = attn_output.apply(&self.o_proj)?;
|
||||
Ok(attn_output)
|
||||
}
|
||||
|
||||
pub fn clear_kv_cache(&mut self) {
|
||||
self.kv_cache = None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NaiveAttnTwoLinearMLPBlock {
|
||||
self_attn: NaiveAttention,
|
||||
mlp: TwoLinearMLP,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use anyhow::{anyhow, Result};
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Lfm2Config {
|
||||
pub architectures: Vec<String>,
|
||||
pub block_auto_adjust_ff_dim: bool,
|
||||
pub block_dim: usize,
|
||||
pub block_ff_dim: usize,
|
||||
pub block_ffn_dim_multiplier: f64,
|
||||
pub block_mlp_init_scale: f64,
|
||||
pub block_multiple_of: usize,
|
||||
pub block_norm_eps: f64,
|
||||
pub block_out_init_scale: f64,
|
||||
pub block_use_swiglu: bool,
|
||||
pub block_use_xavier_init: bool,
|
||||
pub bos_token_id: u32,
|
||||
#[serde[rename="conv_L_cache"]]
|
||||
pub conv_l_cache: usize,
|
||||
pub conv_bias: bool,
|
||||
pub conv_dim: usize,
|
||||
pub conv_dim_out: Option<usize>,
|
||||
pub conv_use_xavier_init: bool,
|
||||
pub eos_token_id: u32,
|
||||
pub full_attn_idxs: Option<Vec<usize>>,
|
||||
pub layer_types: Option<Vec<String>>,
|
||||
pub hidden_size: usize,
|
||||
pub initializer_range: f64,
|
||||
pub intermediate_size: Option<usize>,
|
||||
pub max_position_embeddings: usize,
|
||||
pub model_type: String,
|
||||
pub norm_eps: f64,
|
||||
pub num_attention_heads: usize,
|
||||
pub num_heads: usize,
|
||||
pub num_hidden_layers: usize,
|
||||
pub num_key_value_heads: usize,
|
||||
pub pad_token_id: u32,
|
||||
pub rope_theta: f32,
|
||||
pub torch_dtype: Option<String>,
|
||||
pub dtype: Option<String>,
|
||||
pub use_cache: bool,
|
||||
pub use_pos_enc: bool,
|
||||
pub vocab_size: usize,
|
||||
pub tie_embedding: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
impl Lfm2Config {
|
||||
pub fn full_attn_idx2layer_type(&mut self) {
|
||||
if self.layer_types.is_none()
|
||||
&& let Some(idx) = &self.full_attn_idxs
|
||||
{
|
||||
let mut layer_types = vec![];
|
||||
for i in 0..self.num_hidden_layers {
|
||||
if idx.contains(&i) {
|
||||
layer_types.push("full_attention".to_string());
|
||||
} else {
|
||||
layer_types.push("conv".to_string());
|
||||
}
|
||||
}
|
||||
self.layer_types = Some(layer_types);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_layer_types(&self) -> Result<Vec<String>> {
|
||||
if let Some(types) = &self.layer_types {
|
||||
Ok(types.clone())
|
||||
} else if let Some(idx) = &self.full_attn_idxs {
|
||||
let mut layer_types = vec![];
|
||||
for i in 0..self.num_hidden_layers {
|
||||
if idx.contains(&i) {
|
||||
layer_types.push("full_attention".to_string());
|
||||
} else {
|
||||
layer_types.push("conv".to_string());
|
||||
}
|
||||
}
|
||||
Ok(layer_types)
|
||||
} else {
|
||||
Err(anyhow!("layer_types full_attn_idxs cannot be none at the same time"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Lfm2GenerateConfig {
|
||||
pub bos_token_id: u32,
|
||||
pub eos_token_id: u32,
|
||||
pub pad_token_id: u32
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
use crate::{
|
||||
chat_template::ChatTemplate,
|
||||
models::{GenerateModel, lfm2::{
|
||||
config::{Lfm2Config, Lfm2GenerateConfig},
|
||||
model::Lfm2Model,
|
||||
}},
|
||||
tokenizer::TokenizerModel,
|
||||
utils::{
|
||||
build_completion_response, find_type_files, get_device, get_dtype, get_logit_processor,
|
||||
},
|
||||
};
|
||||
use crate::utils::build_completion_chunk_response;
|
||||
use aha_openai_dive::v1::resources::chat::{ChatCompletionParameters, ChatCompletionResponse};
|
||||
use anyhow::Result;
|
||||
use candle_core::{DType, Device, Tensor};
|
||||
use candle_nn::VarBuilder;
|
||||
use rocket::async_stream::stream;
|
||||
|
||||
pub struct Lfm2GenerateModel<'a> {
|
||||
chat_template: ChatTemplate<'a>,
|
||||
tokenizer: TokenizerModel,
|
||||
device: Device,
|
||||
model: Lfm2Model,
|
||||
eos_token_id: u32,
|
||||
model_name: String,
|
||||
}
|
||||
impl<'a> Lfm2GenerateModel<'a> {
|
||||
pub fn init(path: &str, device: Option<&Device>, dtype: Option<DType>) -> Result<Self> {
|
||||
let chat_template = ChatTemplate::init(path)?;
|
||||
let tokenizer = TokenizerModel::init(path)?;
|
||||
let device = get_device(device);
|
||||
let gen_cfg_path = path.to_string() + "/generation_config.json";
|
||||
let gen_cfg: Lfm2GenerateConfig = serde_json::from_slice(&std::fs::read(gen_cfg_path)?)?;
|
||||
let cfg_path = path.to_string() + "/config.json";
|
||||
let cfg: Lfm2Config = serde_json::from_slice(&std::fs::read(cfg_path)?)?;
|
||||
let model_path = find_type_files(path, "safetensors")?;
|
||||
let cfg_dtype = if let Some(dtype) = &cfg.dtype {
|
||||
dtype.clone()
|
||||
} else if let Some(dtype) = &cfg.torch_dtype {
|
||||
dtype.clone()
|
||||
} else {
|
||||
"bfloat16".to_string()
|
||||
};
|
||||
let dtype = get_dtype(dtype, &cfg_dtype);
|
||||
let vb = unsafe { VarBuilder::from_mmaped_safetensors(&model_path, dtype, &device)? };
|
||||
let model = Lfm2Model::new(vb, &cfg)?;
|
||||
let eos_token_id = gen_cfg.eos_token_id;
|
||||
let model_name = std::path::Path::new(path)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("lfm2")
|
||||
.to_string();
|
||||
Ok(Self {
|
||||
chat_template,
|
||||
tokenizer,
|
||||
device,
|
||||
model,
|
||||
eos_token_id,
|
||||
model_name,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl<'a> GenerateModel for Lfm2GenerateModel<'a> {
|
||||
fn generate(&mut self, mes: ChatCompletionParameters) -> Result<ChatCompletionResponse> {
|
||||
let mes_render = self.chat_template.apply_chat_template(&mes)?;
|
||||
let mut logits = get_logit_processor(
|
||||
mes.temperature,
|
||||
mes.top_p,
|
||||
None,
|
||||
mes.seed.unwrap_or(34562) as u64,
|
||||
);
|
||||
let mut input_ids = self.tokenizer.text_encode(mes_render, &self.device)?;
|
||||
let mut seq_len = input_ids.dim(1)?;
|
||||
let prompt_tokens = seq_len as u32;
|
||||
let mut seqlen_offset = 0;
|
||||
let mut generate = vec![];
|
||||
let sample_len = mes.max_tokens.unwrap_or(1024);
|
||||
for _ in 0..sample_len {
|
||||
let logit = self.model.forward(&input_ids, seqlen_offset)?;
|
||||
let logit = logit.squeeze(0)?.squeeze(0)?;
|
||||
let next_token = logits.sample(&logit)?;
|
||||
generate.push(next_token);
|
||||
if next_token == self.eos_token_id {
|
||||
break;
|
||||
}
|
||||
input_ids = Tensor::new(vec![next_token], &self.device)?.unsqueeze(0)?;
|
||||
seqlen_offset += seq_len;
|
||||
seq_len = 1;
|
||||
}
|
||||
self.model.clear_cache();
|
||||
let completion_tokens = generate.len() as u32;
|
||||
let decode = self.tokenizer.token_decode(generate)?;
|
||||
let mes = build_completion_response(
|
||||
decode,
|
||||
&self.model_name,
|
||||
Some(completion_tokens),
|
||||
Some(prompt_tokens),
|
||||
);
|
||||
Ok(mes)
|
||||
}
|
||||
|
||||
fn generate_stream(
|
||||
&mut self,
|
||||
mes: ChatCompletionParameters,
|
||||
) -> Result<
|
||||
Box<
|
||||
dyn rocket::futures::Stream<Item = Result<aha_openai_dive::v1::resources::chat::ChatCompletionChunkResponse, anyhow::Error>>
|
||||
+ Send
|
||||
+ Unpin
|
||||
+ '_,
|
||||
>,
|
||||
> {
|
||||
let mes_render = self.chat_template.apply_chat_template(&mes)?;
|
||||
let mut logits = get_logit_processor(
|
||||
mes.temperature,
|
||||
mes.top_p,
|
||||
None,
|
||||
mes.seed.unwrap_or(34562) as u64,
|
||||
);
|
||||
let mut input_ids = self.tokenizer.text_encode(mes_render, &self.device)?;
|
||||
let mut seq_len = input_ids.dim(1)?;
|
||||
let mut seqlen_offset = 0;
|
||||
let sample_len = mes.max_tokens.unwrap_or(1024);
|
||||
let stream = stream! {
|
||||
let mut err_tokens = vec![];
|
||||
for _ in 0..sample_len {
|
||||
let logit = self.model.forward(&input_ids, seqlen_offset)?;
|
||||
let logit = logit.squeeze(0)?.squeeze(0)?;
|
||||
let next_token = logits.sample(&logit)?;
|
||||
let mut decode_ids = vec![];
|
||||
if !err_tokens.is_empty() {
|
||||
decode_ids.extend_from_slice(&err_tokens);
|
||||
}
|
||||
decode_ids.push(next_token);
|
||||
let decode = self.tokenizer.token_decode(decode_ids)?;
|
||||
if decode.contains("�") {
|
||||
err_tokens.push(next_token);
|
||||
if err_tokens.len() > 3 {
|
||||
err_tokens.clear();
|
||||
}
|
||||
input_ids = Tensor::new(vec![next_token], &self.device)?.unsqueeze(0)?;
|
||||
seqlen_offset += seq_len;
|
||||
seq_len = 1;
|
||||
continue;
|
||||
}
|
||||
err_tokens.clear();
|
||||
let chunk = build_completion_chunk_response(decode, &self.model_name, None, None);
|
||||
yield Ok(chunk);
|
||||
if next_token == self.eos_token_id {
|
||||
break;
|
||||
}
|
||||
input_ids = Tensor::new(vec![next_token], &self.device)?.unsqueeze(0)?;
|
||||
seqlen_offset += seq_len;
|
||||
seq_len = 1;
|
||||
}
|
||||
self.model.clear_cache();
|
||||
};
|
||||
Ok(Box::new(Box::pin(stream)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod config;
|
||||
pub mod model;
|
||||
pub mod generate;
|
||||
@@ -0,0 +1,313 @@
|
||||
use crate::{
|
||||
models::{
|
||||
common::{GateUpDownMLP, QKNormAttention, conv1d_depthwise, get_conv1d},
|
||||
lfm2::config::Lfm2Config,
|
||||
},
|
||||
position_embed::rope::RoPE,
|
||||
utils::tensor_utils::prepare_causal_attention_mask,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use candle_core::{D, Tensor};
|
||||
use candle_nn::{
|
||||
Conv1d, Embedding, Linear, Module, RmsNorm, VarBuilder, embedding, linear_b, rms_norm,
|
||||
};
|
||||
|
||||
pub struct Lfm2ShortConv {
|
||||
l_cache: usize,
|
||||
conv: Conv1d,
|
||||
in_proj: Linear,
|
||||
out_proj: Linear,
|
||||
cache: Option<Tensor>,
|
||||
}
|
||||
|
||||
impl Lfm2ShortConv {
|
||||
pub fn new(vb: VarBuilder, config: &Lfm2Config) -> Result<Self> {
|
||||
let l_cache = config.conv_l_cache;
|
||||
let bias = config.conv_bias;
|
||||
let conv = get_conv1d(
|
||||
vb.pp("conv"),
|
||||
config.hidden_size,
|
||||
config.hidden_size,
|
||||
l_cache,
|
||||
l_cache - 1,
|
||||
1,
|
||||
1,
|
||||
config.hidden_size,
|
||||
bias,
|
||||
)?;
|
||||
let in_proj = linear_b(
|
||||
config.hidden_size,
|
||||
config.hidden_size * 3,
|
||||
bias,
|
||||
vb.pp("in_proj"),
|
||||
)?;
|
||||
let out_proj = linear_b(
|
||||
config.hidden_size,
|
||||
config.hidden_size,
|
||||
bias,
|
||||
vb.pp("out_proj"),
|
||||
)?;
|
||||
Ok(Self {
|
||||
l_cache,
|
||||
conv,
|
||||
in_proj,
|
||||
out_proj,
|
||||
cache: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn forward(&mut self, xs: &Tensor) -> Result<Tensor> {
|
||||
let seq_len = xs.dim(1)?;
|
||||
let bc_x = self.in_proj.forward(xs)?.transpose(D::Minus1, D::Minus2)?;
|
||||
let chunk = bc_x.chunk(3, D::Minus2)?;
|
||||
let bx = chunk[0].mul(&chunk[2])?;
|
||||
let c: &Tensor = &chunk[1];
|
||||
let conv_out = if self.cache.is_none() && seq_len > 1 {
|
||||
let pad_num = self.l_cache as isize - seq_len as isize;
|
||||
let conv_state = if pad_num > 0 {
|
||||
bx.pad_with_zeros(D::Minus1, pad_num as usize, 0)?
|
||||
} else {
|
||||
bx.narrow(D::Minus1, pad_num.unsigned_abs(), self.l_cache)?
|
||||
};
|
||||
self.cache = Some(conv_state);
|
||||
let bx = bx.pad_with_zeros(D::Minus1, self.l_cache-1, self.l_cache-1)?;
|
||||
let bx = conv1d_depthwise(&bx, self.conv.weight(), self.conv.bias())?;
|
||||
bx.narrow(D::Minus1, 0, seq_len)?
|
||||
} else {
|
||||
let conv_state = self.cache.as_ref().unwrap();
|
||||
let conv_state = Tensor::cat(&[conv_state, &bx], D::Minus1)?;
|
||||
let conv_state = conv_state.narrow(D::Minus1, 1, self.l_cache)?;
|
||||
let conv_out = conv1d_depthwise(&conv_state, self.conv.weight(), self.conv.bias())?;
|
||||
self.cache = Some(conv_state);
|
||||
conv_out
|
||||
};
|
||||
let y = c.mul(&conv_out)?;
|
||||
let y = y.transpose(D::Minus1, D::Minus2)?.contiguous()?;
|
||||
let y = self.out_proj.forward(&y)?;
|
||||
Ok(y)
|
||||
}
|
||||
|
||||
pub fn clear_cache(&mut self) {
|
||||
self.cache = None;
|
||||
}
|
||||
}
|
||||
|
||||
enum LayerKind {
|
||||
SelfAttn(QKNormAttention),
|
||||
Conv(Lfm2ShortConv),
|
||||
}
|
||||
|
||||
impl LayerKind {
|
||||
pub fn forward(
|
||||
&mut self,
|
||||
xs: &Tensor,
|
||||
cos: &Tensor,
|
||||
sin: &Tensor,
|
||||
attention_mask: Option<&Tensor>,
|
||||
) -> Result<Tensor> {
|
||||
match self {
|
||||
LayerKind::SelfAttn(attn) => attn.forward(xs, cos, sin, attention_mask),
|
||||
LayerKind::Conv(conv) => conv.forward(xs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lfm2DecoderLayer {
|
||||
layer: LayerKind,
|
||||
feed_forward: GateUpDownMLP,
|
||||
operator_norm: RmsNorm,
|
||||
ffn_norm: RmsNorm,
|
||||
}
|
||||
|
||||
impl Lfm2DecoderLayer {
|
||||
pub fn new(vb: VarBuilder, config: &Lfm2Config, layer_type: &str) -> Result<Self> {
|
||||
let layer = if layer_type.eq("full_attention") {
|
||||
let attn = QKNormAttention::new(
|
||||
vb.pp("self_attn"),
|
||||
config.hidden_size,
|
||||
config.num_attention_heads,
|
||||
None,
|
||||
Some(config.num_key_value_heads),
|
||||
false,
|
||||
config.block_norm_eps,
|
||||
Some("q_proj"),
|
||||
Some("k_proj"),
|
||||
Some("v_proj"),
|
||||
Some("out_proj"),
|
||||
Some("q_layernorm"),
|
||||
Some("k_layernorm"),
|
||||
)?;
|
||||
LayerKind::SelfAttn(attn)
|
||||
} else {
|
||||
let conv = Lfm2ShortConv::new(vb.pp("conv"), config)?;
|
||||
LayerKind::Conv(conv)
|
||||
};
|
||||
let intermediate_size = if config.block_auto_adjust_ff_dim {
|
||||
let inter_size = 2 * config.block_ff_dim / 3;
|
||||
let inter_size = (config.block_ffn_dim_multiplier * inter_size as f64) as usize;
|
||||
let inter_size = config.block_multiple_of
|
||||
* ((inter_size + config.block_multiple_of - 1) / config.block_multiple_of);
|
||||
inter_size
|
||||
} else {
|
||||
config.block_ff_dim
|
||||
};
|
||||
let feed_forward = GateUpDownMLP::new(
|
||||
vb.pp("feed_forward"),
|
||||
config.hidden_size,
|
||||
intermediate_size,
|
||||
candle_nn::Activation::Silu,
|
||||
false,
|
||||
Some("w1"),
|
||||
Some("w3"),
|
||||
Some("w2"),
|
||||
)?;
|
||||
|
||||
let operator_norm = rms_norm(config.hidden_size, config.norm_eps, vb.pp("operator_norm"))?;
|
||||
let ffn_norm = rms_norm(config.hidden_size, config.norm_eps, vb.pp("ffn_norm"))?;
|
||||
Ok(Self {
|
||||
layer,
|
||||
feed_forward,
|
||||
operator_norm,
|
||||
ffn_norm,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn forward(
|
||||
&mut self,
|
||||
xs: &Tensor,
|
||||
cos: &Tensor,
|
||||
sin: &Tensor,
|
||||
attention_mask: Option<&Tensor>,
|
||||
) -> Result<Tensor> {
|
||||
let res = xs.clone();
|
||||
let xs = self.operator_norm.forward(xs)?;
|
||||
let xs = self.layer.forward(&xs, cos, sin, attention_mask)?;
|
||||
let res = xs.add(&res)?;
|
||||
let xs = self.ffn_norm.forward(&res)?;
|
||||
let xs = self.feed_forward.forward(&xs)?;
|
||||
let xs = xs.add(&res)?;
|
||||
Ok(xs)
|
||||
}
|
||||
|
||||
pub fn clear_cache(&mut self) {
|
||||
match &mut self.layer {
|
||||
LayerKind::SelfAttn(attn) => attn.clear_kv_cache(),
|
||||
LayerKind::Conv(conv) => conv.clear_cache(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lfm2Decoder {
|
||||
embed_tokens: Embedding,
|
||||
layers: Vec<Lfm2DecoderLayer>,
|
||||
// rotary_emb: RoPE,
|
||||
pos_emb: RoPE,
|
||||
embedding_norm: RmsNorm,
|
||||
}
|
||||
|
||||
impl Lfm2Decoder {
|
||||
pub fn new(vb: VarBuilder, config: &Lfm2Config) -> Result<Self> {
|
||||
let embed_tokens = embedding(config.vocab_size, config.hidden_size, vb.pp("embed_tokens"))?;
|
||||
let mut layers = vec![];
|
||||
let vb_layers = vb.pp("layers");
|
||||
// let layer_types = config.layer_types.as_ref().unwrap();
|
||||
let layer_types = config.get_layer_types()?;
|
||||
for i in 0..config.num_hidden_layers {
|
||||
let layer_type = layer_types.get(i).unwrap();
|
||||
let layer = Lfm2DecoderLayer::new(vb_layers.pp(i), config, layer_type)?;
|
||||
layers.push(layer);
|
||||
}
|
||||
let dim = config.hidden_size / config.num_attention_heads;
|
||||
let pos_emb = RoPE::new(dim, config.rope_theta, vb.device())?;
|
||||
let embedding_norm =
|
||||
rms_norm(config.hidden_size, config.norm_eps, vb.pp("embedding_norm"))?;
|
||||
Ok(Self {
|
||||
embed_tokens,
|
||||
layers,
|
||||
pos_emb,
|
||||
embedding_norm,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn forward(
|
||||
&mut self,
|
||||
input_ids: &Tensor,
|
||||
inputs_embeds: Option<&Tensor>,
|
||||
seqlen_offset: usize,
|
||||
) -> Result<Tensor> {
|
||||
let inputs_embeds = if let Some(embed) = inputs_embeds {
|
||||
embed.clone()
|
||||
} else {
|
||||
self.embed_tokens.forward(input_ids)?
|
||||
};
|
||||
|
||||
let (bs, seq_len, _) = inputs_embeds.dims3()?;
|
||||
let attention_mask = if seq_len > 1 {
|
||||
Some(prepare_causal_attention_mask(
|
||||
bs,
|
||||
seq_len,
|
||||
seqlen_offset,
|
||||
inputs_embeds.device(),
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (cos, sin) = self
|
||||
.pos_emb
|
||||
.forward(seqlen_offset, seq_len, inputs_embeds.device())?;
|
||||
let mut xs = inputs_embeds;
|
||||
for layer in &mut self.layers {
|
||||
xs = layer.forward(&xs, &cos, &sin, attention_mask.as_ref())?;
|
||||
}
|
||||
let xs = self.embedding_norm.forward(&xs)?;
|
||||
Ok(xs)
|
||||
}
|
||||
|
||||
pub fn clear_cache(&mut self) {
|
||||
for layer in &mut self.layers {
|
||||
layer.clear_cache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lfm2Model {
|
||||
model: Lfm2Decoder,
|
||||
lm_head: Linear,
|
||||
}
|
||||
|
||||
impl Lfm2Model {
|
||||
pub fn new(vb: VarBuilder, config: &Lfm2Config) -> Result<Self> {
|
||||
let model = Lfm2Decoder::new(vb.pp("model"), config)?;
|
||||
let lm_head = if let Some(flag) = config.tie_embedding
|
||||
&& flag
|
||||
{
|
||||
Linear::new(model.embed_tokens.embeddings().clone(), None)
|
||||
} else {
|
||||
let linear = linear_b(
|
||||
config.hidden_size,
|
||||
config.vocab_size,
|
||||
false,
|
||||
vb.pp("lm_head"),
|
||||
);
|
||||
match linear {
|
||||
Ok(linear) => linear,
|
||||
Err(_) => {
|
||||
Linear::new(model.embed_tokens.embeddings().clone(), None)
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(Self { model, lm_head })
|
||||
}
|
||||
|
||||
pub fn forward(&mut self, input_ids: &Tensor, seqlen_offset: usize) -> Result<Tensor> {
|
||||
let xs = self.model.forward(input_ids, None, seqlen_offset)?;
|
||||
let seq_len = xs.dim(1)?;
|
||||
let last_xs = xs.narrow(1, seq_len - 1, 1)?;
|
||||
let xs = self.lm_head.forward(&last_xs)?;
|
||||
Ok(xs)
|
||||
}
|
||||
|
||||
pub fn clear_cache(&mut self) {
|
||||
self.model.clear_cache();
|
||||
}
|
||||
}
|
||||
+22
-9
@@ -19,6 +19,7 @@ pub mod qwen3vl;
|
||||
pub mod rmbg2_0;
|
||||
pub mod voxcpm;
|
||||
pub mod w2v_bert_2_0;
|
||||
pub mod lfm2;
|
||||
|
||||
use aha_openai_dive::v1::resources::chat::{
|
||||
ChatCompletionChunkResponse, ChatCompletionParameters, ChatCompletionResponse,
|
||||
@@ -27,18 +28,15 @@ use anyhow::{Result, anyhow};
|
||||
use rocket::futures::Stream;
|
||||
|
||||
use crate::models::{
|
||||
deepseek_ocr::generate::DeepseekOCRGenerateModel,
|
||||
fun_asr_nano::generate::FunAsrNanoGenerateModel,
|
||||
glm_asr_nano::generate::GlmAsrNanoGenerateModel, glm_ocr::generate::GlmOcrGenerateModel,
|
||||
hunyuan_ocr::generate::HunyuanOCRGenerateModel, minicpm4::generate::MiniCPMGenerateModel,
|
||||
paddleocr_vl::generate::PaddleOCRVLGenerateModel, qwen2_5vl::generate::Qwen2_5VLGenerateModel,
|
||||
qwen3::generate::Qwen3GenerateModel, qwen3_5::generate::Qwen3_5GenerateModel,
|
||||
qwen3_asr::generate::Qwen3AsrGenerateModel, qwen3vl::generate::Qwen3VLGenerateModel,
|
||||
rmbg2_0::generate::RMBG2_0Model, voxcpm::generate::VoxCPMGenerate,
|
||||
deepseek_ocr::generate::DeepseekOCRGenerateModel, fun_asr_nano::generate::FunAsrNanoGenerateModel, glm_asr_nano::generate::GlmAsrNanoGenerateModel, glm_ocr::generate::GlmOcrGenerateModel, hunyuan_ocr::generate::HunyuanOCRGenerateModel, lfm2::generate::Lfm2GenerateModel, minicpm4::generate::MiniCPMGenerateModel, paddleocr_vl::generate::PaddleOCRVLGenerateModel, qwen2_5vl::generate::Qwen2_5VLGenerateModel, qwen3::generate::Qwen3GenerateModel, qwen3_5::generate::Qwen3_5GenerateModel, qwen3_asr::generate::Qwen3AsrGenerateModel, qwen3vl::generate::Qwen3VLGenerateModel, rmbg2_0::generate::RMBG2_0Model, voxcpm::generate::VoxCPMGenerate
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
|
||||
pub enum WhichModel {
|
||||
#[value(name = "lfm2-1.2b", hide = true)]
|
||||
LFM2_1_2B,
|
||||
#[value(name = "lfm2.5-1.2b-instruct", hide = true)]
|
||||
LFM2_5_1_2BInstruct,
|
||||
#[value(name = "minicpm4-0.5b", hide = true)]
|
||||
MiniCPM4_0_5B,
|
||||
#[value(name = "qwen2.5vl-3b", hide = true)]
|
||||
@@ -97,6 +95,8 @@ impl WhichModel {
|
||||
/// Get the ModelScope model ID for this model variant
|
||||
pub fn model_id(self) -> &'static str {
|
||||
match self {
|
||||
WhichModel::LFM2_1_2B => "LiquidAI/LFM2-1.2B",
|
||||
WhichModel::LFM2_5_1_2BInstruct => "LiquidAI/LFM2.5-1.2B-Instruct",
|
||||
WhichModel::MiniCPM4_0_5B => "OpenBMB/MiniCPM4-0.5B",
|
||||
WhichModel::Qwen2_5vl3B => "Qwen/Qwen2.5-VL-3B-Instruct",
|
||||
WhichModel::Qwen2_5vl7B => "Qwen/Qwen2.5-VL-7B-Instruct",
|
||||
@@ -130,7 +130,9 @@ impl WhichModel {
|
||||
pub fn model_type(self) -> &'static str {
|
||||
match self {
|
||||
// LLM models
|
||||
WhichModel::MiniCPM4_0_5B | WhichModel::Qwen3_0_6B => "llm",
|
||||
WhichModel::MiniCPM4_0_5B | WhichModel::Qwen3_0_6B
|
||||
| WhichModel::LFM2_1_2B
|
||||
| WhichModel::LFM2_5_1_2BInstruct => "llm",
|
||||
WhichModel::Qwen2_5vl3B
|
||||
| WhichModel::Qwen2_5vl7B
|
||||
| WhichModel::Qwen3vl2B
|
||||
@@ -177,6 +179,7 @@ pub trait GenerateModel {
|
||||
|
||||
pub enum ModelInstance<'a> {
|
||||
MiniCPM4(MiniCPMGenerateModel<'a>),
|
||||
Lfm2(Lfm2GenerateModel<'a>),
|
||||
Qwen2_5VL(Qwen2_5VLGenerateModel<'a>),
|
||||
Qwen3(Qwen3GenerateModel<'a>),
|
||||
Qwen3_5(Qwen3_5GenerateModel<'a>),
|
||||
@@ -196,6 +199,7 @@ impl<'a> GenerateModel for ModelInstance<'a> {
|
||||
fn generate(&mut self, mes: ChatCompletionParameters) -> Result<ChatCompletionResponse> {
|
||||
match self {
|
||||
ModelInstance::MiniCPM4(model) => model.generate(mes),
|
||||
ModelInstance::Lfm2(model) => model.generate(mes),
|
||||
ModelInstance::Qwen2_5VL(model) => model.generate(mes),
|
||||
ModelInstance::Qwen3(model) => model.generate(mes),
|
||||
ModelInstance::Qwen3_5(model) => model.generate(mes),
|
||||
@@ -225,6 +229,7 @@ impl<'a> GenerateModel for ModelInstance<'a> {
|
||||
> {
|
||||
match self {
|
||||
ModelInstance::MiniCPM4(model) => model.generate_stream(mes),
|
||||
ModelInstance::Lfm2(model) => model.generate_stream(mes),
|
||||
ModelInstance::Qwen2_5VL(model) => model.generate_stream(mes),
|
||||
ModelInstance::Qwen3(model) => model.generate_stream(mes),
|
||||
ModelInstance::Qwen3_5(model) => model.generate_stream(mes),
|
||||
@@ -253,6 +258,14 @@ pub fn load_model<'a>(
|
||||
let model = MiniCPMGenerateModel::init(path, None, None)?;
|
||||
ModelInstance::MiniCPM4(model)
|
||||
}
|
||||
WhichModel::LFM2_1_2B => {
|
||||
let model = Lfm2GenerateModel::init(path, None, None)?;
|
||||
ModelInstance::Lfm2(model)
|
||||
}
|
||||
WhichModel::LFM2_5_1_2BInstruct => {
|
||||
let model = Lfm2GenerateModel::init(path, None, None)?;
|
||||
ModelInstance::Lfm2(model)
|
||||
}
|
||||
WhichModel::Qwen2_5vl3B => {
|
||||
let model = Qwen2_5VLGenerateModel::init(path, None, None)?;
|
||||
ModelInstance::Qwen2_5VL(model)
|
||||
|
||||
@@ -6,7 +6,7 @@ use candle_nn::{
|
||||
|
||||
use crate::{
|
||||
models::{
|
||||
common::{GateUpDownMLP, eager_attention_forward},
|
||||
common::{GateUpDownMLP, QKNormAttention, eager_attention_forward},
|
||||
qwen3::config::Qwen3Config,
|
||||
},
|
||||
position_embed::rope::{RoPE, apply_rotary_pos_emb},
|
||||
@@ -135,7 +135,8 @@ impl Qwen3Attention {
|
||||
}
|
||||
|
||||
pub struct Qwen3DecoderLayer {
|
||||
self_attn: Qwen3Attention,
|
||||
// self_attn: Qwen3Attention,
|
||||
self_attn: QKNormAttention,
|
||||
mlp: GateUpDownMLP,
|
||||
input_layernorm: RmsNorm,
|
||||
post_attention_layernorm: RmsNorm,
|
||||
@@ -143,7 +144,22 @@ pub struct Qwen3DecoderLayer {
|
||||
|
||||
impl Qwen3DecoderLayer {
|
||||
pub fn new(config: &Qwen3Config, vb: VarBuilder) -> Result<Self> {
|
||||
let self_attn = Qwen3Attention::new(config, vb.pp("self_attn"))?;
|
||||
// let self_attn = Qwen3Attention::new(config, vb.pp("self_attn"))?;
|
||||
let self_attn = QKNormAttention::new(
|
||||
vb.pp("self_attn"),
|
||||
config.hidden_size,
|
||||
config.num_attention_heads,
|
||||
Some(config.head_dim),
|
||||
Some(config.num_key_value_heads),
|
||||
config.attention_bias,
|
||||
config.rms_norm_eps,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
let mlp = GateUpDownMLP::new(
|
||||
vb.pp("mlp"),
|
||||
config.hidden_size,
|
||||
|
||||
@@ -36,10 +36,9 @@ pub struct Qwen3_5GenerateModel<'a> {
|
||||
|
||||
impl<'a> Qwen3_5GenerateModel<'a> {
|
||||
pub fn init(path: &str, device: Option<&Device>, dtype: Option<DType>) -> Result<Self> {
|
||||
let model_name = path
|
||||
.split("/")
|
||||
.collect::<Vec<&str>>()
|
||||
.pop()
|
||||
let model_name = std::path::Path::new(path)
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("qwen3.5");
|
||||
let chat_template = ChatTemplate::init(path)?;
|
||||
let tokenizer = TokenizerModel::init(path)?;
|
||||
|
||||
@@ -110,6 +110,14 @@ impl TokenizerModel {
|
||||
.map_err(|e| anyhow!(format!("tokenizer encode error{}", e)))?;
|
||||
Ok(decode)
|
||||
}
|
||||
|
||||
pub fn token_decode_with_special(&self, tokens: Vec<u32>) -> Result<String> {
|
||||
let decode = self
|
||||
.tokenizer
|
||||
.decode(&tokens, false)
|
||||
.map_err(|e| anyhow!(format!("tokenizer encode error{}", e)))?;
|
||||
Ok(decode)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sentencepiece_encode(
|
||||
|
||||
Reference in New Issue
Block a user