1use crate::{
2 models::with_tracing::{linear_b, linear_no_bias, Linear, RmsNorm},
3 utils::repeat_kv,
4};
5use candle::{DType, Device, Module, Result, Tensor};
6use candle_nn::{kv_cache::ConcatKvCache, Activation, VarBuilder};
7use std::sync::Arc;
8
9#[cfg(feature = "flash-attn")]
10use candle_flash_attn;
11
12#[cfg(not(feature = "flash-attn"))]
13use candle_nn::attention::{flash_attn, AttnMask};
14
15#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
16pub struct Config {
17 pub vocab_size: usize,
18 pub hidden_size: usize,
19 pub intermediate_size: usize,
20 pub num_hidden_layers: usize,
21 pub num_attention_heads: usize,
22 pub head_dim: usize,
23 pub attention_bias: bool,
24 pub num_key_value_heads: usize,
25 pub max_position_embeddings: usize,
26 pub sliding_window: Option<usize>,
27 pub max_window_layers: usize,
28 pub tie_word_embeddings: bool,
29 pub rope_theta: f64,
30 pub rms_norm_eps: f64,
31 pub use_sliding_window: bool,
32 pub hidden_act: Activation,
33}
34
35#[derive(Debug, Clone)]
36pub(crate) struct Qwen3RotaryEmbedding {
37 sin: Tensor,
38 cos: Tensor,
39}
40
41impl Qwen3RotaryEmbedding {
42 pub(crate) fn new(dtype: DType, cfg: &Config, dev: &Device) -> Result<Self> {
43 let dim = cfg.head_dim;
44 let max_seq_len = cfg.max_position_embeddings;
45 let inv_freq: Vec<_> = (0..dim)
46 .step_by(2)
47 .map(|i| 1f32 / cfg.rope_theta.powf(i as f64 / dim as f64) as f32)
48 .collect();
49 let inv_freq_len = inv_freq.len();
50 let inv_freq = Tensor::from_vec(inv_freq, (1, inv_freq_len), dev)?.to_dtype(DType::F32)?;
51 let t = Tensor::arange(0u32, max_seq_len as u32, dev)?
52 .to_dtype(DType::F32)?
53 .reshape((max_seq_len, 1))?;
54 let freqs = t.matmul(&inv_freq)?;
55 Ok(Self {
56 sin: freqs.sin()?.to_dtype(dtype)?,
57 cos: freqs.cos()?.to_dtype(dtype)?,
58 })
59 }
60
61 pub(crate) fn apply(&self, q: &Tensor, k: &Tensor, offset: usize) -> Result<(Tensor, Tensor)> {
63 let (_, _, seq_len, _) = q.dims4()?;
64 let cos = self.cos.narrow(0, offset, seq_len)?;
65 let sin = self.sin.narrow(0, offset, seq_len)?;
66 let q_embed = candle_nn::rotary_emb::rope(&q.contiguous()?, &cos, &sin)?;
67 let k_embed = candle_nn::rotary_emb::rope(&k.contiguous()?, &cos, &sin)?;
68 Ok((q_embed, k_embed))
69 }
70}
71
72#[derive(Debug, Clone)]
73pub(crate) struct Qwen3MLP {
74 gate_proj: Linear,
75 up_proj: Linear,
76 down_proj: Linear,
77 act_fn: Activation,
78}
79
80impl Qwen3MLP {
81 pub(crate) fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
82 Ok(Self {
83 gate_proj: linear_no_bias(cfg.hidden_size, cfg.intermediate_size, vb.pp("gate_proj"))?,
84 up_proj: linear_no_bias(cfg.hidden_size, cfg.intermediate_size, vb.pp("up_proj"))?,
85 down_proj: linear_no_bias(cfg.intermediate_size, cfg.hidden_size, vb.pp("down_proj"))?,
86 act_fn: cfg.hidden_act,
87 })
88 }
89}
90
91impl Module for Qwen3MLP {
92 fn forward(&self, x: &Tensor) -> Result<Tensor> {
93 let lhs = x.apply(&self.gate_proj)?.apply(&self.act_fn)?;
94 let rhs = x.apply(&self.up_proj)?;
95 (lhs * rhs)?.apply(&self.down_proj)
96 }
97}
98
99#[derive(Debug, Clone)]
100pub(crate) struct Qwen3Attention {
101 q_proj: Linear,
103 k_proj: Linear,
104 v_proj: Linear,
105 o_proj: Linear,
106 q_norm: RmsNorm,
108 k_norm: RmsNorm,
109 num_heads: usize,
111 num_kv_heads: usize,
112 num_kv_groups: usize,
113 head_dim: usize,
114 hidden_size: usize,
115 rotary_emb: Arc<Qwen3RotaryEmbedding>,
117 kv_cache: ConcatKvCache,
118}
119
120impl Qwen3Attention {
121 pub(crate) fn new(
122 cfg: &Config,
123 rotary_emb: Arc<Qwen3RotaryEmbedding>,
124 vb: VarBuilder,
125 ) -> Result<Self> {
126 if cfg.use_sliding_window {
127 candle::bail!("sliding window is not supported")
128 }
129
130 let head_dim = cfg.head_dim;
131 let num_heads = cfg.num_attention_heads;
132 let num_kv_heads = cfg.num_key_value_heads;
133 let num_kv_groups = num_heads / num_kv_heads;
134
135 let q_proj = linear_b(
136 cfg.hidden_size,
137 num_heads * head_dim,
138 cfg.attention_bias,
139 vb.pp("q_proj"),
140 )?;
141 let k_proj = linear_b(
142 cfg.hidden_size,
143 num_kv_heads * head_dim,
144 cfg.attention_bias,
145 vb.pp("k_proj"),
146 )?;
147 let v_proj = linear_b(
148 cfg.hidden_size,
149 num_kv_heads * head_dim,
150 cfg.attention_bias,
151 vb.pp("v_proj"),
152 )?;
153 let o_proj = linear_b(
154 num_heads * head_dim,
155 cfg.hidden_size,
156 cfg.attention_bias,
157 vb.pp("o_proj"),
158 )?;
159
160 let q_norm = RmsNorm::new(head_dim, cfg.rms_norm_eps, vb.pp("q_norm"))?;
161 let k_norm = RmsNorm::new(head_dim, cfg.rms_norm_eps, vb.pp("k_norm"))?;
162
163 let hidden_size = head_dim * cfg.num_attention_heads;
165
166 let kv_cache = ConcatKvCache::new(2);
169
170 Ok(Self {
171 q_proj,
172 k_proj,
173 v_proj,
174 o_proj,
175 q_norm,
176 k_norm,
177 num_heads,
178 num_kv_heads,
179 num_kv_groups,
180 head_dim,
181 hidden_size,
182 rotary_emb,
183 kv_cache,
184 })
185 }
186
187 pub(crate) fn forward(
188 &mut self,
189 x: &Tensor,
190 attn_mask: Option<&Tensor>,
191 offset: usize,
192 ) -> Result<Tensor> {
193 let (b, l, _) = x.dims3()?;
194
195 let q = self.q_proj.forward(x)?;
197 let k = self.k_proj.forward(x)?;
198 let v = self.v_proj.forward(x)?;
199
200 let q = q
202 .reshape((b, l, self.num_heads, self.head_dim))?
203 .transpose(1, 2)?;
204 let k = k
205 .reshape((b, l, self.num_kv_heads, self.head_dim))?
206 .transpose(1, 2)?;
207 let v = v
208 .reshape((b, l, self.num_kv_heads, self.head_dim))?
209 .transpose(1, 2)?;
210
211 let q_flat = q.flatten(0, 2)?;
213 let k_flat = k.flatten(0, 2)?;
214 let q_flat = self.q_norm.forward(&q_flat)?;
215 let k_flat = self.k_norm.forward(&k_flat)?;
216 let q = q_flat.reshape((b, self.num_heads, l, self.head_dim))?;
217 let k = k_flat.reshape((b, self.num_kv_heads, l, self.head_dim))?;
218
219 let (q, k) = self.rotary_emb.apply(&q, &k, offset)?;
221
222 let (k, v) = self.kv_cache.append(&k, &v)?;
224
225 let on_cpu = x.device().is_cpu();
230
231 #[cfg(not(feature = "flash-attn"))]
232 if on_cpu {
233 return self.forward_cpu_flash_attn(&q, &k, &v, offset, b, l);
234 }
235 #[cfg(feature = "flash-attn")]
236 if !on_cpu {
237 return self.forward_flash_attn(&q, &k, &v, offset, b, l);
238 }
239
240 self.forward_standard_attn(&q, &k, &v, attn_mask, b, l)
241 }
242
243 #[cfg(feature = "flash-attn")]
245 fn forward_flash_attn(
246 &self,
247 q: &Tensor,
248 k: &Tensor,
249 v: &Tensor,
250 _offset: usize,
251 b: usize,
252 l: usize,
253 ) -> Result<Tensor> {
254 let q = q.transpose(1, 2)?.contiguous()?;
256 let k = k.transpose(1, 2)?.contiguous()?;
257 let v = v.transpose(1, 2)?.contiguous()?;
258
259 let scale = 1.0 / (self.head_dim as f32).sqrt();
260 let causal = l > 1;
261 let ctx = candle_flash_attn::flash_attn(&q, &k, &v, scale, causal)?;
262
263 ctx.reshape((b, l, self.hidden_size))?.apply(&self.o_proj)
265 }
266
267 #[cfg(not(feature = "flash-attn"))]
273 fn forward_cpu_flash_attn(
274 &self,
275 q: &Tensor,
276 k: &Tensor,
277 v: &Tensor,
278 offset: usize,
279 b: usize,
280 l: usize,
281 ) -> Result<Tensor> {
282 let q = q.transpose(1, 2)?.contiguous()?;
284 let k = k.transpose(1, 2)?.contiguous()?;
285 let v = v.transpose(1, 2)?.contiguous()?;
286
287 let scale = 1.0 / (self.head_dim as f32).sqrt();
288
289 let ctx = match q.dtype() {
290 DType::F32 => flash_attn::<f32>(
291 &q,
292 &k,
293 &v,
294 scale,
295 AttnMask::causal_with_offset(offset),
296 None,
297 None,
298 )?,
299 other => {
303 let q_f32 = q.to_dtype(DType::F32)?;
304 let k_f32 = k.to_dtype(DType::F32)?;
305 let v_f32 = v.to_dtype(DType::F32)?;
306 let ctx_f32 = flash_attn::<f32>(
307 &q_f32,
308 &k_f32,
309 &v_f32,
310 scale,
311 AttnMask::causal_with_offset(offset),
312 None,
313 None,
314 )?;
315 ctx_f32.to_dtype(other)?
316 }
317 };
318
319 let ctx = ctx.transpose(1, 2)?;
321
322 ctx.reshape((b, l, self.hidden_size))?.apply(&self.o_proj)
323 }
324
325 fn forward_standard_attn(
327 &self,
328 q: &Tensor,
329 k: &Tensor,
330 v: &Tensor,
331 attn_mask: Option<&Tensor>,
332 b: usize,
333 l: usize,
334 ) -> Result<Tensor> {
335 let k = repeat_kv(k.clone(), self.num_kv_groups)?.contiguous()?;
337 let v = repeat_kv(v.clone(), self.num_kv_groups)?.contiguous()?;
338
339 let scale = 1.0 / (self.head_dim as f64).sqrt();
341 let mut scores = (q.matmul(&k.transpose(2, 3)?)? * scale)?;
342 if let Some(m) = attn_mask {
343 scores = scores.broadcast_add(m)?;
344 }
345 let probs = candle_nn::ops::softmax_last_dim(&scores)?;
346 let ctx = probs.matmul(&v)?; ctx.transpose(1, 2)?
350 .reshape((b, l, self.hidden_size))?
351 .apply(&self.o_proj)
352 }
353
354 pub(crate) fn clear_kv_cache(&mut self) {
355 self.kv_cache.reset();
356 }
357}
358
359#[derive(Debug, Clone)]
360struct DecoderLayer {
361 self_attn: Qwen3Attention,
362 mlp: Qwen3MLP,
363 ln1: RmsNorm,
364 ln2: RmsNorm,
365}
366
367impl DecoderLayer {
368 fn new(cfg: &Config, rotary: Arc<Qwen3RotaryEmbedding>, vb: VarBuilder) -> Result<Self> {
369 let self_attn = Qwen3Attention::new(cfg, rotary, vb.pp("self_attn"))?;
370 let mlp = Qwen3MLP::new(cfg, vb.pp("mlp"))?;
371 let ln1 = RmsNorm::new(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?;
372 let ln2 = RmsNorm::new(
373 cfg.hidden_size,
374 cfg.rms_norm_eps,
375 vb.pp("post_attention_layernorm"),
376 )?;
377 Ok(Self {
378 self_attn,
379 mlp,
380 ln1,
381 ln2,
382 })
383 }
384
385 fn forward(&mut self, x: &Tensor, mask: Option<&Tensor>, offset: usize) -> Result<Tensor> {
386 let h = self.ln1.forward(x)?;
387 let h = self.self_attn.forward(&h, mask, offset)?;
388 let x = (x + h)?;
389 let h2 = self.ln2.forward(&x)?;
390 let h2 = h2.apply(&self.mlp)?;
391 x + h2
392 }
393
394 fn clear_kv_cache(&mut self) {
395 self.self_attn.clear_kv_cache();
396 }
397}
398
399#[derive(Debug, Clone)]
400pub struct Model {
401 embed_tokens: candle_nn::Embedding,
402 layers: Vec<DecoderLayer>,
403 norm: RmsNorm,
404 device: Device,
405 dtype: DType,
406}
407
408impl Model {
409 pub fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
410 if vb.dtype() == DType::F64 {
414 candle::bail!(
415 "Qwen3 does not support f64; load weights as f32 or bf16 (CPU) or f16/bf16 (GPU)"
416 );
417 }
418 let embed_tokens =
419 candle_nn::embedding(cfg.vocab_size, cfg.hidden_size, vb.pp("model.embed_tokens"))?;
420 let rotary = Arc::new(Qwen3RotaryEmbedding::new(vb.dtype(), cfg, vb.device())?);
421 let mut layers = Vec::with_capacity(cfg.num_hidden_layers);
422 let vb_l = vb.pp("model.layers");
423 for i in 0..cfg.num_hidden_layers {
424 layers.push(DecoderLayer::new(cfg, rotary.clone(), vb_l.pp(i))?);
425 }
426 Ok(Self {
427 embed_tokens,
428 layers,
429 norm: RmsNorm::new(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("model.norm"))?,
430 device: vb.device().clone(),
431 dtype: vb.dtype(),
432 })
433 }
434
435 fn clear_kv_cache(&mut self) {
436 for l in &mut self.layers {
437 l.clear_kv_cache();
438 }
439 }
440
441 fn causal_mask(
442 &self,
443 b: usize,
444 tgt: usize,
445 offset: usize,
446 sw: Option<usize>,
447 ) -> Result<Tensor> {
448 let minf = f32::NEG_INFINITY;
449 let mask: Vec<_> = (0..tgt)
450 .flat_map(|i| {
451 (0..(tgt + offset)).map(move |j| {
452 let past_ok = j <= i + offset;
453 let sw_ok = match sw {
454 Some(w) => (i + offset) as i64 - j as i64 <= w as i64,
455 None => true,
456 };
457 if past_ok && sw_ok {
458 0.
459 } else {
460 minf
461 }
462 })
463 })
464 .collect();
465 Tensor::from_slice(&mask, (b, 1, tgt, tgt + offset), &self.device)?.to_dtype(self.dtype)
466 }
467
468 pub fn forward(&mut self, input: &Tensor, offset: usize) -> Result<Tensor> {
469 let (b, l) = input.dims2()?;
470 let mut h = self.embed_tokens.forward(input)?;
471
472 #[cfg(not(feature = "flash-attn"))]
475 let needs_mask = !self.device.is_cpu() && l > 1;
476 #[cfg(feature = "flash-attn")]
477 let needs_mask = self.device.is_cpu() && l > 1;
478 let causal = if needs_mask {
479 Some(self.causal_mask(b, l, offset, None)?)
480 } else {
481 None
482 };
483
484 for layer in &mut self.layers {
485 h = layer.forward(&h, causal.as_ref(), offset)?;
486 }
487 self.norm.forward(&h)
488 }
489}
490
491#[derive(Debug, Clone)]
492pub struct ModelForCausalLM {
493 base: Model,
494 lm_head: Linear,
495}
496
497impl ModelForCausalLM {
498 pub fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
499 let base = Model::new(cfg, vb.clone())?;
500 let lm_head = if cfg.tie_word_embeddings {
501 Linear::from_weights(base.embed_tokens.embeddings().clone(), None)
502 } else {
503 linear_no_bias(cfg.hidden_size, cfg.vocab_size, vb.pp("lm_head"))?
504 };
505 Ok(Self { base, lm_head })
506 }
507
508 pub fn forward(&mut self, input: &Tensor, offset: usize) -> Result<Tensor> {
509 let (_, l) = input.dims2()?;
510 self.base
511 .forward(input, offset)?
512 .narrow(1, l - 1, 1)?
513 .apply(&self.lm_head)
514 }
515
516 pub fn clear_kv_cache(&mut self) {
517 self.base.clear_kv_cache();
518 }
519}