Skip to main content

candle_transformers/models/
gemma3.rs

1//! Gemma LLM architecture (Google) inference implementation.
2//!
3//! See ["Introducing Gemma 3: The most capable model you can run on a single GPU or TPU"](https://blog.google/technology/developers/gemma-3/)
4//!
5//! Based on implementations from HuggingFace transformers.
6
7use std::sync::Arc;
8
9use candle::{DType, Device, Module, Result, Tensor, D};
10use candle_nn::{linear_b as linear, Activation, Linear, VarBuilder};
11
12#[derive(serde::Deserialize, Debug, Clone)]
13pub struct Config {
14    pub attention_bias: bool,
15    pub head_dim: usize,
16    pub hidden_activation: Activation,
17    pub hidden_size: usize,
18    pub intermediate_size: usize,
19    pub num_attention_heads: usize,
20    pub num_hidden_layers: usize,
21    pub num_key_value_heads: usize,
22    pub rms_norm_eps: f64,
23    pub rope_theta: f64,
24    pub rope_local_base_freq: f64,
25    pub vocab_size: usize,
26    pub final_logit_softcapping: Option<f64>,
27    pub attn_logit_softcapping: Option<f64>,
28    pub query_pre_attn_scalar: usize,
29    pub sliding_window: usize,
30    pub sliding_window_pattern: usize,
31    pub max_position_embeddings: usize,
32}
33
34#[derive(Debug, Clone)]
35struct RmsNorm {
36    weight: Tensor,
37    eps: f64,
38}
39
40impl RmsNorm {
41    fn new(dim: usize, eps: f64, vb: VarBuilder) -> Result<Self> {
42        let weight = vb.get(dim, "weight")?;
43        Ok(Self { weight, eps })
44    }
45}
46
47impl Module for RmsNorm {
48    fn forward(&self, x: &Tensor) -> Result<Tensor> {
49        let x_dtype = x.dtype();
50        let internal_dtype = match x_dtype {
51            DType::F16 | DType::BF16 => DType::F32,
52            d => d,
53        };
54        let hidden_size = x.dim(D::Minus1)?;
55        let x = x.to_dtype(internal_dtype)?;
56        let norm_x = (x.sqr()?.sum_keepdim(D::Minus1)? / hidden_size as f64)?;
57        let x_normed = x.broadcast_div(&(norm_x + self.eps)?.sqrt()?)?;
58        x_normed
59            .to_dtype(x_dtype)?
60            .broadcast_mul(&(&self.weight + 1.0)?)
61    }
62}
63
64#[derive(Debug, Clone)]
65struct RotaryEmbedding {
66    sin: Tensor,
67    cos: Tensor,
68}
69
70impl RotaryEmbedding {
71    fn new(
72        dtype: DType,
73        cfg: &Config,
74        dev: &Device,
75        sliding_window: Option<usize>,
76    ) -> Result<Self> {
77        let dim = cfg.head_dim;
78        let max_seq_len = cfg.max_position_embeddings;
79        let rope_freq = if sliding_window.is_some() {
80            cfg.rope_local_base_freq
81        } else {
82            cfg.rope_theta
83        };
84        let inv_freq: Vec<_> = (0..dim)
85            .step_by(2)
86            .map(|i| 1f32 / rope_freq.powf(i as f64 / dim as f64) as f32)
87            .collect();
88        let inv_freq_len = inv_freq.len();
89        let inv_freq = Tensor::from_vec(inv_freq, (1, inv_freq_len), dev)?.to_dtype(dtype)?;
90        let t = Tensor::arange(0u32, max_seq_len as u32, dev)?
91            .to_dtype(dtype)?
92            .reshape((max_seq_len, 1))?;
93        let freqs = t.matmul(&inv_freq)?;
94        Ok(Self {
95            sin: freqs.sin()?,
96            cos: freqs.cos()?,
97        })
98    }
99
100    fn apply_rotary_emb_qkv(
101        &self,
102        q: &Tensor,
103        k: &Tensor,
104        seqlen_offset: usize,
105    ) -> Result<(Tensor, Tensor)> {
106        let (_b_sz, _h, seq_len, _n_embd) = q.dims4()?;
107        let cos = self.cos.narrow(0, seqlen_offset, seq_len)?;
108        let sin = self.sin.narrow(0, seqlen_offset, seq_len)?;
109        let q_embed = candle_nn::rotary_emb::rope(&q.contiguous()?, &cos, &sin)?;
110        let k_embed = candle_nn::rotary_emb::rope(&k.contiguous()?, &cos, &sin)?;
111        Ok((q_embed, k_embed))
112    }
113}
114
115#[derive(Debug, Clone)]
116#[allow(clippy::upper_case_acronyms)]
117struct MLP {
118    gate_proj: Linear,
119    up_proj: Linear,
120    down_proj: Linear,
121    act_fn: candle_nn::Activation,
122}
123
124impl MLP {
125    fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
126        let hidden_sz = cfg.hidden_size;
127        let intermediate_sz = cfg.intermediate_size;
128        let gate_proj = linear(hidden_sz, intermediate_sz, false, vb.pp("gate_proj"))?;
129        let up_proj = linear(hidden_sz, intermediate_sz, false, vb.pp("up_proj"))?;
130        let down_proj = linear(intermediate_sz, hidden_sz, false, vb.pp("down_proj"))?;
131        Ok(Self {
132            gate_proj,
133            up_proj,
134            down_proj,
135            act_fn: cfg.hidden_activation,
136        })
137    }
138}
139
140impl Module for MLP {
141    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
142        let lhs = xs.apply(&self.gate_proj)?.apply(&self.act_fn)?;
143        let rhs = xs.apply(&self.up_proj)?;
144        (lhs * rhs)?.apply(&self.down_proj)
145    }
146}
147
148#[derive(Debug, Clone)]
149enum KvCache {
150    Normal(candle_nn::kv_cache::KvCache),
151    Rotating(candle_nn::kv_cache::RotatingKvCache),
152}
153
154#[derive(Debug, Clone)]
155struct Attention {
156    q_proj: Linear,
157    k_proj: Linear,
158    v_proj: Linear,
159    o_proj: Linear,
160    q_norm: RmsNorm,
161    k_norm: RmsNorm,
162    num_heads: usize,
163    num_kv_heads: usize,
164    num_kv_groups: usize,
165    head_dim: usize,
166    attn_logit_softcapping: Option<f64>,
167    rotary_emb: Arc<RotaryEmbedding>,
168    kv_cache: KvCache,
169    use_flash_attn: bool,
170}
171
172impl Attention {
173    fn new(
174        rotary_emb: Arc<RotaryEmbedding>,
175        use_flash_attn: bool,
176        cfg: &Config,
177        sliding_window: Option<usize>,
178        vb: VarBuilder,
179    ) -> Result<Self> {
180        let hidden_sz = cfg.hidden_size;
181        let num_heads = cfg.num_attention_heads;
182        let num_kv_heads = cfg.num_key_value_heads;
183        let num_kv_groups = num_heads / num_kv_heads;
184        let head_dim = cfg.head_dim;
185        let bias = cfg.attention_bias;
186        let q_proj = linear(hidden_sz, num_heads * head_dim, bias, vb.pp("q_proj"))?;
187        let k_proj = linear(hidden_sz, num_kv_heads * head_dim, bias, vb.pp("k_proj"))?;
188        let v_proj = linear(hidden_sz, num_kv_heads * head_dim, bias, vb.pp("v_proj"))?;
189        let o_proj = linear(num_heads * head_dim, hidden_sz, bias, vb.pp("o_proj"))?;
190        let q_norm = RmsNorm::new(head_dim, cfg.rms_norm_eps, vb.pp("q_norm"))?;
191        let k_norm = RmsNorm::new(head_dim, cfg.rms_norm_eps, vb.pp("k_norm"))?;
192        let kv_cache = if let Some(sliding_window) = sliding_window {
193            KvCache::Rotating(candle_nn::kv_cache::RotatingKvCache::new(2, sliding_window))
194        } else {
195            KvCache::Normal(candle_nn::kv_cache::KvCache::new(
196                2,
197                cfg.max_position_embeddings,
198            ))
199        };
200        Ok(Self {
201            q_proj,
202            k_proj,
203            v_proj,
204            o_proj,
205            q_norm,
206            k_norm,
207            num_heads,
208            num_kv_heads,
209            num_kv_groups,
210            head_dim,
211            attn_logit_softcapping: cfg.attn_logit_softcapping,
212            rotary_emb,
213            kv_cache,
214            use_flash_attn,
215        })
216    }
217
218    fn forward(
219        &mut self,
220        xs: &Tensor,
221        attention_mask: Option<&Tensor>,
222        seqlen_offset: usize,
223    ) -> Result<Tensor> {
224        let (b_sz, q_len, _) = xs.dims3()?;
225
226        let query_states = self.q_proj.forward(xs)?;
227        let key_states = self.k_proj.forward(xs)?;
228        let value_states = self.v_proj.forward(xs)?;
229
230        let query_states = query_states
231            .reshape((b_sz, q_len, self.num_heads, self.head_dim))?
232            .transpose(1, 2)?;
233        let key_states = key_states
234            .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))?
235            .transpose(1, 2)?;
236        let value_states = value_states
237            .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))?
238            .transpose(1, 2)?;
239        let query_states = self.q_norm.forward(&query_states)?;
240        let key_states = self.k_norm.forward(&key_states)?;
241
242        let (query_states, key_states) =
243            self.rotary_emb
244                .apply_rotary_emb_qkv(&query_states, &key_states, seqlen_offset)?;
245
246        let (key_states, value_states) = match &mut self.kv_cache {
247            KvCache::Normal(cache) => cache.append(&key_states, &value_states)?,
248            KvCache::Rotating(cache) => cache.append(&key_states, &value_states)?,
249        };
250
251        let key_states = crate::utils::repeat_kv(key_states, self.num_kv_groups)?.contiguous()?;
252        let value_states =
253            crate::utils::repeat_kv(value_states, self.num_kv_groups)?.contiguous()?;
254
255        let attn_output = if self.use_flash_attn {
256            // flash-attn expects (b_sz, seq_len, nheads, head_dim)
257            let q = query_states.transpose(1, 2)?;
258            let k = key_states.transpose(1, 2)?;
259            let v = value_states.transpose(1, 2)?;
260            let scale = 1f32 / (self.head_dim as f32).sqrt();
261            flash_attn(&q, &k, &v, scale, attention_mask.is_some())?.transpose(1, 2)?
262        } else {
263            let scale = 1f64 / f64::sqrt(self.head_dim as f64);
264            let attn_weights = (query_states.matmul(&key_states.transpose(2, 3)?)? * scale)?;
265
266            let attn_weights = match self.attn_logit_softcapping {
267                None => attn_weights,
268                Some(sc) => ((attn_weights / sc)?.tanh()? * sc)?,
269            };
270
271            let attn_weights = match attention_mask {
272                None => attn_weights,
273                Some(mask) => attn_weights.broadcast_add(mask)?,
274            };
275            let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?;
276            attn_weights.matmul(&value_states)?
277        };
278        attn_output
279            .transpose(1, 2)?
280            .reshape((b_sz, q_len, ()))?
281            .apply(&self.o_proj)
282    }
283
284    fn clear_kv_cache(&mut self) {
285        match &mut self.kv_cache {
286            KvCache::Normal(c) => c.reset(),
287            KvCache::Rotating(c) => c.reset(),
288        }
289    }
290}
291
292#[cfg(feature = "flash-attn")]
293fn flash_attn(
294    q: &Tensor,
295    k: &Tensor,
296    v: &Tensor,
297    softmax_scale: f32,
298    causal: bool,
299) -> Result<Tensor> {
300    candle_flash_attn::flash_attn(q, k, v, softmax_scale, causal)
301}
302
303#[cfg(not(feature = "flash-attn"))]
304fn flash_attn(_: &Tensor, _: &Tensor, _: &Tensor, _: f32, _: bool) -> Result<Tensor> {
305    unimplemented!("compile with '--features flash-attn'")
306}
307
308#[derive(Debug, Clone)]
309struct DecoderLayer {
310    self_attn: Attention,
311    mlp: MLP,
312    input_layernorm: RmsNorm,
313    pre_feedforward_layernorm: RmsNorm,
314    post_feedforward_layernorm: RmsNorm,
315    post_attention_layernorm: RmsNorm,
316    sliding_window: Option<usize>,
317}
318
319impl DecoderLayer {
320    fn new(
321        use_flash_attn: bool,
322        cfg: &Config,
323        vb: VarBuilder,
324        sliding_window: Option<usize>,
325    ) -> Result<Self> {
326        let rotary_emb = Arc::new(RotaryEmbedding::new(
327            vb.dtype(),
328            cfg,
329            vb.device(),
330            sliding_window,
331        )?);
332        let self_attn = Attention::new(
333            rotary_emb,
334            use_flash_attn,
335            cfg,
336            sliding_window,
337            vb.pp("self_attn"),
338        )?;
339        let mlp = MLP::new(cfg, vb.pp("mlp"))?;
340        let input_layernorm =
341            RmsNorm::new(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?;
342        let pre_feedforward_layernorm = RmsNorm::new(
343            cfg.hidden_size,
344            cfg.rms_norm_eps,
345            vb.pp("pre_feedforward_layernorm"),
346        )?;
347        let post_feedforward_layernorm = RmsNorm::new(
348            cfg.hidden_size,
349            cfg.rms_norm_eps,
350            vb.pp("post_feedforward_layernorm"),
351        )?;
352        let post_attention_layernorm = RmsNorm::new(
353            cfg.hidden_size,
354            cfg.rms_norm_eps,
355            vb.pp("post_attention_layernorm"),
356        )?;
357        Ok(Self {
358            self_attn,
359            mlp,
360            input_layernorm,
361            pre_feedforward_layernorm,
362            post_feedforward_layernorm,
363            post_attention_layernorm,
364            sliding_window,
365        })
366    }
367
368    fn forward(
369        &mut self,
370        xs: &Tensor,
371        attention_mask: Option<&Tensor>,
372        seqlen_offset: usize,
373    ) -> Result<Tensor> {
374        let residual = xs;
375        let xs = self.input_layernorm.forward(xs)?;
376        let xs = self.self_attn.forward(&xs, attention_mask, seqlen_offset)?;
377        let xs = xs.apply(&self.post_attention_layernorm)?;
378        let xs = (xs + residual)?;
379        let residual = &xs;
380        let xs = xs.apply(&self.pre_feedforward_layernorm)?;
381        let xs = xs.apply(&self.mlp)?;
382        let xs = xs.apply(&self.post_feedforward_layernorm)?;
383        residual + xs
384    }
385
386    fn clear_kv_cache(&mut self) {
387        self.self_attn.clear_kv_cache()
388    }
389}
390
391fn prepare_decoder_attention_mask(
392    b_size: usize,
393    tgt_len: usize,
394    seqlen_offset: usize,
395    sliding_window: Option<usize>,
396    dtype: DType,
397    device: &Device,
398) -> Result<Tensor> {
399    let mask: Vec<_> = if let Some(sliding_window) = sliding_window {
400        (0..tgt_len)
401            .flat_map(|i| {
402                (0..tgt_len).map(move |j| {
403                    if i < j || j + sliding_window < i {
404                        f32::NEG_INFINITY
405                    } else {
406                        0.
407                    }
408                })
409            })
410            .collect()
411    } else {
412        (0..tgt_len)
413            .flat_map(|i| (0..tgt_len).map(move |j| if i < j { f32::NEG_INFINITY } else { 0f32 }))
414            .collect()
415    };
416    let mask = Tensor::from_slice(&mask, (tgt_len, tgt_len), device)?;
417    let mask = if seqlen_offset > 0 {
418        let mask0 = Tensor::zeros((tgt_len, seqlen_offset), DType::F32, device)?;
419        Tensor::cat(&[&mask0, &mask], D::Minus1)?
420    } else {
421        mask
422    };
423    mask.expand((b_size, 1, tgt_len, tgt_len + seqlen_offset))?
424        .to_dtype(dtype)
425}
426
427#[derive(Debug, Clone)]
428pub struct Model {
429    embed_tokens: candle_nn::Embedding,
430    layers: Vec<DecoderLayer>,
431    norm: RmsNorm,
432    lm_head: Linear,
433    final_logit_softcapping: Option<f64>,
434    device: Device,
435    dtype: DType,
436    hidden_size: usize,
437    sliding_window: usize,
438}
439
440impl Model {
441    pub fn new(use_flash_attn: bool, cfg: &Config, vb: VarBuilder) -> Result<Self> {
442        let vb_m = vb.pp("model");
443        let embed_tokens =
444            candle_nn::embedding(cfg.vocab_size, cfg.hidden_size, vb_m.pp("embed_tokens"))?;
445        let mut layers = Vec::with_capacity(cfg.num_hidden_layers);
446        let vb_l = vb_m.pp("layers");
447        for layer_idx in 0..cfg.num_hidden_layers {
448            let sliding_window = (layer_idx + 1) % cfg.sliding_window_pattern > 0;
449            let layer = DecoderLayer::new(
450                use_flash_attn,
451                cfg,
452                vb_l.pp(layer_idx),
453                sliding_window.then_some(cfg.sliding_window),
454            )?;
455            layers.push(layer)
456        }
457        let norm = RmsNorm::new(cfg.hidden_size, cfg.rms_norm_eps, vb_m.pp("norm"))?;
458        let lm_head = Linear::new(embed_tokens.embeddings().clone(), None);
459        Ok(Self {
460            embed_tokens,
461            layers,
462            norm,
463            lm_head,
464            final_logit_softcapping: cfg.final_logit_softcapping,
465            device: vb.device().clone(),
466            dtype: vb.dtype(),
467            hidden_size: cfg.hidden_size,
468            sliding_window: cfg.sliding_window,
469        })
470    }
471
472    fn create_attention_masks(
473        &self,
474        batch_size: usize,
475        seq_len: usize,
476        seqlen_offset: usize,
477    ) -> Result<(Option<Tensor>, Option<Tensor>)> {
478        if seq_len <= 1 {
479            return Ok((None, None));
480        }
481
482        let mask = prepare_decoder_attention_mask(
483            batch_size,
484            seq_len,
485            seqlen_offset,
486            None,
487            self.dtype,
488            &self.device,
489        )?;
490
491        let sliding_mask = prepare_decoder_attention_mask(
492            batch_size,
493            seq_len,
494            seqlen_offset,
495            Some(self.sliding_window),
496            self.dtype,
497            &self.device,
498        )?;
499
500        Ok((Some(mask), Some(sliding_mask)))
501    }
502
503    pub fn forward(&mut self, input_ids: &Tensor, seqlen_offset: usize) -> Result<Tensor> {
504        let (b_size, seq_len) = input_ids.dims2()?;
505        let xs = self.embed_tokens.forward(input_ids)?;
506        let mut xs = (xs * (self.hidden_size as f64).sqrt())?;
507
508        let (attention_mask, sliding_attention_mask) =
509            self.create_attention_masks(b_size, seq_len, seqlen_offset)?;
510
511        for layer in self.layers.iter_mut() {
512            let mask = if layer.sliding_window.is_some() {
513                &sliding_attention_mask
514            } else {
515                &attention_mask
516            };
517            xs = layer.forward(&xs, mask.as_ref(), seqlen_offset)?
518        }
519        let logits = xs
520            .narrow(1, seq_len - 1, 1)?
521            .apply(&self.norm)?
522            .apply(&self.lm_head)?;
523        let logits = match self.final_logit_softcapping {
524            None => logits,
525            Some(sc) => ((logits / sc)?.tanh()? * sc)?,
526        };
527
528        Ok(logits)
529    }
530
531    pub fn clear_kv_cache(&mut self) {
532        for layer in self.layers.iter_mut() {
533            layer.clear_kv_cache()
534        }
535    }
536}