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::KvCache, Activation, VarBuilder};
7use std::sync::Arc;
8
9#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
10pub struct Config {
11 pub vocab_size: usize,
12 pub hidden_size: usize,
13 pub intermediate_size: usize,
14 pub num_hidden_layers: usize,
15 pub num_attention_heads: usize,
16 pub num_key_value_heads: usize,
17 pub max_position_embeddings: usize,
18 pub tie_word_embeddings: bool,
19 pub rope_theta: f64,
20 pub rms_norm_eps: f64,
21 pub hidden_act: Activation,
22 pub attention_bias: Option<bool>,
24 pub attention_dropout: Option<f64>,
25 pub mlp_bias: Option<bool>,
26 pub sliding_window: Option<usize>,
27 pub use_sliding_window: Option<bool>,
28 pub rope_scaling: Option<serde_json::Value>,
29 pub bos_token_id: Option<u32>,
30 pub eos_token_id: Option<u32>,
31 pub pad_token_id: Option<u32>,
32 pub max_window_layers: Option<usize>,
33 pub no_rope_layers: Option<Vec<usize>>,
35 pub no_rope_layer_interval: Option<usize>,
36}
37
38impl Config {
39 pub fn should_skip_rope(&self, layer_idx: usize) -> bool {
40 if let Some(ref no_rope_layers) = self.no_rope_layers {
42 if layer_idx < no_rope_layers.len() {
43 return no_rope_layers[layer_idx] == 0;
45 }
46 }
47
48 if let Some(interval) = self.no_rope_layer_interval {
52 return (layer_idx + 1).is_multiple_of(interval);
53 }
54
55 false
57 }
58
59 pub fn head_dim(&self) -> usize {
61 self.hidden_size / self.num_attention_heads
62 }
63}
64
65#[derive(Debug, Clone)]
66pub(crate) struct SmolLM3RotaryEmbedding {
67 sin: Tensor,
68 cos: Tensor,
69}
70
71impl SmolLM3RotaryEmbedding {
72 pub(crate) fn new(dtype: DType, cfg: &Config, dev: &Device) -> Result<Self> {
73 let dim = cfg.head_dim();
74 let max_seq_len = cfg.max_position_embeddings;
75 let inv_freq: Vec<_> = (0..dim)
76 .step_by(2)
77 .map(|i| 1f32 / cfg.rope_theta.powf(i as f64 / dim as f64) as f32)
78 .collect();
79 let inv_freq_len = inv_freq.len();
80 let inv_freq = Tensor::from_vec(inv_freq, (1, inv_freq_len), dev)?.to_dtype(DType::F32)?;
81 let t = Tensor::arange(0u32, max_seq_len as u32, dev)?
82 .to_dtype(DType::F32)?
83 .reshape((max_seq_len, 1))?;
84 let freqs = t.matmul(&inv_freq)?;
85 Ok(Self {
86 sin: freqs.sin()?.to_dtype(dtype)?,
87 cos: freqs.cos()?.to_dtype(dtype)?,
88 })
89 }
90
91 pub(crate) fn apply(&self, q: &Tensor, k: &Tensor, offset: usize) -> Result<(Tensor, Tensor)> {
93 let (_, _, seq_len, _) = q.dims4()?;
94 let cos = self.cos.narrow(0, offset, seq_len)?;
95 let sin = self.sin.narrow(0, offset, seq_len)?;
96 let q_embed = candle_nn::rotary_emb::rope(&q.contiguous()?, &cos, &sin)?;
97 let k_embed = candle_nn::rotary_emb::rope(&k.contiguous()?, &cos, &sin)?;
98 Ok((q_embed, k_embed))
99 }
100}
101
102#[derive(Debug, Clone)]
103pub(crate) struct SmolLM3MLP {
104 gate_proj: Linear,
105 up_proj: Linear,
106 down_proj: Linear,
107 act_fn: Activation,
108}
109
110impl SmolLM3MLP {
111 pub(crate) fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
112 let mlp_bias = cfg.mlp_bias.unwrap_or(false);
113 Ok(Self {
114 gate_proj: linear_b(
115 cfg.hidden_size,
116 cfg.intermediate_size,
117 mlp_bias,
118 vb.pp("gate_proj"),
119 )?,
120 up_proj: linear_b(
121 cfg.hidden_size,
122 cfg.intermediate_size,
123 mlp_bias,
124 vb.pp("up_proj"),
125 )?,
126 down_proj: linear_b(
127 cfg.intermediate_size,
128 cfg.hidden_size,
129 mlp_bias,
130 vb.pp("down_proj"),
131 )?,
132 act_fn: cfg.hidden_act,
133 })
134 }
135}
136
137impl Module for SmolLM3MLP {
138 fn forward(&self, x: &Tensor) -> Result<Tensor> {
139 let lhs = x.apply(&self.gate_proj)?.apply(&self.act_fn)?;
140 let rhs = x.apply(&self.up_proj)?;
141 (lhs * rhs)?.apply(&self.down_proj)
142 }
143}
144
145#[derive(Debug, Clone)]
146pub(crate) struct SmolLM3Attention {
147 q_proj: Linear,
149 k_proj: Linear,
150 v_proj: Linear,
151 o_proj: Linear,
152 num_heads: usize,
154 num_kv_heads: usize,
155 num_kv_groups: usize,
156 head_dim: usize,
157 hidden_size: usize,
158 rotary_emb: Option<Arc<SmolLM3RotaryEmbedding>>,
160 kv_cache: KvCache,
161 skip_rope: bool,
163}
164
165impl SmolLM3Attention {
166 pub(crate) fn new(
167 cfg: &Config,
168 layer_idx: usize,
169 rotary_emb: Option<Arc<SmolLM3RotaryEmbedding>>,
170 vb: VarBuilder,
171 ) -> Result<Self> {
172 let use_sliding_window = cfg.use_sliding_window.unwrap_or(false);
173 if use_sliding_window {
174 candle::bail!("sliding window is not supported")
175 }
176
177 let head_dim = cfg.head_dim();
178 let num_heads = cfg.num_attention_heads;
179 let num_kv_heads = cfg.num_key_value_heads;
180 let num_kv_groups = num_heads / num_kv_heads;
181
182 let attention_bias = cfg.attention_bias.unwrap_or(false);
183
184 let q_proj = linear_b(
185 cfg.hidden_size,
186 num_heads * head_dim,
187 attention_bias,
188 vb.pp("q_proj"),
189 )?;
190
191 let k_proj = linear_b(
192 cfg.hidden_size,
193 num_kv_heads * head_dim,
194 attention_bias,
195 vb.pp("k_proj"),
196 )?;
197
198 let v_proj = linear_b(
199 cfg.hidden_size,
200 num_kv_heads * head_dim,
201 attention_bias,
202 vb.pp("v_proj"),
203 )?;
204 let o_proj = linear_b(
205 num_heads * head_dim,
206 cfg.hidden_size,
207 attention_bias,
208 vb.pp("o_proj"),
209 )?;
210
211 let hidden_size = head_dim * cfg.num_attention_heads;
213
214 let kv_cache = KvCache::new(2, 512);
217
218 let skip_rope = cfg.should_skip_rope(layer_idx);
220
221 Ok(Self {
222 q_proj,
223 k_proj,
224 v_proj,
225 o_proj,
226 num_heads,
227 num_kv_heads,
228 num_kv_groups,
229 head_dim,
230 hidden_size,
231 rotary_emb,
232 kv_cache,
233 skip_rope,
234 })
235 }
236
237 pub(crate) fn forward(
238 &mut self,
239 x: &Tensor,
240 attn_mask: Option<&Tensor>,
241 offset: usize,
242 ) -> Result<Tensor> {
243 let (b, l, _) = x.dims3()?;
244
245 let q = self.q_proj.forward(x)?;
247 let k = self.k_proj.forward(x)?;
248 let v = self.v_proj.forward(x)?;
249
250 let q = q
252 .reshape((b, l, self.num_heads, self.head_dim))?
253 .transpose(1, 2)?;
254 let k = k
255 .reshape((b, l, self.num_kv_heads, self.head_dim))?
256 .transpose(1, 2)?;
257 let v = v
258 .reshape((b, l, self.num_kv_heads, self.head_dim))?
259 .transpose(1, 2)?;
260
261 let (q, k) = if self.skip_rope {
263 (q.contiguous()?, k.contiguous()?)
265 } else {
266 if let Some(ref rope) = self.rotary_emb {
268 rope.apply(&q, &k, offset)?
269 } else {
270 (q, k)
271 }
272 };
273
274 if offset == 0 {
277 self.kv_cache.reset();
278 }
279 let (k, v) = self.kv_cache.append(&k.contiguous()?, &v.contiguous()?)?;
280
281 let k = repeat_kv(k, self.num_kv_groups)?;
283 let v = repeat_kv(v, self.num_kv_groups)?;
284
285 let scale = 1.0 / (self.head_dim as f64).sqrt();
287 let mut scores = (q.matmul(&k.transpose(2, 3)?)? * scale)?;
288 if let Some(m) = attn_mask {
289 scores = scores.broadcast_add(m)?;
290 }
291 let probs = candle_nn::ops::softmax_last_dim(&scores)?;
292 let ctx = probs.matmul(&v)?; ctx.transpose(1, 2)?
296 .reshape((b, l, self.hidden_size))?
297 .apply(&self.o_proj)
298 }
299
300 pub fn clear_kv_cache(&mut self) {
301 self.kv_cache.reset();
302 }
303}
304
305#[derive(Debug, Clone)]
306pub(crate) struct DecoderLayer {
307 self_attn: SmolLM3Attention,
308 mlp: SmolLM3MLP,
309 ln1: RmsNorm,
310 ln2: RmsNorm,
311}
312
313impl DecoderLayer {
314 fn new(
315 cfg: &Config,
316 layer_idx: usize,
317 rotary: Option<Arc<SmolLM3RotaryEmbedding>>,
318 vb: VarBuilder,
319 ) -> Result<Self> {
320 let self_attn = SmolLM3Attention::new(cfg, layer_idx, rotary, vb.pp("self_attn"))?;
321 let mlp = SmolLM3MLP::new(cfg, vb.pp("mlp"))?;
322 let ln1 = RmsNorm::new(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?;
323 let ln2 = RmsNorm::new(
324 cfg.hidden_size,
325 cfg.rms_norm_eps,
326 vb.pp("post_attention_layernorm"),
327 )?;
328 Ok(Self {
329 self_attn,
330 mlp,
331 ln1,
332 ln2,
333 })
334 }
335
336 fn forward(&mut self, x: &Tensor, mask: Option<&Tensor>, offset: usize) -> Result<Tensor> {
337 let h = self.ln1.forward(x)?;
338 let h = self.self_attn.forward(&h, mask, offset)?;
339 let x = (x + h)?;
340 let h2 = self.ln2.forward(&x)?;
341 let h2 = h2.apply(&self.mlp)?;
342 x + h2
343 }
344
345 pub fn clear_kv_cache(&mut self) {
346 self.self_attn.clear_kv_cache();
347 }
348}
349
350#[derive(Debug, Clone)]
351pub struct Model {
352 pub(crate) embed_tokens: candle_nn::Embedding,
353 pub(crate) layers: Vec<DecoderLayer>,
354 pub(crate) norm: RmsNorm,
355 device: Device,
356 dtype: DType,
357}
358
359impl Model {
360 pub fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
361 let embed_tokens =
362 candle_nn::embedding(cfg.vocab_size, cfg.hidden_size, vb.pp("model.embed_tokens"))?;
363
364 let needs_rope = (0..cfg.num_hidden_layers).any(|i| !cfg.should_skip_rope(i));
366 let rotary = if needs_rope {
367 Some(Arc::new(SmolLM3RotaryEmbedding::new(
368 vb.dtype(),
369 cfg,
370 vb.device(),
371 )?))
372 } else {
373 None
374 };
375
376 let mut layers = Vec::with_capacity(cfg.num_hidden_layers);
377 let vb_l = vb.pp("model.layers");
378 for i in 0..cfg.num_hidden_layers {
379 layers.push(DecoderLayer::new(cfg, i, rotary.clone(), vb_l.pp(i))?);
380 }
381 Ok(Self {
382 embed_tokens,
383 layers,
384 norm: RmsNorm::new(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("model.norm"))?,
385 device: vb.device().clone(),
386 dtype: vb.dtype(),
387 })
388 }
389
390 pub fn clear_kv_cache(&mut self) {
391 for l in &mut self.layers {
392 l.clear_kv_cache();
393 }
394 }
395
396 fn causal_mask(
397 &self,
398 b: usize,
399 tgt: usize,
400 offset: usize,
401 sw: Option<usize>,
402 ) -> Result<Tensor> {
403 let minf = f32::NEG_INFINITY;
404 let mask: Vec<_> = (0..tgt)
405 .flat_map(|i| {
406 (0..(tgt + offset)).map(move |j| {
407 let past_ok = j <= i + offset;
408 let sw_ok = match sw {
409 Some(w) => (i + offset) as i64 - j as i64 <= w as i64,
410 None => true,
411 };
412 if past_ok && sw_ok {
413 0.
414 } else {
415 minf
416 }
417 })
418 })
419 .collect();
420 Tensor::from_slice(&mask, (b, 1, tgt, tgt + offset), &self.device)?.to_dtype(self.dtype)
421 }
422
423 pub fn forward(&mut self, input: &Tensor, offset: usize) -> Result<Tensor> {
424 let (b, l) = input.dims2()?;
425
426 let mut h = self.embed_tokens.forward(input)?;
427
428 let causal = if l == 1 {
429 None
430 } else {
431 Some(self.causal_mask(b, l, offset, None)?)
432 };
433
434 for layer in &mut self.layers {
435 h = layer.forward(&h, causal.as_ref(), offset)?;
436 }
437 self.norm.forward(&h)
438 }
439}
440
441#[derive(Debug, Clone)]
442pub struct ModelForCausalLM {
443 base: Model,
444 lm_head: Linear,
445}
446
447impl ModelForCausalLM {
448 pub fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
449 let base = Model::new(cfg, vb.clone())?;
450 let lm_head = if cfg.tie_word_embeddings {
451 Linear::from_weights(base.embed_tokens.embeddings().clone(), None)
452 } else {
453 linear_no_bias(cfg.hidden_size, cfg.vocab_size, vb.pp("lm_head"))?
454 };
455 Ok(Self { base, lm_head })
456 }
457
458 pub fn forward(&mut self, input: &Tensor, offset: usize) -> Result<Tensor> {
459 let (_, l) = input.dims2()?;
460
461 self.base
462 .forward(input, offset)?
463 .narrow(1, l - 1, 1)?
464 .apply(&self.lm_head)
465 }
466
467 pub fn clear_kv_cache(&mut self) {
468 self.base.clear_kv_cache();
469 }
470}