add Qwen3.5 mmproj gguf
This commit is contained in:
@@ -25,6 +25,9 @@
|
|||||||
aha is a high-performance, cross-platform AI inference engine built with Rust and the Candle framework. It brings state-of-the-art AI models to your local machine—no API keys, no cloud dependencies, just pure, fast AI running directly on your hardware.
|
aha is a high-performance, cross-platform AI inference engine built with Rust and the Candle framework. It brings state-of-the-art AI models to your local machine—no API keys, no cloud dependencies, just pure, fast AI running directly on your hardware.
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
### 2026-03-16
|
||||||
|
- Added Qwen3.5 mmproj
|
||||||
|
|
||||||
### 2026-03-14
|
### 2026-03-14
|
||||||
- update rust version
|
- update rust version
|
||||||
- Added Qwen3.5 gguf support, but the 4B model still has issues; to be resolved.
|
- Added Qwen3.5 gguf support, but the 4B model still has issues; to be resolved.
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
aha 是一款基于 Rust 和 Candle 框架构建的高性能跨平台 AI 推理引擎。将最先进的 AI 模型带到您的本地机器——无需 API 密钥,无需云依赖,纯粹、快速的 AI,直接在您的硬件上运行。
|
aha 是一款基于 Rust 和 Candle 框架构建的高性能跨平台 AI 推理引擎。将最先进的 AI 模型带到您的本地机器——无需 API 密钥,无需云依赖,纯粹、快速的 AI,直接在您的硬件上运行。
|
||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
### 2026-03-16
|
||||||
|
- 增加 Qwen3.5 mmproj
|
||||||
|
|
||||||
### 2026-03-14
|
### 2026-03-14
|
||||||
- 更新rust版本
|
- 更新rust版本
|
||||||
- 增加了对 Qwen3.5 gguf 的支持,但 4B 模型仍然存在问题;待解决。
|
- 增加了对 Qwen3.5 gguf 的支持,但 4B 模型仍然存在问题;待解决。
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ All notable changes to aha will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
### 2026-03-16
|
||||||
|
- Added Qwen3.5 mmproj
|
||||||
|
|
||||||
### 2026-03-14
|
### 2026-03-14
|
||||||
- update rust version
|
- update rust version
|
||||||
- Added Qwen3.5 gguf support, but the 4B model still has issues; to be resolved.
|
- Added Qwen3.5 gguf support, but the 4B model still has issues; to be resolved.
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||||
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。
|
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。
|
||||||
|
|
||||||
|
### 2026-03-16
|
||||||
|
- 增加 Qwen3.5 mmproj
|
||||||
|
|
||||||
### 2026-03-14
|
### 2026-03-14
|
||||||
- 更新rust版本
|
- 更新rust版本
|
||||||
- 增加了对 Qwen3.5 gguf 的支持,但 4B 模型仍然存在问题;待解决。
|
- 增加了对 Qwen3.5 gguf 的支持,但 4B 模型仍然存在问题;待解决。
|
||||||
|
|||||||
+168
-15
@@ -3,13 +3,15 @@ use std::io::{Read, Seek};
|
|||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use candle_core::{
|
use candle_core::{
|
||||||
Device, Tensor,
|
DType, Device, Tensor,
|
||||||
quantized::{
|
quantized::{
|
||||||
QMatMul, QTensor,
|
QMatMul, QTensor,
|
||||||
gguf_file::{self, Value},
|
gguf_file::{self, Value},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use candle_nn::{Conv1d, Conv1dConfig, Linear, Module, RmsNorm, VarBuilder, linear_b};
|
use candle_nn::{
|
||||||
|
Activation, Conv1d, Conv1dConfig, LayerNorm, Linear, Module, RmsNorm, VarBuilder, linear_b,
|
||||||
|
};
|
||||||
use tokenizers::{self, AddedToken, Tokenizer, models::bpe::BPE};
|
use tokenizers::{self, AddedToken, Tokenizer, models::bpe::BPE};
|
||||||
|
|
||||||
use crate::tokenizer::TokenizerModel;
|
use crate::tokenizer::TokenizerModel;
|
||||||
@@ -37,12 +39,40 @@ impl<R: Read + Seek> Gguf<R> {
|
|||||||
Ok(QMatMul::from_qtensor(ws)?)
|
Ok(QMatMul::from_qtensor(ws)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn quantize_linear(&mut self, prefix: &str, bias: bool) -> Result<QuantizedLinear> {
|
||||||
|
let weight = self.qmatmul(&format!("{prefix}.weight"))?;
|
||||||
|
let bias = if bias {
|
||||||
|
self.get_dequantized(&format!("{prefix}.bias")).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(QuantizedLinear::new(weight, bias))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rms_norm(&mut self, name: &str, eps: f64) -> Result<RmsNorm> {
|
pub fn rms_norm(&mut self, name: &str, eps: f64) -> Result<RmsNorm> {
|
||||||
let ws = self.ct.tensor(&mut self.reader, name, &self.device)?;
|
let ws = self.ct.tensor(&mut self.reader, name, &self.device)?;
|
||||||
let weight = ws.dequantize(&self.device)?;
|
let weight = ws.dequantize(&self.device)?;
|
||||||
Ok(RmsNorm::new(weight, eps))
|
Ok(RmsNorm::new(weight, eps))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn layer_norm(&mut self, prefix: &str, eps: f64) -> Result<LayerNorm> {
|
||||||
|
let weight = self
|
||||||
|
.ct
|
||||||
|
.tensor(&mut self.reader, &format!("{prefix}.weight"), &self.device)?;
|
||||||
|
let weight = weight.dequantize(&self.device)?;
|
||||||
|
let bias = self
|
||||||
|
.ct
|
||||||
|
.tensor(&mut self.reader, &format!("{prefix}.bias"), &self.device);
|
||||||
|
let bias = match bias {
|
||||||
|
Ok(bias) => bias.dequantize(&self.device).ok(),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
match bias {
|
||||||
|
Some(bias) => Ok(LayerNorm::new(weight, bias, eps)),
|
||||||
|
None => Ok(LayerNorm::new_no_bias(weight, eps)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn metadata(&self) -> &std::collections::HashMap<String, gguf_file::Value> {
|
pub fn metadata(&self) -> &std::collections::HashMap<String, gguf_file::Value> {
|
||||||
&self.ct.metadata
|
&self.ct.metadata
|
||||||
}
|
}
|
||||||
@@ -55,6 +85,10 @@ impl<R: Read + Seek> Gguf<R> {
|
|||||||
Ok(self.tensor(name)?.dequantize(&self.device)?)
|
Ok(self.tensor(name)?.dequantize(&self.device)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_dequantized_f16(&mut self, name: &str) -> Result<Tensor> {
|
||||||
|
Ok(self.tensor(name)?.dequantize_f16(&self.device)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn conv1d(
|
pub fn conv1d(
|
||||||
&mut self,
|
&mut self,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
@@ -169,9 +203,36 @@ impl<R: Read + Seek> Gguf<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct QuantizedLinear {
|
||||||
|
inner: QMatMul,
|
||||||
|
bias: Option<Tensor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuantizedLinear {
|
||||||
|
pub fn new(inner: QMatMul, bias: Option<Tensor>) -> Self {
|
||||||
|
Self { inner, bias }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module for QuantizedLinear {
|
||||||
|
fn forward(&self, xs: &Tensor) -> candle_core::Result<Tensor> {
|
||||||
|
let xs = if xs.dtype() == DType::F16 {
|
||||||
|
self.inner.forward_via_f16(xs)?
|
||||||
|
} else {
|
||||||
|
self.inner.forward(xs)?
|
||||||
|
};
|
||||||
|
if let Some(bias) = &self.bias {
|
||||||
|
xs.broadcast_add(&bias.to_dtype(xs.dtype())?)
|
||||||
|
} else {
|
||||||
|
Ok(xs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ProjKind {
|
pub enum ProjKind {
|
||||||
QuantizedProj(QMatMul),
|
QuantizedProj(QuantizedLinear),
|
||||||
LinearProj(Linear),
|
LinearProj(Linear),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,20 +247,52 @@ impl Module for ProjKind {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GateUpDownMLPGguf {
|
pub struct GateUpDownMLPGguf {
|
||||||
gate_proj: ProjKind, // ffn_gate.weight
|
gate_proj: ProjKind,
|
||||||
up_proj: ProjKind, // ffn_up.weight
|
up_proj: ProjKind,
|
||||||
down_proj: ProjKind, // ffn_down.weight
|
down_proj: ProjKind,
|
||||||
|
act: Activation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GateUpDownMLPGguf {
|
impl GateUpDownMLPGguf {
|
||||||
pub fn new_from_gguf<R: Read + Seek>(gguf: &mut Gguf<R>, prefix: &str) -> Result<Self> {
|
pub fn new_from_gguf<R: Read + Seek>(
|
||||||
let gate_proj = gguf.qmatmul(&format!("{prefix}.ffn_gate.weight"))?;
|
gguf: &mut Gguf<R>,
|
||||||
let up_proj = gguf.qmatmul(&format!("{prefix}.ffn_up.weight"))?;
|
prefix: &str,
|
||||||
let down_proj = gguf.qmatmul(&format!("{prefix}.ffn_down.weight"))?;
|
bias: bool,
|
||||||
|
gate_name: Option<&str>,
|
||||||
|
up_name: Option<&str>,
|
||||||
|
down_name: Option<&str>,
|
||||||
|
act: Option<Activation>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let gate_name = gate_name.unwrap_or("ffn_gate");
|
||||||
|
let up_name = up_name.unwrap_or("ffn_up");
|
||||||
|
let down_name = down_name.unwrap_or("ffn_down");
|
||||||
|
let gate_proj = gguf.qmatmul(&format!("{prefix}.{gate_name}.weight"))?;
|
||||||
|
let gate_bias = if bias {
|
||||||
|
gguf.get_dequantized(&format!("{prefix}.{gate_name}.bias"))
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let up_proj = gguf.qmatmul(&format!("{prefix}.{up_name}.weight"))?;
|
||||||
|
let up_bias = if bias {
|
||||||
|
gguf.get_dequantized(&format!("{prefix}.{up_name}.bias"))
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let down_proj = gguf.qmatmul(&format!("{prefix}.{down_name}.weight"))?;
|
||||||
|
let down_bias = if bias {
|
||||||
|
gguf.get_dequantized(&format!("{prefix}.{down_name}.bias"))
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let act = act.unwrap_or(Activation::Silu);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
gate_proj: ProjKind::QuantizedProj(gate_proj),
|
gate_proj: ProjKind::QuantizedProj(QuantizedLinear::new(gate_proj, gate_bias)),
|
||||||
up_proj: ProjKind::QuantizedProj(up_proj),
|
up_proj: ProjKind::QuantizedProj(QuantizedLinear::new(up_proj, up_bias)),
|
||||||
down_proj: ProjKind::QuantizedProj(down_proj),
|
down_proj: ProjKind::QuantizedProj(QuantizedLinear::new(down_proj, down_bias)),
|
||||||
|
act,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn new_from_vb(
|
pub fn new_from_vb(
|
||||||
@@ -210,6 +303,7 @@ impl GateUpDownMLPGguf {
|
|||||||
gate_pp_name: Option<&str>,
|
gate_pp_name: Option<&str>,
|
||||||
up_pp_name: Option<&str>,
|
up_pp_name: Option<&str>,
|
||||||
down_pp_name: Option<&str>,
|
down_pp_name: Option<&str>,
|
||||||
|
act: Option<Activation>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let gate_pp_name = gate_pp_name.unwrap_or("gate_proj");
|
let gate_pp_name = gate_pp_name.unwrap_or("gate_proj");
|
||||||
let up_pp_name = up_pp_name.unwrap_or("up_proj");
|
let up_pp_name = up_pp_name.unwrap_or("up_proj");
|
||||||
@@ -217,18 +311,77 @@ impl GateUpDownMLPGguf {
|
|||||||
let gate_proj = linear_b(hidden_size, intermediate_size, bias, vb.pp(gate_pp_name))?;
|
let gate_proj = linear_b(hidden_size, intermediate_size, bias, vb.pp(gate_pp_name))?;
|
||||||
let up_proj = linear_b(hidden_size, intermediate_size, bias, vb.pp(up_pp_name))?;
|
let up_proj = linear_b(hidden_size, intermediate_size, bias, vb.pp(up_pp_name))?;
|
||||||
let down_proj = linear_b(intermediate_size, hidden_size, bias, vb.pp(down_pp_name))?;
|
let down_proj = linear_b(intermediate_size, hidden_size, bias, vb.pp(down_pp_name))?;
|
||||||
|
let act = act.unwrap_or(Activation::Silu);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
gate_proj: ProjKind::LinearProj(gate_proj),
|
gate_proj: ProjKind::LinearProj(gate_proj),
|
||||||
up_proj: ProjKind::LinearProj(up_proj),
|
up_proj: ProjKind::LinearProj(up_proj),
|
||||||
down_proj: ProjKind::LinearProj(down_proj),
|
down_proj: ProjKind::LinearProj(down_proj),
|
||||||
|
act,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module for GateUpDownMLPGguf {
|
impl Module for GateUpDownMLPGguf {
|
||||||
fn forward(&self, xs: &Tensor) -> candle_core::Result<Tensor> {
|
fn forward(&self, xs: &Tensor) -> candle_core::Result<Tensor> {
|
||||||
let w1 = self.gate_proj.forward(xs)?;
|
let w1 = self.gate_proj.forward(xs)?.apply(&self.act)?;
|
||||||
let w3 = self.up_proj.forward(xs)?;
|
let w3 = self.up_proj.forward(xs)?;
|
||||||
self.down_proj.forward(&(candle_nn::ops::silu(&w1)? * w3)?)
|
self.down_proj.forward(&(w1 * w3)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TwoLinearMLPGguf {
|
||||||
|
linear1: ProjKind,
|
||||||
|
linear2: ProjKind,
|
||||||
|
act: Activation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TwoLinearMLPGguf {
|
||||||
|
pub fn new(
|
||||||
|
vb: VarBuilder,
|
||||||
|
// embedding_dim: usize,
|
||||||
|
// mlp_dim: usize,
|
||||||
|
in_dim: usize,
|
||||||
|
middle_dim: usize,
|
||||||
|
out_dim: usize,
|
||||||
|
act: Activation,
|
||||||
|
bias: bool,
|
||||||
|
linear1_pp_name: &str,
|
||||||
|
linear2_pp_name: &str,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let linear1 = linear_b(in_dim, middle_dim, bias, vb.pp(linear1_pp_name))?;
|
||||||
|
let linear2 = linear_b(middle_dim, out_dim, bias, vb.pp(linear2_pp_name))?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
linear1: ProjKind::LinearProj(linear1),
|
||||||
|
linear2: ProjKind::LinearProj(linear2),
|
||||||
|
act,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_gguf<R: Read + Seek>(
|
||||||
|
gguf: &mut Gguf<R>,
|
||||||
|
prefix: &str,
|
||||||
|
bias: bool,
|
||||||
|
linear1_name: Option<&str>,
|
||||||
|
linear2_name: Option<&str>,
|
||||||
|
act: Option<Activation>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let linear1_name = linear1_name.unwrap_or("ffn_up");
|
||||||
|
let linear2_name = linear2_name.unwrap_or("ffn_down");
|
||||||
|
let linear1 = gguf.quantize_linear(&format!("{prefix}.{linear1_name}"), bias)?;
|
||||||
|
let linear2 = gguf.quantize_linear(&format!("{prefix}.{linear2_name}"), bias)?;
|
||||||
|
let act = act.unwrap_or(Activation::Silu);
|
||||||
|
Ok(Self {
|
||||||
|
linear1: ProjKind::QuantizedProj(linear1),
|
||||||
|
linear2: ProjKind::QuantizedProj(linear2),
|
||||||
|
act,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn forward(&self, xs: &Tensor) -> Result<Tensor> {
|
||||||
|
let xs = xs
|
||||||
|
.apply(&self.linear1)?
|
||||||
|
.apply(&self.act)?
|
||||||
|
.apply(&self.linear2)?;
|
||||||
|
Ok(xs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1172,7 +1172,7 @@ pub fn conv1d_depthwise(input: &Tensor, weight: &Tensor, bias: Option<&Tensor>)
|
|||||||
// weight: (dim, 1, k) -> (dim, k)
|
// weight: (dim, 1, k) -> (dim, k)
|
||||||
// input already padding
|
// input already padding
|
||||||
let len_in = input.dim(2)?;
|
let len_in = input.dim(2)?;
|
||||||
let weight = weight.squeeze(1)?;
|
let weight = weight.squeeze(1)?.to_dtype(input.dtype())?;
|
||||||
let kernel_size = weight.dim(1)?;
|
let kernel_size = weight.dim(1)?;
|
||||||
// len_out = (len_in - k + 2p) / s + 1, p = 0, s = 1
|
// len_out = (len_in - k + 2p) / s + 1, p = 0, s = 1
|
||||||
let len_out = len_in - kernel_size + 1;
|
let len_out = len_in - kernel_size + 1;
|
||||||
@@ -1189,7 +1189,7 @@ pub fn conv1d_depthwise(input: &Tensor, weight: &Tensor, bias: Option<&Tensor>)
|
|||||||
None => Ok(out),
|
None => Ok(out),
|
||||||
Some(bias) => {
|
Some(bias) => {
|
||||||
let b = bias.dims1()?;
|
let b = bias.dims1()?;
|
||||||
let bias = bias.reshape((1, b, 1))?;
|
let bias = bias.reshape((1, b, 1))?.to_dtype(input.dtype())?;
|
||||||
Ok(out.broadcast_add(&bias)?)
|
Ok(out.broadcast_add(&bias)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -168,7 +168,7 @@ pub enum ModelInstance<'a> {
|
|||||||
Qwen3(Qwen3GenerateModel<'a>),
|
Qwen3(Qwen3GenerateModel<'a>),
|
||||||
Qwen3_5(Qwen3_5GenerateModel<'a>),
|
Qwen3_5(Qwen3_5GenerateModel<'a>),
|
||||||
Qwen3ASR(Qwen3AsrGenerateModel<'a>),
|
Qwen3ASR(Qwen3AsrGenerateModel<'a>),
|
||||||
Qwen3VL(Qwen3VLGenerateModel<'a>),
|
Qwen3VL(Box<Qwen3VLGenerateModel<'a>>),
|
||||||
DeepSeekOCR(DeepseekOCRGenerateModel),
|
DeepSeekOCR(DeepseekOCRGenerateModel),
|
||||||
HunyuanOCR(HunyuanOCRGenerateModel<'a>),
|
HunyuanOCR(HunyuanOCRGenerateModel<'a>),
|
||||||
PaddleOCRVL(Box<PaddleOCRVLGenerateModel<'a>>),
|
PaddleOCRVL(Box<PaddleOCRVLGenerateModel<'a>>),
|
||||||
@@ -273,19 +273,19 @@ pub fn load_model(model_type: WhichModel, path: &str) -> Result<ModelInstance<'_
|
|||||||
}
|
}
|
||||||
WhichModel::Qwen3vl2B => {
|
WhichModel::Qwen3vl2B => {
|
||||||
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3VL(model)
|
ModelInstance::Qwen3VL(Box::new(model))
|
||||||
}
|
}
|
||||||
WhichModel::Qwen3vl4B => {
|
WhichModel::Qwen3vl4B => {
|
||||||
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3VL(model)
|
ModelInstance::Qwen3VL(Box::new(model))
|
||||||
}
|
}
|
||||||
WhichModel::Qwen3vl8B => {
|
WhichModel::Qwen3vl8B => {
|
||||||
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3VL(model)
|
ModelInstance::Qwen3VL(Box::new(model))
|
||||||
}
|
}
|
||||||
WhichModel::Qwen3vl32B => {
|
WhichModel::Qwen3vl32B => {
|
||||||
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
let model = Qwen3VLGenerateModel::init(path, None, None)?;
|
||||||
ModelInstance::Qwen3VL(model)
|
ModelInstance::Qwen3VL(Box::new(model))
|
||||||
}
|
}
|
||||||
WhichModel::DeepSeekOCR => {
|
WhichModel::DeepSeekOCR => {
|
||||||
let model = DeepseekOCRGenerateModel::init(path, None, None)?;
|
let model = DeepseekOCRGenerateModel::init(path, None, None)?;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use crate::{
|
|||||||
pub struct Qwen3_5GenerateModel<'a> {
|
pub struct Qwen3_5GenerateModel<'a> {
|
||||||
chat_template: ChatTemplate<'a>,
|
chat_template: ChatTemplate<'a>,
|
||||||
tokenizer: TokenizerModel,
|
tokenizer: TokenizerModel,
|
||||||
pre_processor: Qwen3VLProcessor,
|
pre_processor: Option<Qwen3VLProcessor>,
|
||||||
qwen3_5: Qwen3_5Model,
|
qwen3_5: Qwen3_5Model,
|
||||||
device: Device,
|
device: Device,
|
||||||
eos_token_id: u32,
|
eos_token_id: u32,
|
||||||
@@ -57,7 +57,7 @@ impl<'a> Qwen3_5GenerateModel<'a> {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
chat_template,
|
chat_template,
|
||||||
tokenizer,
|
tokenizer,
|
||||||
pre_processor,
|
pre_processor: Some(pre_processor),
|
||||||
qwen3_5,
|
qwen3_5,
|
||||||
device,
|
device,
|
||||||
eos_token_id,
|
eos_token_id,
|
||||||
@@ -84,26 +84,26 @@ impl<'a> Qwen3_5GenerateModel<'a> {
|
|||||||
let mut reader = std::fs::File::open(model_file)?;
|
let mut reader = std::fs::File::open(model_file)?;
|
||||||
let content = gguf_file::Content::read(&mut reader)?;
|
let content = gguf_file::Content::read(&mut reader)?;
|
||||||
let device = get_device(device);
|
let device = get_device(device);
|
||||||
let mut gguf = Gguf::new(content, reader, device.clone());
|
let mut model_gguf = Gguf::new(content, reader, device.clone());
|
||||||
|
|
||||||
let chat_template_str = gguf
|
let chat_template_str = model_gguf
|
||||||
.get_matedata("tokenizer.chat_template")?
|
.get_matedata("tokenizer.chat_template")?
|
||||||
.to_string()?
|
.to_string()?
|
||||||
.clone();
|
.clone();
|
||||||
let chat_template = ChatTemplate::str_init(&chat_template_str)?;
|
let chat_template = ChatTemplate::str_init(&chat_template_str)?;
|
||||||
let tokenizer = gguf.build_tokenizer(Some(false), Some(false), Some(false))?;
|
let tokenizer = model_gguf.build_tokenizer(Some(false), Some(false), Some(false))?;
|
||||||
let dtype = match gguf.get_matedata("general.type") {
|
let (pre_processor, mut mmproj_gguf) = if let Some(mmproj_f) = mmproj_file {
|
||||||
Ok(v) => match v.to_u32() {
|
let mut reader = std::fs::File::open(mmproj_f)?;
|
||||||
Ok(0) => DType::F32,
|
let content = gguf_file::Content::read(&mut reader)?;
|
||||||
Ok(1) => DType::F16,
|
let mmproj_gguf = Gguf::new(content, reader, device.clone());
|
||||||
_ => DType::F16,
|
let processor = Qwen3VLProcessor::new_qwen3_5_default(&device, DType::F32)?;
|
||||||
},
|
(Some(processor), Some(mmproj_gguf))
|
||||||
Err(_) => DType::F16,
|
} else {
|
||||||
|
(None, None)
|
||||||
};
|
};
|
||||||
let pre_processor = Qwen3VLProcessor::new_qwen3_5_default(&device, dtype)?;
|
|
||||||
|
|
||||||
// let eos_token_id = gguf.get_matedata("tokenizer.ggml.eos_token_id")?.to_u32()?;
|
// let eos_token_id = gguf.get_matedata("tokenizer.ggml.eos_token_id")?.to_u32()?;
|
||||||
let qwen3_5 = Qwen3_5Model::new_from_gguf(&mut gguf, &device)?;
|
let qwen3_5 = Qwen3_5Model::new_from_gguf(&mut model_gguf, mmproj_gguf.as_mut(), &device)?;
|
||||||
let stem = std::path::Path::new(model_file)
|
let stem = std::path::Path::new(model_file)
|
||||||
.file_stem() // 获取文件名主干(不含扩展名)
|
.file_stem() // 获取文件名主干(不含扩展名)
|
||||||
.and_then(|s| s.to_str())
|
.and_then(|s| s.to_str())
|
||||||
@@ -131,18 +131,28 @@ impl<'a> GenerateModel for Qwen3_5GenerateModel<'a> {
|
|||||||
get_logit_processor(temperature.into(), top_p.into(), Some(20), seed);
|
get_logit_processor(temperature.into(), top_p.into(), Some(20), seed);
|
||||||
// let mut logit_processor = get_logit_processor(mes.temperature, mes.top_p, None, seed);
|
// let mut logit_processor = get_logit_processor(mes.temperature, mes.top_p, None, seed);
|
||||||
let mes_render = self.chat_template.apply_chat_template(&mes)?;
|
let mes_render = self.chat_template.apply_chat_template(&mes)?;
|
||||||
|
let (mes_text, pixel_values, image_grid_thw, pixel_values_video, video_grid_thw) =
|
||||||
let input = self.pre_processor.process_info(&mes, &mes_render)?;
|
if let Some(processor) = &self.pre_processor {
|
||||||
let mut input_ids = self
|
let input = processor.process_info(&mes, &mes_render)?;
|
||||||
.tokenizer
|
(
|
||||||
.text_encode(input.replace_text.clone(), &self.device)?;
|
input.replace_text,
|
||||||
|
input.pixel_values,
|
||||||
|
input.image_grid_thw,
|
||||||
|
input.pixel_values_video,
|
||||||
|
input.video_grid_thw,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(mes_render, None, None, None, None)
|
||||||
|
};
|
||||||
|
// let input = self.pre_processor.process_info(&mes, &mes_render)?;
|
||||||
|
let mut input_ids = self.tokenizer.text_encode(mes_text, &self.device)?;
|
||||||
let mut seq_len = input_ids.dim(1)?;
|
let mut seq_len = input_ids.dim(1)?;
|
||||||
let prompt_tokens = seq_len as u32;
|
let prompt_tokens = seq_len as u32;
|
||||||
let mut seqlen_offset = 0;
|
let mut seqlen_offset = 0;
|
||||||
let mut pixel_values = input.pixel_values.as_ref();
|
let mut pixel_values = pixel_values.as_ref();
|
||||||
let image_grid_thw = input.image_grid_thw.as_ref();
|
let image_grid_thw = image_grid_thw.as_ref();
|
||||||
let mut pixel_values_video = input.pixel_values_video.as_ref();
|
let mut pixel_values_video = pixel_values_video.as_ref();
|
||||||
let video_grid_thw = input.video_grid_thw.as_ref();
|
let video_grid_thw = video_grid_thw.as_ref();
|
||||||
let mut generate = Vec::new();
|
let mut generate = Vec::new();
|
||||||
let sample_len = mes.max_tokens.unwrap_or(1024);
|
let sample_len = mes.max_tokens.unwrap_or(1024);
|
||||||
for _ in 0..sample_len {
|
for _ in 0..sample_len {
|
||||||
@@ -202,19 +212,30 @@ impl<'a> GenerateModel for Qwen3_5GenerateModel<'a> {
|
|||||||
let seed = mes.seed.unwrap_or(34562) as u64;
|
let seed = mes.seed.unwrap_or(34562) as u64;
|
||||||
let mut logit_processor = get_logit_processor(mes.temperature, mes.top_p, None, seed);
|
let mut logit_processor = get_logit_processor(mes.temperature, mes.top_p, None, seed);
|
||||||
let mes_render = self.chat_template.apply_chat_template(&mes)?;
|
let mes_render = self.chat_template.apply_chat_template(&mes)?;
|
||||||
let input = self.pre_processor.process_info(&mes, &mes_render)?;
|
// let input = self.pre_processor.process_info(&mes, &mes_render)?;
|
||||||
let mut input_ids = self
|
let (mes_text, pixel_values, image_grid_thw, pixel_values_video, video_grid_thw) =
|
||||||
.tokenizer
|
if let Some(processor) = &self.pre_processor {
|
||||||
.text_encode(input.replace_text.clone(), &self.device)?;
|
let input = processor.process_info(&mes, &mes_render)?;
|
||||||
|
(
|
||||||
|
input.replace_text,
|
||||||
|
input.pixel_values,
|
||||||
|
input.image_grid_thw,
|
||||||
|
input.pixel_values_video,
|
||||||
|
input.video_grid_thw,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(mes_render, None, None, None, None)
|
||||||
|
};
|
||||||
|
let mut input_ids = self.tokenizer.text_encode(mes_text, &self.device)?;
|
||||||
let mut seq_len = input_ids.dim(1)?;
|
let mut seq_len = input_ids.dim(1)?;
|
||||||
let mut seqlen_offset = 0;
|
let mut seqlen_offset = 0;
|
||||||
let sample_len = mes.max_tokens.unwrap_or(1024);
|
let sample_len = mes.max_tokens.unwrap_or(1024);
|
||||||
let stream = stream! {
|
let stream = stream! {
|
||||||
let mut error_tokens = Vec::new();
|
let mut error_tokens = Vec::new();
|
||||||
let mut pixel_values = input.pixel_values.as_ref();
|
let mut pixel_values = pixel_values.as_ref();
|
||||||
let image_grid_thw = input.image_grid_thw.as_ref();
|
let image_grid_thw = image_grid_thw.as_ref();
|
||||||
let mut pixel_values_video = input.pixel_values_video.as_ref();
|
let mut pixel_values_video = pixel_values_video.as_ref();
|
||||||
let video_grid_thw = input.video_grid_thw.as_ref();
|
let video_grid_thw = video_grid_thw.as_ref();
|
||||||
let mut tool_call_id = None;
|
let mut tool_call_id = None;
|
||||||
let mut tool_call_content = String::new();
|
let mut tool_call_content = String::new();
|
||||||
let mut generate = Vec::new();
|
let mut generate = Vec::new();
|
||||||
|
|||||||
+75
-38
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
models::{
|
models::{
|
||||||
common::{
|
common::{
|
||||||
conv1d_depthwise, eager_attention_forward, get_conv1d,
|
conv1d_depthwise, eager_attention_forward, get_conv1d,
|
||||||
gguf::{GateUpDownMLPGguf, Gguf, ProjKind},
|
gguf::{GateUpDownMLPGguf, Gguf, ProjKind, QuantizedLinear},
|
||||||
softplus,
|
softplus,
|
||||||
},
|
},
|
||||||
qwen3_5::config::{Qwen3_5Config, Qwen3_5TextConfig},
|
qwen3_5::config::{Qwen3_5Config, Qwen3_5TextConfig},
|
||||||
@@ -55,24 +55,29 @@ impl Qwen3_5RMSNorm {
|
|||||||
|
|
||||||
pub struct Qwen3_5RMSNormGated {
|
pub struct Qwen3_5RMSNormGated {
|
||||||
norm: RmsNorm,
|
norm: RmsNorm,
|
||||||
|
dtype: DType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Qwen3_5RMSNormGated {
|
impl Qwen3_5RMSNormGated {
|
||||||
pub fn new(vb: VarBuilder, hidden_size: usize, eps: f64) -> Result<Self> {
|
pub fn new(vb: VarBuilder, hidden_size: usize, eps: f64) -> Result<Self> {
|
||||||
|
let dtype = vb.dtype();
|
||||||
let norm = rms_norm(hidden_size, eps, vb)?;
|
let norm = rms_norm(hidden_size, eps, vb)?;
|
||||||
Ok(Self { norm })
|
Ok(Self { norm, dtype })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_weight(weight: Tensor, eps: f64) -> Result<Self> {
|
pub fn from_weight(weight: Tensor, eps: f64) -> Result<Self> {
|
||||||
|
let dtype = weight.dtype();
|
||||||
let norm = RmsNorm::new(weight, eps);
|
let norm = RmsNorm::new(weight, eps);
|
||||||
Ok(Self { norm })
|
Ok(Self { norm, dtype })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forward(&self, xs: &Tensor, gate: Option<&Tensor>) -> Result<Tensor> {
|
pub fn forward(&self, xs: &Tensor, gate: Option<&Tensor>) -> Result<Tensor> {
|
||||||
let mut xs = self.norm.forward(xs)?;
|
let orig_dtype = xs.dtype();
|
||||||
|
let mut xs = self.norm.forward(&xs.to_dtype(self.dtype)?)?;
|
||||||
if let Some(gate) = gate {
|
if let Some(gate) = gate {
|
||||||
xs = xs.broadcast_mul(&gate.silu()?)?;
|
xs = xs.broadcast_mul(&gate.silu()?.to_dtype(xs.dtype())?)?;
|
||||||
}
|
}
|
||||||
|
xs = xs.to_dtype(orig_dtype)?;
|
||||||
Ok(xs)
|
Ok(xs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,11 +230,16 @@ impl Qwen3_5GatedDeltaNet {
|
|||||||
let a_log = gguf.get_dequantized(&format!("{prefix}.ssm_a"))?;
|
let a_log = gguf.get_dequantized(&format!("{prefix}.ssm_a"))?;
|
||||||
let norm_weight = gguf.get_dequantized(&format!("{prefix}.ssm_norm.weight"))?;
|
let norm_weight = gguf.get_dequantized(&format!("{prefix}.ssm_norm.weight"))?;
|
||||||
let norm = Qwen3_5RMSNormGated::from_weight(norm_weight, rms_norm_eps)?;
|
let norm = Qwen3_5RMSNormGated::from_weight(norm_weight, rms_norm_eps)?;
|
||||||
let out_proj = gguf.qmatmul(&format!("{prefix}.ssm_out.weight"))?;
|
// let out_proj = gguf.qmatmul(&format!("{prefix}.ssm_out.weight"))?;
|
||||||
let in_proj_qkv = gguf.qmatmul(&format!("{prefix}.attn_qkv.weight"))?;
|
// let in_proj_qkv = gguf.qmatmul(&format!("{prefix}.attn_qkv.weight"))?;
|
||||||
let in_proj_z = gguf.qmatmul(&format!("{prefix}.attn_gate.weight"))?;
|
// let in_proj_z = gguf.qmatmul(&format!("{prefix}.attn_gate.weight"))?;
|
||||||
let in_proj_b = gguf.qmatmul(&format!("{prefix}.ssm_beta.weight"))?;
|
// let in_proj_b = gguf.qmatmul(&format!("{prefix}.ssm_beta.weight"))?;
|
||||||
let in_proj_a = gguf.qmatmul(&format!("{prefix}.ssm_alpha.weight"))?;
|
// let in_proj_a = gguf.qmatmul(&format!("{prefix}.ssm_alpha.weight"))?;
|
||||||
|
let out_proj = gguf.quantize_linear(&format!("{prefix}.ssm_out"), false)?;
|
||||||
|
let in_proj_qkv = gguf.quantize_linear(&format!("{prefix}.attn_qkv"), false)?;
|
||||||
|
let in_proj_z = gguf.quantize_linear(&format!("{prefix}.attn_gate"), false)?;
|
||||||
|
let in_proj_b = gguf.quantize_linear(&format!("{prefix}.ssm_beta"), false)?;
|
||||||
|
let in_proj_a = gguf.quantize_linear(&format!("{prefix}.ssm_alpha"), false)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
num_v_heads,
|
num_v_heads,
|
||||||
@@ -497,7 +507,7 @@ impl Qwen3_5GatedDeltaNet {
|
|||||||
|
|
||||||
pub fn forward(&mut self, xs: &Tensor, attention_mask: Option<&Tensor>) -> Result<Tensor> {
|
pub fn forward(&mut self, xs: &Tensor, attention_mask: Option<&Tensor>) -> Result<Tensor> {
|
||||||
let xs = if let Some(mask) = attention_mask {
|
let xs = if let Some(mask) = attention_mask {
|
||||||
xs.broadcast_mul(&mask.unsqueeze(D::Minus1)?)?
|
xs.broadcast_mul(&mask.unsqueeze(D::Minus1)?.to_dtype(xs.dtype())?)?
|
||||||
} else {
|
} else {
|
||||||
xs.clone()
|
xs.clone()
|
||||||
};
|
};
|
||||||
@@ -654,10 +664,14 @@ impl Qwen3_5Attention {
|
|||||||
let num_kv_groups = num_attention_heads / num_key_value_heads;
|
let num_kv_groups = num_attention_heads / num_key_value_heads;
|
||||||
let head_dim = gguf.get_matedata("qwen35.attention.key_length")?.to_u32()? as usize;
|
let head_dim = gguf.get_matedata("qwen35.attention.key_length")?.to_u32()? as usize;
|
||||||
let scaling = 1f64 / f64::sqrt(head_dim as f64);
|
let scaling = 1f64 / f64::sqrt(head_dim as f64);
|
||||||
let q_proj = gguf.qmatmul(&format!("{prefix}.attn_q.weight"))?;
|
// let q_proj = gguf.qmatmul(&format!("{prefix}.attn_q.weight"))?;
|
||||||
let k_proj = gguf.qmatmul(&format!("{prefix}.attn_k.weight"))?;
|
// let k_proj = gguf.qmatmul(&format!("{prefix}.attn_k.weight"))?;
|
||||||
let v_proj = gguf.qmatmul(&format!("{prefix}.attn_v.weight"))?;
|
// let v_proj = gguf.qmatmul(&format!("{prefix}.attn_v.weight"))?;
|
||||||
let o_proj = gguf.qmatmul(&format!("{prefix}.attn_output.weight"))?;
|
// let o_proj = gguf.qmatmul(&format!("{prefix}.attn_output.weight"))?;
|
||||||
|
let q_proj = gguf.quantize_linear(&format!("{prefix}.attn_q"), false)?;
|
||||||
|
let k_proj = gguf.quantize_linear(&format!("{prefix}.attn_k"), false)?;
|
||||||
|
let v_proj = gguf.quantize_linear(&format!("{prefix}.attn_v"), false)?;
|
||||||
|
let o_proj = gguf.quantize_linear(&format!("{prefix}.attn_output"), false)?;
|
||||||
let q_norm_weight = gguf.get_dequantized(&format!("{prefix}.attn_q_norm.weight"))?;
|
let q_norm_weight = gguf.get_dequantized(&format!("{prefix}.attn_q_norm.weight"))?;
|
||||||
let q_norm = Qwen3_5RMSNorm::from_weight(q_norm_weight, rms_norm_eps)?;
|
let q_norm = Qwen3_5RMSNorm::from_weight(q_norm_weight, rms_norm_eps)?;
|
||||||
let k_norm_weight = gguf.get_dequantized(&format!("{prefix}.attn_k_norm.weight"))?;
|
let k_norm_weight = gguf.get_dequantized(&format!("{prefix}.attn_k_norm.weight"))?;
|
||||||
@@ -800,6 +814,7 @@ impl Qwen3_5DecoderLayer {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
Some(config.hidden_act),
|
||||||
)?;
|
)?;
|
||||||
let input_layernorm =
|
let input_layernorm =
|
||||||
Qwen3_5RMSNorm::new(vb.pp("input_layernorm"), hidden_size, config.rms_norm_eps)?;
|
Qwen3_5RMSNorm::new(vb.pp("input_layernorm"), hidden_size, config.rms_norm_eps)?;
|
||||||
@@ -831,7 +846,15 @@ impl Qwen3_5DecoderLayer {
|
|||||||
let attn = Qwen3_5Attention::new_from_gguf(gguf, prefix, rms_norm_eps)?;
|
let attn = Qwen3_5Attention::new_from_gguf(gguf, prefix, rms_norm_eps)?;
|
||||||
AttnKind::SelfAttn(attn)
|
AttnKind::SelfAttn(attn)
|
||||||
};
|
};
|
||||||
let mlp = GateUpDownMLPGguf::new_from_gguf(gguf, prefix)?;
|
let mlp = GateUpDownMLPGguf::new_from_gguf(
|
||||||
|
gguf,
|
||||||
|
prefix,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(candle_nn::Activation::Silu),
|
||||||
|
)?;
|
||||||
let input_norm_weight = gguf.get_dequantized(&format!("{prefix}.attn_norm.weight"))?;
|
let input_norm_weight = gguf.get_dequantized(&format!("{prefix}.attn_norm.weight"))?;
|
||||||
let input_layernorm = Qwen3_5RMSNorm::from_weight(input_norm_weight, rms_norm_eps)?;
|
let input_layernorm = Qwen3_5RMSNorm::from_weight(input_norm_weight, rms_norm_eps)?;
|
||||||
let post_norm_weight =
|
let post_norm_weight =
|
||||||
@@ -857,16 +880,6 @@ impl Qwen3_5DecoderLayer {
|
|||||||
let residual = xs.clone();
|
let residual = xs.clone();
|
||||||
let mut xs = self.input_layernorm.forward(xs)?;
|
let mut xs = self.input_layernorm.forward(xs)?;
|
||||||
xs = self.attn.forward(&xs, cos, sin, attention_mask)?;
|
xs = self.attn.forward(&xs, cos, sin, attention_mask)?;
|
||||||
// if self.layer_type.eq("linear_attention")
|
|
||||||
// && let Some(linear_attn) = self.linear_attn.as_mut()
|
|
||||||
// {
|
|
||||||
// xs = linear_attn.forward(&xs, attention_mask)?;
|
|
||||||
// } else if let Some(self_attn) = self.self_attn.as_mut()
|
|
||||||
// && let Some(cos) = cos
|
|
||||||
// && let Some(sin) = sin
|
|
||||||
// {
|
|
||||||
// xs = self_attn.forward(&xs, cos, sin, attention_mask)?;
|
|
||||||
// }
|
|
||||||
let residual = xs.add(&residual)?;
|
let residual = xs.add(&residual)?;
|
||||||
xs = self.post_attention_layernorm.forward(&residual)?;
|
xs = self.post_attention_layernorm.forward(&residual)?;
|
||||||
xs = self.mlp.forward(&xs)?;
|
xs = self.mlp.forward(&xs)?;
|
||||||
@@ -920,6 +933,14 @@ impl Qwen3_5TextModel {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn new_from_gguf<R: Read + Seek>(gguf: &mut Gguf<R>, device: &Device) -> Result<Self> {
|
pub fn new_from_gguf<R: Read + Seek>(gguf: &mut Gguf<R>, device: &Device) -> Result<Self> {
|
||||||
|
let dtype = match gguf.get_matedata("general.dtype") {
|
||||||
|
Ok(v) => match v.to_u32() {
|
||||||
|
Ok(0) => DType::F32,
|
||||||
|
Ok(1) => DType::F16,
|
||||||
|
_ => DType::F16,
|
||||||
|
},
|
||||||
|
Err(_) => DType::F16,
|
||||||
|
};
|
||||||
let num_layers = gguf.get_matedata("qwen35.block_count")?.to_u32()? as usize;
|
let num_layers = gguf.get_matedata("qwen35.block_count")?.to_u32()? as usize;
|
||||||
let full_attention_interval = gguf
|
let full_attention_interval = gguf
|
||||||
.get_matedata("qwen35.full_attention_interval")?
|
.get_matedata("qwen35.full_attention_interval")?
|
||||||
@@ -939,6 +960,10 @@ impl Qwen3_5TextModel {
|
|||||||
.to_f32()? as f64;
|
.to_f32()? as f64;
|
||||||
let hidden_size = gguf.get_matedata("qwen35.embedding_length")?.to_u32()? as usize; // 1024
|
let hidden_size = gguf.get_matedata("qwen35.embedding_length")?.to_u32()? as usize; // 1024
|
||||||
let embed_tensor = gguf.tensor("token_embd.weight")?;
|
let embed_tensor = gguf.tensor("token_embd.weight")?;
|
||||||
|
// let embed_tokens = match dtype {
|
||||||
|
// DType::F32 => Embedding::new(embed_tensor.dequantize(device)?, hidden_size),
|
||||||
|
// _ => Embedding::new(embed_tensor.dequantize_f16(device)?, hidden_size),
|
||||||
|
// };
|
||||||
let embed_tokens = Embedding::new(embed_tensor.dequantize(device)?, hidden_size);
|
let embed_tokens = Embedding::new(embed_tensor.dequantize(device)?, hidden_size);
|
||||||
let mut layers = vec![];
|
let mut layers = vec![];
|
||||||
for i in 0..num_layers {
|
for i in 0..num_layers {
|
||||||
@@ -956,14 +981,7 @@ impl Qwen3_5TextModel {
|
|||||||
let norm_weight = gguf.get_dequantized("output_norm.weight")?;
|
let norm_weight = gguf.get_dequantized("output_norm.weight")?;
|
||||||
let norm = Qwen3_5RMSNorm::from_weight(norm_weight, rms_norm_eps)?;
|
let norm = Qwen3_5RMSNorm::from_weight(norm_weight, rms_norm_eps)?;
|
||||||
let rotary_emb = Qwen3VLTextRotaryEmbedding::new(rope_dimension_count, rope_freq_base);
|
let rotary_emb = Qwen3VLTextRotaryEmbedding::new(rope_dimension_count, rope_freq_base);
|
||||||
let dtype = match gguf.get_matedata("general.type") {
|
|
||||||
Ok(v) => match v.to_u32() {
|
|
||||||
Ok(0) => DType::F32,
|
|
||||||
Ok(1) => DType::F16,
|
|
||||||
_ => DType::F16,
|
|
||||||
},
|
|
||||||
Err(_) => DType::F16,
|
|
||||||
};
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
embed_tokens,
|
embed_tokens,
|
||||||
layers,
|
layers,
|
||||||
@@ -993,6 +1011,7 @@ impl Qwen3_5TextModel {
|
|||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// let mut i = 0;
|
||||||
for layer in self.layers.iter_mut() {
|
for layer in self.layers.iter_mut() {
|
||||||
let layer_mask =
|
let layer_mask =
|
||||||
if layer.layer_type.ne("linear_attention") || (seq_len != 1 && b_size != 1) {
|
if layer.layer_type.ne("linear_attention") || (seq_len != 1 && b_size != 1) {
|
||||||
@@ -1001,8 +1020,11 @@ impl Qwen3_5TextModel {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
xs = layer.forward(&xs, Some(&cos), Some(&sin), layer_mask.as_ref())?;
|
xs = layer.forward(&xs, Some(&cos), Some(&sin), layer_mask.as_ref())?;
|
||||||
|
// println!("layer {i} : {}", xs);
|
||||||
|
// i += 1;
|
||||||
}
|
}
|
||||||
xs = self.norm.forward(&xs)?;
|
xs = self.norm.forward(&xs)?;
|
||||||
|
// println!("norm : {}", xs);
|
||||||
Ok(xs)
|
Ok(xs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,11 +1074,21 @@ impl Qwen3_5Model {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_gguf<R: Read + Seek>(gguf: &mut Gguf<R>, device: &Device) -> Result<Self> {
|
pub fn new_from_gguf<R: Read + Seek>(
|
||||||
|
gguf: &mut Gguf<R>,
|
||||||
|
mmproj_gguf: Option<&mut Gguf<R>>,
|
||||||
|
device: &Device,
|
||||||
|
) -> Result<Self> {
|
||||||
let spatial_merge_size = 2usize;
|
let spatial_merge_size = 2usize;
|
||||||
let image_token_id = 248056u32;
|
let image_token_id = 248056u32;
|
||||||
let video_token_id = 248057u32;
|
let video_token_id = 248057u32;
|
||||||
let vision_start_token_id = 248053u32;
|
let vision_start_token_id = 248053u32;
|
||||||
|
let visual = if let Some(mmproj) = mmproj_gguf {
|
||||||
|
let visual = Qwen3VLVisionModel::new_from_gguf(mmproj)?;
|
||||||
|
Some(visual)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let language_model = Qwen3_5TextModel::new_from_gguf(gguf, device)?;
|
let language_model = Qwen3_5TextModel::new_from_gguf(gguf, device)?;
|
||||||
let lm_head_tensor = match gguf.tensor("output.weight") {
|
let lm_head_tensor = match gguf.tensor("output.weight") {
|
||||||
Ok(tensor) => tensor,
|
Ok(tensor) => tensor,
|
||||||
@@ -1068,9 +1100,9 @@ impl Qwen3_5Model {
|
|||||||
image_token_id,
|
image_token_id,
|
||||||
video_token_id,
|
video_token_id,
|
||||||
vision_start_token_id,
|
vision_start_token_id,
|
||||||
visual: None,
|
visual,
|
||||||
language_model,
|
language_model,
|
||||||
lm_head: ProjKind::QuantizedProj(lm_head),
|
lm_head: ProjKind::QuantizedProj(QuantizedLinear::new(lm_head, None)),
|
||||||
rope_deltas: None,
|
rope_deltas: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1351,6 +1383,7 @@ impl Qwen3_5Model {
|
|||||||
seqlen_offset: usize,
|
seqlen_offset: usize,
|
||||||
) -> Result<Tensor> {
|
) -> Result<Tensor> {
|
||||||
let mut inputs_embeds = self.language_model.embed_tokens.forward(input_ids)?;
|
let mut inputs_embeds = self.language_model.embed_tokens.forward(input_ids)?;
|
||||||
|
// println!("embed_tokens: {}", inputs_embeds);
|
||||||
if let Some(pixel_values) = pixel_values
|
if let Some(pixel_values) = pixel_values
|
||||||
&& let Some(image_grid_thw) = image_grid_thw
|
&& let Some(image_grid_thw) = image_grid_thw
|
||||||
&& let Some(visual) = self.visual.as_ref()
|
&& let Some(visual) = self.visual.as_ref()
|
||||||
@@ -1365,6 +1398,7 @@ impl Qwen3_5Model {
|
|||||||
image_embeds.dim(0)?
|
image_embeds.dim(0)?
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
let image_embeds = image_embeds.to_dtype(inputs_embeds.dtype())?;
|
||||||
inputs_embeds = masked_scatter_dim0(&inputs_embeds, &image_embeds, &vision_mask)?;
|
inputs_embeds = masked_scatter_dim0(&inputs_embeds, &image_embeds, &vision_mask)?;
|
||||||
}
|
}
|
||||||
if let Some(pixel_values_video) = pixel_values_video
|
if let Some(pixel_values_video) = pixel_values_video
|
||||||
@@ -1381,9 +1415,10 @@ impl Qwen3_5Model {
|
|||||||
video_embeds.dim(0)?
|
video_embeds.dim(0)?
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
let video_embeds = video_embeds.to_dtype(inputs_embeds.dtype())?;
|
||||||
inputs_embeds = masked_scatter_dim0(&inputs_embeds, &video_embeds, &vision_mask)?;
|
inputs_embeds = masked_scatter_dim0(&inputs_embeds, &video_embeds, &vision_mask)?;
|
||||||
}
|
}
|
||||||
|
// println!("visual : {}", inputs_embeds);
|
||||||
let position_ids = self.compute_3d_position_ids(
|
let position_ids = self.compute_3d_position_ids(
|
||||||
input_ids,
|
input_ids,
|
||||||
&inputs_embeds,
|
&inputs_embeds,
|
||||||
@@ -1394,7 +1429,9 @@ impl Qwen3_5Model {
|
|||||||
let outputs = self.language_model.forward(&inputs_embeds, &position_ids)?;
|
let outputs = self.language_model.forward(&inputs_embeds, &position_ids)?;
|
||||||
let seq_len = outputs.dim(1)?;
|
let seq_len = outputs.dim(1)?;
|
||||||
let hidden_state = outputs.narrow(1, seq_len - 1, 1)?;
|
let hidden_state = outputs.narrow(1, seq_len - 1, 1)?;
|
||||||
|
// println!("narrow 1 : {}", hidden_state);
|
||||||
let logits = self.lm_head.forward(&hidden_state)?;
|
let logits = self.lm_head.forward(&hidden_state)?;
|
||||||
|
// println!("logits : {}", logits);
|
||||||
Ok(logits)
|
Ok(logits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+212
-11
@@ -1,3 +1,5 @@
|
|||||||
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use candle_core::{D, DType, IndexOp, Shape, Tensor};
|
use candle_core::{D, DType, IndexOp, Shape, Tensor};
|
||||||
use candle_nn::{
|
use candle_nn::{
|
||||||
@@ -7,7 +9,10 @@ use candle_nn::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::{
|
models::{
|
||||||
common::{TwoLinearMLP, eager_attention_forward, get_layer_norm},
|
common::{
|
||||||
|
eager_attention_forward, get_layer_norm,
|
||||||
|
gguf::{Gguf, ProjKind, TwoLinearMLPGguf},
|
||||||
|
},
|
||||||
qwen3::model::Qwen3DecoderLayer,
|
qwen3::model::Qwen3DecoderLayer,
|
||||||
qwen3vl::config::{
|
qwen3vl::config::{
|
||||||
Qwen3VLConfig, Qwen3VLTextConfig, Qwen3VLVisionConfig, qwen3vl_text_config2qwen3_config,
|
Qwen3VLConfig, Qwen3VLTextConfig, Qwen3VLVisionConfig, qwen3vl_text_config2qwen3_config,
|
||||||
@@ -60,6 +65,33 @@ impl Qwen3VLVisionPatchEmbed {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_gguf<R: Read + Seek>(gguf: &mut Gguf<R>) -> Result<Self> {
|
||||||
|
// convert_hf_to_gguf.py
|
||||||
|
// temporal_patch_size = 2
|
||||||
|
// spilt (embed_dim, in_channels, temporal_patch_size, patch_size, patch_size)
|
||||||
|
// into two (embed_dim, in_channels, patch_size, patch_size)
|
||||||
|
// elif 'patch_embed.proj.weight' in name:
|
||||||
|
// # split Conv3D into Conv2Ds
|
||||||
|
// c1, c2, kt, kh, kw = data_torch.shape
|
||||||
|
// del c1, c2, kh, kw # unused
|
||||||
|
// assert kt == 2, "Current implementation only support temporal_patch_size of 2"
|
||||||
|
// yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight" , data_torch[:, :, 0, ...])
|
||||||
|
// yield (gguf.TENSOR_NAMES[gguf.MODEL_TENSOR.V_ENC_EMBD_PATCH] + ".weight.1", data_torch[:, :, 1, ...])
|
||||||
|
// (embed_dim, in_channels, patch_size, patch_size) -> (embed_dim, in_channels, 1, patch_size, patch_size)
|
||||||
|
let conv3d_weight_0 = gguf.get_dequantized("v.patch_embd.weight")?.unsqueeze(2)?;
|
||||||
|
let conv3d_weight_1 = gguf
|
||||||
|
.get_dequantized("v.patch_embd.weight.1")?
|
||||||
|
.unsqueeze(2)?;
|
||||||
|
let conv3d_weight = Tensor::cat(&[conv3d_weight_0, conv3d_weight_1], 2)?
|
||||||
|
.flatten(1, 4)?
|
||||||
|
.t()?;
|
||||||
|
let conv3d_bias = gguf.get_dequantized("v.patch_embd.bias")?;
|
||||||
|
Ok(Self {
|
||||||
|
conv3d_weight,
|
||||||
|
conv3d_bias,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn forward(&self, hidden_states: &Tensor) -> Result<Tensor> {
|
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)
|
// hidden_states shape: (grid_t*grid_h*grid_w, c*temporal_patch_size*patch_size*patch_size)
|
||||||
// ((), 1536) matmul (1536, 1024) -> ((), 1024)
|
// ((), 1536) matmul (1536, 1024) -> ((), 1024)
|
||||||
@@ -73,9 +105,11 @@ pub struct Qwen3VLVisionPatchMerger {
|
|||||||
hidden_size: usize,
|
hidden_size: usize,
|
||||||
use_postshuffle_norm: bool,
|
use_postshuffle_norm: bool,
|
||||||
norm: LayerNorm,
|
norm: LayerNorm,
|
||||||
linear_fc1: Linear,
|
// linear_fc1: Linear,
|
||||||
|
linear_fc1: ProjKind,
|
||||||
act_fn: Activation,
|
act_fn: Activation,
|
||||||
linear_fc2: Linear,
|
// linear_fc2: Linear,
|
||||||
|
linear_fc2: ProjKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Qwen3VLVisionPatchMerger {
|
impl Qwen3VLVisionPatchMerger {
|
||||||
@@ -98,9 +132,34 @@ impl Qwen3VLVisionPatchMerger {
|
|||||||
hidden_size,
|
hidden_size,
|
||||||
use_postshuffle_norm,
|
use_postshuffle_norm,
|
||||||
norm,
|
norm,
|
||||||
linear_fc1,
|
linear_fc1: ProjKind::LinearProj(linear_fc1),
|
||||||
act_fn,
|
act_fn,
|
||||||
linear_fc2,
|
linear_fc2: ProjKind::LinearProj(linear_fc2),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_gguf<R: Read + Seek>(
|
||||||
|
gguf: &mut Gguf<R>,
|
||||||
|
rms_norm_eps: f64,
|
||||||
|
use_postshuffle_norm: bool,
|
||||||
|
hidden_size: usize,
|
||||||
|
spatial_merge_size: usize,
|
||||||
|
norm_prefix: &str,
|
||||||
|
linear1_prefix: &str,
|
||||||
|
linear2_prefix: &str,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let hidden_size = hidden_size * spatial_merge_size.pow(2);
|
||||||
|
let norm = gguf.layer_norm(norm_prefix, rms_norm_eps)?;
|
||||||
|
let linear_1 = gguf.quantize_linear(linear1_prefix, true)?;
|
||||||
|
let act_fn = Activation::Gelu;
|
||||||
|
let linear_2 = gguf.quantize_linear(linear2_prefix, true)?;
|
||||||
|
Ok(Self {
|
||||||
|
hidden_size,
|
||||||
|
use_postshuffle_norm,
|
||||||
|
norm,
|
||||||
|
linear_fc1: ProjKind::QuantizedProj(linear_1),
|
||||||
|
act_fn,
|
||||||
|
linear_fc2: ProjKind::QuantizedProj(linear_2),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +179,10 @@ impl Qwen3VLVisionPatchMerger {
|
|||||||
|
|
||||||
pub struct Qwen3VLVisionAttention {
|
pub struct Qwen3VLVisionAttention {
|
||||||
num_heads: usize,
|
num_heads: usize,
|
||||||
qkv: Linear,
|
// qkv: Linear,
|
||||||
proj: Linear,
|
// proj: Linear,
|
||||||
|
qkv: ProjKind,
|
||||||
|
proj: ProjKind,
|
||||||
scaling: f64,
|
scaling: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,8 +197,27 @@ impl Qwen3VLVisionAttention {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
num_heads,
|
num_heads,
|
||||||
qkv,
|
qkv: ProjKind::LinearProj(qkv),
|
||||||
proj,
|
proj: ProjKind::LinearProj(proj),
|
||||||
|
scaling,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_gguf<R: Read + Seek>(mmproj_gguf: &mut Gguf<R>, prefix: &str) -> Result<Self> {
|
||||||
|
let num_heads = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.attention.head_count")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let hidden_size = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.embedding_length")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let head_dim = hidden_size / num_heads;
|
||||||
|
let scaling = 1.0 / (head_dim as f64).sqrt();
|
||||||
|
let qkv = mmproj_gguf.quantize_linear(&format!("{prefix}.attn_qkv"), true)?;
|
||||||
|
let proj = mmproj_gguf.quantize_linear(&format!("{prefix}.attn_out"), true)?;
|
||||||
|
Ok(Self {
|
||||||
|
num_heads,
|
||||||
|
qkv: ProjKind::QuantizedProj(qkv),
|
||||||
|
proj: ProjKind::QuantizedProj(proj),
|
||||||
scaling,
|
scaling,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -195,7 +275,8 @@ pub struct Qwen3VLVisionBlock {
|
|||||||
norm1: LayerNorm,
|
norm1: LayerNorm,
|
||||||
norm2: LayerNorm,
|
norm2: LayerNorm,
|
||||||
attn: Qwen3VLVisionAttention,
|
attn: Qwen3VLVisionAttention,
|
||||||
mlp: TwoLinearMLP,
|
// mlp: TwoLinearMLP,
|
||||||
|
mlp: TwoLinearMLPGguf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Qwen3VLVisionBlock {
|
impl Qwen3VLVisionBlock {
|
||||||
@@ -203,7 +284,17 @@ impl Qwen3VLVisionBlock {
|
|||||||
let norm1 = get_layer_norm(vb.pp("norm1"), 1e-6, config.hidden_size, true)?;
|
let norm1 = get_layer_norm(vb.pp("norm1"), 1e-6, config.hidden_size, true)?;
|
||||||
let norm2 = get_layer_norm(vb.pp("norm2"), 1e-6, config.hidden_size, true)?;
|
let norm2 = get_layer_norm(vb.pp("norm2"), 1e-6, config.hidden_size, true)?;
|
||||||
let attn = Qwen3VLVisionAttention::new(config.clone(), vb.pp("attn"))?;
|
let attn = Qwen3VLVisionAttention::new(config.clone(), vb.pp("attn"))?;
|
||||||
let mlp = TwoLinearMLP::new(
|
// let mlp = TwoLinearMLP::new(
|
||||||
|
// vb.pp("mlp"),
|
||||||
|
// config.hidden_size,
|
||||||
|
// config.intermediate_size,
|
||||||
|
// config.hidden_size,
|
||||||
|
// config.hidden_act,
|
||||||
|
// true,
|
||||||
|
// "linear_fc1",
|
||||||
|
// "linear_fc2",
|
||||||
|
// )?;
|
||||||
|
let mlp = TwoLinearMLPGguf::new(
|
||||||
vb.pp("mlp"),
|
vb.pp("mlp"),
|
||||||
config.hidden_size,
|
config.hidden_size,
|
||||||
config.intermediate_size,
|
config.intermediate_size,
|
||||||
@@ -221,6 +312,30 @@ impl Qwen3VLVisionBlock {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_gguf<R: Read + Seek>(
|
||||||
|
mmproj_gguf: &mut Gguf<R>,
|
||||||
|
prefix: &str,
|
||||||
|
rms_norm_eps: f64,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let norm1 = mmproj_gguf.layer_norm(&format!("{prefix}.ln1"), rms_norm_eps)?;
|
||||||
|
let norm2 = mmproj_gguf.layer_norm(&format!("{prefix}.ln2"), rms_norm_eps)?;
|
||||||
|
let attn = Qwen3VLVisionAttention::new_from_gguf(mmproj_gguf, prefix)?;
|
||||||
|
let mlp = TwoLinearMLPGguf::new_from_gguf(
|
||||||
|
mmproj_gguf,
|
||||||
|
prefix,
|
||||||
|
true,
|
||||||
|
Some("ffn_up"),
|
||||||
|
Some("ffn_down"),
|
||||||
|
Some(Activation::GeluPytorchTanh),
|
||||||
|
)?;
|
||||||
|
Ok(Self {
|
||||||
|
norm1,
|
||||||
|
norm2,
|
||||||
|
attn,
|
||||||
|
mlp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn forward(
|
pub fn forward(
|
||||||
&self,
|
&self,
|
||||||
xs: &Tensor,
|
xs: &Tensor,
|
||||||
@@ -292,6 +407,92 @@ impl Qwen3VLVisionModel {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_gguf<R: Read + Seek>(mmproj_gguf: &mut Gguf<R>) -> Result<Self> {
|
||||||
|
// let num_layers = gguf.get_matedata("qwen35.block_count")?.to_u32()? as usize;
|
||||||
|
let spatial_merge_size = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.spatial_merge_size")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let patch_embed = Qwen3VLVisionPatchEmbed::new_from_gguf(mmproj_gguf)?;
|
||||||
|
let pos_emb_weight = mmproj_gguf.get_dequantized("v.position_embd.weight")?;
|
||||||
|
let hidden_size = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.embedding_length")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let pos_embed = Embedding::new(pos_emb_weight, hidden_size);
|
||||||
|
let patch_size = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.patch_size")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let image_size = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.image_size")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let num_grid_per_side = image_size / patch_size;
|
||||||
|
let num_heads = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.attention.head_count")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
let head_dim = hidden_size / num_heads;
|
||||||
|
let rotary_pos_emb = Qwen2_5VisionRotaryEmbedding::new(head_dim / 2, None);
|
||||||
|
let rms_norm_eps = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.attention.layer_norm_epsilon")?
|
||||||
|
.to_f32()? as f64;
|
||||||
|
let mut blocks = Vec::new();
|
||||||
|
let num_block = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.block_count")?
|
||||||
|
.to_u32()? as usize;
|
||||||
|
for i in 0..num_block {
|
||||||
|
let prefix = format!("v.blk.{i}");
|
||||||
|
// let block = Qwen3VLVisionBlock::new(config.clone(), vb_blocks.pp(i))?;
|
||||||
|
let block = Qwen3VLVisionBlock::new_from_gguf(mmproj_gguf, &prefix, rms_norm_eps)?;
|
||||||
|
blocks.push(block);
|
||||||
|
}
|
||||||
|
let merger = Qwen3VLVisionPatchMerger::new_from_gguf(
|
||||||
|
mmproj_gguf,
|
||||||
|
rms_norm_eps,
|
||||||
|
false,
|
||||||
|
hidden_size,
|
||||||
|
spatial_merge_size,
|
||||||
|
"v.post_ln",
|
||||||
|
"mm.0",
|
||||||
|
"mm.2",
|
||||||
|
)?;
|
||||||
|
let mut deepstack_merger_list = Vec::new();
|
||||||
|
let is_deepstack = mmproj_gguf
|
||||||
|
.get_matedata("clip.vision.is_deepstack_layers")?
|
||||||
|
.to_vec()?
|
||||||
|
.iter()
|
||||||
|
.map(|b| b.to_bool())
|
||||||
|
.collect::<Result<Vec<bool>, candle_core::Error>>()?;
|
||||||
|
let deepstack_visual_indexes = is_deepstack
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, &b)| if b { Some(i) } else { None })
|
||||||
|
.collect::<Vec<usize>>();
|
||||||
|
for i in &deepstack_visual_indexes {
|
||||||
|
let prefix = format!("v.deepstack.{i}");
|
||||||
|
let merger_i = Qwen3VLVisionPatchMerger::new_from_gguf(
|
||||||
|
mmproj_gguf,
|
||||||
|
rms_norm_eps,
|
||||||
|
true,
|
||||||
|
hidden_size,
|
||||||
|
spatial_merge_size,
|
||||||
|
&format!("{prefix}.norm"),
|
||||||
|
&format!("{prefix}.fc1"),
|
||||||
|
&format!("{prefix}.fc2"),
|
||||||
|
)?;
|
||||||
|
deepstack_merger_list.push(merger_i);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
spatial_merge_size,
|
||||||
|
patch_embed,
|
||||||
|
pos_embed,
|
||||||
|
num_grid_per_side: num_grid_per_side as u32,
|
||||||
|
rotary_pos_emb,
|
||||||
|
blocks,
|
||||||
|
merger,
|
||||||
|
deepstack_visual_indexes,
|
||||||
|
deepstack_merger_list,
|
||||||
|
dtype: DType::F32,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fast_pos_embed_interpolate(&self, grid_thw: &Tensor) -> Result<Tensor> {
|
pub fn fast_pos_embed_interpolate(&self, grid_thw: &Tensor) -> Result<Tensor> {
|
||||||
let mut idx_list = vec![vec![]; 4];
|
let mut idx_list = vec![vec![]; 4];
|
||||||
let mut weight_list = vec![vec![]; 4];
|
let mut weight_list = vec![vec![]; 4];
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ impl Qwen3VLProcessor {
|
|||||||
max_frames: 768,
|
max_frames: 768,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_qwen3_5_default(device: &Device, dtype: DType) -> Result<Self> {
|
pub fn new_qwen3_5_default(device: &Device, dtype: DType) -> Result<Self> {
|
||||||
let img_process_cfg = PreprocessorConfig::qwen3_5_img_default();
|
let img_process_cfg = PreprocessorConfig::qwen3_5_img_default();
|
||||||
let video_process_cfg = PreprocessorConfig::qwen3_5_video_default();
|
let video_process_cfg = PreprocessorConfig::qwen3_5_video_default();
|
||||||
|
|||||||
+40
-15
@@ -5,24 +5,40 @@ use aha::{
|
|||||||
models::{GenerateModel, qwen3_5::generate::Qwen3_5GenerateModel},
|
models::{GenerateModel, qwen3_5::generate::Qwen3_5GenerateModel},
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
// use candle_core::{Device, quantized::gguf_file};
|
// use candle_core::{DType, Device, quantized::gguf_file};
|
||||||
#[test]
|
#[test]
|
||||||
fn gguf_test() -> Result<()> {
|
fn gguf_test() -> Result<()> {
|
||||||
// cargo test -r -F cuda --test test_gguf_qwen3_5 gguf_test -- --nocapture
|
// RUST_BACKTRACE=1 cargo test -r -F cuda --test test_gguf_qwen3_5 gguf_test -- --nocapture
|
||||||
// let path = "/home/jhq/.aha/Qwen/Qwen3.5-4B-GGUF/Qwen3.5-4B-Q5_K_M.gguf"; // 有问题
|
// let model_path = "/home/jhq/.aha/Qwen/Qwen3.5-4B-GGUF/Qwen3.5-4B-Q5_K_M.gguf"; // 有问题
|
||||||
// let path = "/home/jhq/.aha/Qwen/Qwen3.5-2B-GGUF/Qwen3.5-2B-Q6_K.gguf";
|
// let mmproj_path = "/home/jhq/.aha/Qwen/Qwen3.5-4B-GGUF/mmproj-F16.gguf";
|
||||||
let path = "/home/jhq/.aha/Qwen/Qwen3.5-0.8B-GGUF/Qwen3.5-0.8B-Q4_K_M.gguf";
|
// let model_path = "/home/jhq/.aha/Qwen/Qwen3.5-2B-GGUF/Qwen3.5-2B-Q6_K.gguf";
|
||||||
// let mut file = std::fs::File::open(path)?;
|
let model_path = "/home/jhq/.aha/Qwen/Qwen3.5-0.8B-GGUF/Qwen3.5-0.8B-Q4_K_M.gguf";
|
||||||
// let model = gguf_file::Content::read(&mut file)?;
|
let mmproj_path = "/home/jhq/.aha/Qwen/Qwen3.5-0.8B-GGUF/mmproj-F16.gguf";
|
||||||
// println!("group_count: {:?}", model.metadata.get("qwen35.ssm.group_count"));
|
// let mut model_file = std::fs::File::open(model_path)?;
|
||||||
// println!("time_step_rank: {:?}", model.metadata.get("qwen35.ssm.time_step_rank"));
|
// let model = gguf_file::Content::read(&mut model_file)?;
|
||||||
// println!("state_size: {:?}", model.metadata.get("qwen35.ssm.state_size"));
|
|
||||||
// for (key, value) in model.metadata {
|
// for (key, value) in model.metadata {
|
||||||
// if key.contains("tokenizer") {
|
// if key.contains("tokeni") {
|
||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
// println!("{key}: {:#?}", value);
|
// println!("{key}: {:#?}", value);
|
||||||
// }
|
// }
|
||||||
|
// let mut mmproj_file = std::fs::File::open(mmproj_path)?;
|
||||||
|
// let mmproj = gguf_file::Content::read(&mut mmproj_file)?;
|
||||||
|
// println!("model: {:#?}", mmproj.tensor_infos.keys());
|
||||||
|
// println!("group_count: {:?}", model.metadata.get("qwen35.ssm.group_count"));
|
||||||
|
// println!("time_step_rank: {:?}", model.metadata.get("qwen35.ssm.time_step_rank"));
|
||||||
|
// println!("state_size: {:?}", model.metadata.get("qwen35.ssm.state_size"));
|
||||||
|
// for (key, value) in mmproj.metadata {
|
||||||
|
// println!("{key}: {:#?}", value);
|
||||||
|
// }
|
||||||
|
// let device = Device::new_cuda(0)?;
|
||||||
|
// let mut mmproj_gguf = Gguf::new(mmproj, mmproj_file, device.clone());
|
||||||
|
// let weight = mmproj_gguf.get_dequantized("v.position_embd.weight")?;
|
||||||
|
// println!("weight: {:?}", weight);
|
||||||
|
// let conv3d_weight_1 = mmproj_gguf.get_dequantized("v.patch_embd.weight.1")?;
|
||||||
|
// println!("conv3d_weight_1: {}", conv3d_weight_1);
|
||||||
|
// let conv3d_bias = mmproj_gguf.get_dequantized("v.patch_embd.bias")?;
|
||||||
|
// println!("conv3d_bias: {}", conv3d_bias);
|
||||||
// println!("model: {:?}", model.magic);
|
// println!("model: {:?}", model.magic);
|
||||||
// println!("generat.type: {:#?}", model.metadata.keys());
|
// println!("generat.type: {:#?}", model.metadata.keys());
|
||||||
// println!("tokenizer.ggml.eos_token_id: {:#?}", model.metadata.get("tokenizer.ggml.eos_token_id"));
|
// println!("tokenizer.ggml.eos_token_id: {:#?}", model.metadata.get("tokenizer.ggml.eos_token_id"));
|
||||||
@@ -33,19 +49,28 @@ fn gguf_test() -> Result<()> {
|
|||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "image",
|
||||||
"text": "你如何看待AI"
|
"image_url":
|
||||||
|
{
|
||||||
|
"url": "file://./assets/img/ocr_test1.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "请分析图片并提取所有可见文本内容,按从左到右、从上到下的布局,返回纯文本"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mes: ChatCompletionParameters = serde_json::from_str(message)?;
|
let mes: ChatCompletionParameters = serde_json::from_str(message)?;
|
||||||
let i_start = Instant::now();
|
let i_start = Instant::now();
|
||||||
let mut gguf_qwen3_5 = Qwen3_5GenerateModel::init_from_gguf(path, None, None)?;
|
let mut gguf_qwen3_5 =
|
||||||
|
Qwen3_5GenerateModel::init_from_gguf(model_path, mmproj_path.into(), None)?;
|
||||||
let i_duration = i_start.elapsed();
|
let i_duration = i_start.elapsed();
|
||||||
println!("Time elapsed in load model is: {:?}", i_duration);
|
println!("Time elapsed in load model is: {:?}", i_duration);
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ fn qwen3_5_generate() -> Result<()> {
|
|||||||
"type": "image",
|
"type": "image",
|
||||||
"image_url":
|
"image_url":
|
||||||
{
|
{
|
||||||
"url": "file:///home/jhq/Downloads/gougou1.jpg"
|
"url": "file://./assets/img/ocr_test1.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "描述这张图片."
|
"text": "OCR"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fn qwen3vl_thinking_generate() -> Result<()> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "请分析图片并提取所有可见文本内容,按从左到右、从上到下的布局,返回纯文本"
|
"text": "OCR"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ fn qwen3vl_thinking_generate() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn qwen3vl_generate() -> Result<()> {
|
fn qwen3vl_generate() -> Result<()> {
|
||||||
// test with cuda: RUST_BACKTRACE=1 cargo test -F cuda,ffmpeg --test test_qwen3vl qwen3vl_generate -r -- --nocapture
|
// test with cuda: RUST_BACKTRACE=1 cargo test -F cuda --test test_qwen3vl qwen3vl_generate -r -- --nocapture
|
||||||
|
|
||||||
let save_dir =
|
let save_dir =
|
||||||
aha::utils::get_default_save_dir().ok_or(anyhow::anyhow!("Failed to get save dir"))?;
|
aha::utils::get_default_save_dir().ok_or(anyhow::anyhow!("Failed to get save dir"))?;
|
||||||
@@ -73,15 +73,15 @@ fn qwen3vl_generate() -> Result<()> {
|
|||||||
"role": "user",
|
"role": "user",
|
||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"type": "video",
|
"type": "image",
|
||||||
"video_url":
|
"image_url":
|
||||||
{
|
{
|
||||||
"url": "./assets/video/video_test.mp4"
|
"url": "file://./assets/img/ocr_test1.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "视频中发生了什么?"
|
"text": "OCR"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user