Skip to main content

candle_transformers/models/
llama2_c.rs

1//! Llama2 inference implementation.
2//!
3//! See ["LLaMA 2: Open Foundation and Fine-Tuned Chat Models"](https://arxiv.org/abs/2307.09288)
4//!
5//! - ⚡ [Interactive Wasm Example](https://huggingface.co/spaces/lmz/candle-llama2)
6//! - 💻 llama2.c [GH Link](https://github.com/karpathy/llama2.c)
7//!
8
9use candle::{DType, Device, IndexOp, Result, Tensor, D};
10use candle_nn::linear_no_bias as linear;
11use candle_nn::{embedding, rms_norm, Embedding, Linear, Module, RmsNorm, VarBuilder};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone)]
15pub struct Config {
16    pub dim: usize,        // transformer dimension
17    pub hidden_dim: usize, // for ffn layers
18    pub n_layers: usize,   // number of layers
19    pub n_heads: usize,    // number of query heads
20    pub n_kv_heads: usize, // number of key/value heads (can be < query heads because of multiquery)
21    pub vocab_size: usize, // vocabulary size, usually 256 (byte-level)
22    pub seq_len: usize,    // max sequence length
23    pub norm_eps: f64,
24}
25
26impl Config {
27    pub fn tiny_260k() -> Self {
28        Self {
29            dim: 64,
30            hidden_dim: 768,
31            n_layers: 5,
32            n_heads: 8,
33            n_kv_heads: 4,
34            vocab_size: 32000,
35            seq_len: 512,
36            norm_eps: 1e-5,
37        }
38    }
39
40    pub fn tiny_15m() -> Self {
41        Self {
42            dim: 288,
43            hidden_dim: 768,
44            n_layers: 6,
45            n_heads: 6,
46            n_kv_heads: 6,
47            vocab_size: 32000,
48            seq_len: 256,
49            norm_eps: 1e-5,
50        }
51    }
52
53    pub fn tiny_42m() -> Self {
54        Self {
55            dim: 512,
56            hidden_dim: 768,
57            n_layers: 8,
58            n_heads: 8,
59            n_kv_heads: 8,
60            vocab_size: 32000,
61            seq_len: 1024,
62            norm_eps: 1e-5,
63        }
64    }
65
66    pub fn tiny_110m() -> Self {
67        Self {
68            dim: 768,
69            hidden_dim: 768,
70            n_layers: 12,
71            n_heads: 12,
72            n_kv_heads: 12,
73            vocab_size: 32000,
74            seq_len: 1024,
75            norm_eps: 1e-5,
76        }
77    }
78}
79
80#[derive(Debug, Clone)]
81pub struct Cache {
82    masks: HashMap<(usize, usize), Tensor>,
83    pub use_kv_cache: bool,
84    pub kvs: Vec<Option<(Tensor, Tensor)>>,
85    pub cos: Tensor,
86    pub sin: Tensor,
87    device: Device,
88}
89
90impl Cache {
91    pub fn new(use_kv_cache: bool, cfg: &Config, vb: VarBuilder) -> Result<Self> {
92        let n_elem = cfg.dim / cfg.n_heads;
93        let theta: Vec<_> = (0..n_elem)
94            .step_by(2)
95            .map(|i| 1f32 / 10000f32.powf(i as f32 / n_elem as f32))
96            .collect();
97        let theta = Tensor::new(theta.as_slice(), vb.device())?;
98        let idx_theta = Tensor::arange(0, cfg.seq_len as u32, vb.device())?
99            .to_dtype(DType::F32)?
100            .reshape((cfg.seq_len, 1))?
101            .matmul(&theta.reshape((1, theta.elem_count()))?)?;
102        let precomputed_cos = idx_theta.cos()?;
103        let precomputed_sin = idx_theta.sin()?;
104
105        let freq_cis_real = vb
106            .get((cfg.seq_len, cfg.head_size() / 2), "freq_cis_real")
107            .unwrap_or(precomputed_cos);
108        let freq_cis_imag = vb
109            .get((cfg.seq_len, cfg.head_size() / 2), "freq_cis_imag")
110            .unwrap_or(precomputed_sin);
111        let cos = freq_cis_real.reshape((cfg.seq_len, cfg.head_size() / 2, 1))?;
112        let sin = freq_cis_imag.reshape((cfg.seq_len, cfg.head_size() / 2, 1))?;
113        Ok(Self {
114            masks: HashMap::new(),
115            use_kv_cache,
116            kvs: vec![None; cfg.n_layers],
117            cos,
118            sin,
119            device: vb.device().clone(),
120        })
121    }
122
123    pub fn mask(&mut self, seq_len: usize, index_pos: usize) -> Result<Tensor> {
124        let kv_len = index_pos + seq_len;
125        if let Some(mask) = self.masks.get(&(seq_len, kv_len)) {
126            Ok(mask.clone())
127        } else {
128            let mask = crate::utils::build_causal_mask(seq_len, index_pos, &self.device)?;
129            self.masks.insert((seq_len, kv_len), mask.clone());
130            Ok(mask)
131        }
132    }
133}
134
135fn silu(xs: &Tensor) -> Result<Tensor> {
136    xs / (xs.neg()?.exp()? + 1.0)?
137}
138
139#[derive(Debug, Clone)]
140struct CausalSelfAttention {
141    q_proj: Linear,
142    k_proj: Linear,
143    v_proj: Linear,
144    o_proj: Linear,
145    n_head: usize,
146    n_key_value_head: usize,
147    head_dim: usize,
148}
149
150impl CausalSelfAttention {
151    fn apply_rotary_emb(&self, x: &Tensor, index_pos: usize, cache: &Cache) -> Result<Tensor> {
152        let (b_sz, seq_len, h, n_embd) = x.dims4()?;
153        let cos = cache.cos.i(index_pos..index_pos + seq_len)?;
154        let sin = cache.sin.i(index_pos..index_pos + seq_len)?;
155        let cos = cos.unsqueeze(1)?;
156        let sin = sin.unsqueeze(1)?;
157        let cos = cos.broadcast_as((b_sz, seq_len, 1, n_embd / 2, 1))?;
158        let sin = sin.broadcast_as((b_sz, seq_len, 1, n_embd / 2, 1))?;
159        let x = x.reshape((b_sz, seq_len, h, n_embd / 2, 2))?;
160        let x0 = x.narrow(D::Minus1, 0, 1)?;
161        let x1 = x.narrow(D::Minus1, 1, 1)?;
162        let dst0 = (x0.broadcast_mul(&cos)? - x1.broadcast_mul(&sin)?)?;
163        let dst1 = (x0.broadcast_mul(&sin)? + x1.broadcast_mul(&cos)?)?;
164        let rope = Tensor::cat(&[&dst0, &dst1], D::Minus1)?.reshape((b_sz, seq_len, h, n_embd))?;
165        Ok(rope)
166    }
167
168    fn forward(
169        &self,
170        x: &Tensor,
171        index_pos: usize,
172        block_idx: usize,
173        cache: &mut Cache,
174    ) -> Result<Tensor> {
175        let (b_sz, seq_len, n_embd) = x.dims3()?;
176        let q = self.q_proj.forward(x)?;
177        let k = self.k_proj.forward(x)?;
178        let v = self.v_proj.forward(x)?;
179
180        let q = q.reshape((b_sz, seq_len, self.n_head, self.head_dim))?;
181        let k = k.reshape((b_sz, seq_len, self.n_key_value_head, self.head_dim))?;
182        let mut v = v.reshape((b_sz, seq_len, self.n_key_value_head, self.head_dim))?;
183
184        let q = self.apply_rotary_emb(&q, index_pos, cache)?;
185        let mut k = self.apply_rotary_emb(&k, index_pos, cache)?;
186
187        if cache.use_kv_cache {
188            if let Some((cache_k, cache_v)) = &cache.kvs[block_idx] {
189                k = Tensor::cat(&[cache_k, &k], 1)?.contiguous()?;
190                v = Tensor::cat(&[cache_v, &v], 1)?.contiguous()?;
191            }
192            cache.kvs[block_idx] = Some((k.clone(), v.clone()))
193        }
194
195        let k = self.repeat_kv(k)?;
196        let v = self.repeat_kv(v)?;
197
198        let q = q.transpose(1, 2)?.contiguous()?;
199        let k = k.transpose(1, 2)?.contiguous()?;
200        let v = v.transpose(1, 2)?.contiguous()?;
201
202        let att = (q.matmul(&k.t()?)? / (self.head_dim as f64).sqrt())?;
203        let att = if seq_len <= 1 {
204            att
205        } else {
206            let mask = cache.mask(seq_len, index_pos)?.broadcast_as(att.shape())?;
207            masked_fill(&att, &mask, f32::NEG_INFINITY)?
208        };
209        let att = candle_nn::ops::softmax(&att, D::Minus1)?;
210        // Convert to contiguous as matmul doesn't support strided vs for now.
211        let y = att.matmul(&v.contiguous()?)?;
212        let y = y.transpose(1, 2)?.reshape(&[b_sz, seq_len, n_embd])?;
213        let y = self.o_proj.forward(&y)?;
214        Ok(y)
215    }
216
217    fn repeat_kv(&self, x: Tensor) -> Result<Tensor> {
218        let n_rep = self.n_head / self.n_key_value_head;
219        if n_rep == 1 {
220            Ok(x)
221        } else {
222            let (b_sz, seq_len, n_kv_head, head_dim) = x.dims4()?;
223            let x = x
224                .unsqueeze(3)?
225                .expand((b_sz, seq_len, n_kv_head, n_rep, head_dim))?
226                .reshape((b_sz, seq_len, n_kv_head * n_rep, head_dim))?;
227            Ok(x)
228        }
229    }
230
231    fn load(vb: VarBuilder, cfg: &Config) -> Result<Self> {
232        let size_in = cfg.dim;
233        let size_q = (cfg.dim / cfg.n_heads) * cfg.n_heads;
234        let size_kv = (cfg.dim / cfg.n_heads) * cfg.n_kv_heads;
235        let q_proj = linear(size_in, size_q, vb.pp("q_proj"))?;
236        let k_proj = linear(size_in, size_kv, vb.pp("k_proj"))?;
237        let v_proj = linear(size_in, size_kv, vb.pp("v_proj"))?;
238        let o_proj = linear(size_q, size_in, vb.pp("o_proj"))?;
239        Ok(Self {
240            q_proj,
241            k_proj,
242            v_proj,
243            o_proj,
244            n_head: cfg.n_heads,
245            n_key_value_head: cfg.n_kv_heads,
246            head_dim: cfg.dim / cfg.n_heads,
247        })
248    }
249}
250
251fn masked_fill(on_false: &Tensor, mask: &Tensor, on_true: f32) -> Result<Tensor> {
252    let shape = mask.shape();
253    let on_true = Tensor::new(on_true, on_false.device())?.broadcast_as(shape.dims())?;
254    let m = mask.where_cond(&on_true, on_false)?;
255    Ok(m)
256}
257
258#[derive(Debug, Clone)]
259struct Mlp {
260    c_fc1: Linear,
261    c_fc2: Linear,
262    c_proj: Linear,
263}
264
265impl Mlp {
266    fn new(c_fc1: Linear, c_fc2: Linear, c_proj: Linear) -> Self {
267        Self {
268            c_fc1,
269            c_fc2,
270            c_proj,
271        }
272    }
273
274    fn forward(&self, x: &Tensor) -> Result<Tensor> {
275        let x = (silu(&self.c_fc1.forward(x)?)? * self.c_fc2.forward(x)?)?;
276        self.c_proj.forward(&x)
277    }
278
279    fn load(vb: VarBuilder, cfg: &Config) -> Result<Self> {
280        let h_size = cfg.dim;
281        let i_size = cfg.hidden_dim;
282        let c_fc1 = linear(h_size, i_size, vb.pp("gate_proj"))?;
283        let c_fc2 = linear(h_size, i_size, vb.pp("up_proj"))?;
284        let c_proj = linear(i_size, h_size, vb.pp("down_proj"))?;
285        Ok(Self::new(c_fc1, c_fc2, c_proj))
286    }
287}
288
289#[derive(Debug, Clone)]
290struct Block {
291    rms_1: RmsNorm,
292    attn: CausalSelfAttention,
293    rms_2: RmsNorm,
294    mlp: Mlp,
295}
296
297impl Block {
298    fn new(rms_1: RmsNorm, attn: CausalSelfAttention, rms_2: RmsNorm, mlp: Mlp) -> Self {
299        Self {
300            rms_1,
301            attn,
302            rms_2,
303            mlp,
304        }
305    }
306
307    fn forward(
308        &self,
309        x: &Tensor,
310        index_pos: usize,
311        block_idx: usize,
312        cache: &mut Cache,
313    ) -> Result<Tensor> {
314        let residual = x;
315        let x = self.rms_1.forward(x)?;
316        let x = (self.attn.forward(&x, index_pos, block_idx, cache)? + residual)?;
317        let residual = &x;
318        let x = (self.mlp.forward(&self.rms_2.forward(&x)?)? + residual)?;
319        Ok(x)
320    }
321
322    fn load(vb: VarBuilder, cfg: &Config) -> Result<Self> {
323        let attn = CausalSelfAttention::load(vb.pp("self_attn"), cfg)?;
324        let mlp = Mlp::load(vb.pp("mlp"), cfg)?;
325        let input_layernorm = rms_norm(cfg.dim, cfg.norm_eps, vb.pp("input_layernorm"))?;
326        let post_attention_layernorm =
327            rms_norm(cfg.dim, cfg.norm_eps, vb.pp("post_attention_layernorm"))?;
328        Ok(Self::new(
329            input_layernorm,
330            attn,
331            post_attention_layernorm,
332            mlp,
333        ))
334    }
335}
336
337#[derive(Debug, Clone)]
338pub struct Llama {
339    wte: Embedding,
340    blocks: Vec<Block>,
341    ln_f: RmsNorm,
342    lm_head: Linear,
343    pub config: Config,
344}
345
346impl Llama {
347    pub fn forward(&self, x: &Tensor, index_pos: usize, cache: &mut Cache) -> Result<Tensor> {
348        let (_b_sz, _seq_len) = x.dims2()?;
349        let mut x = self.wte.forward(x)?;
350        for (block_idx, block) in self.blocks.iter().enumerate() {
351            x = block.forward(&x, index_pos, block_idx, cache)?;
352        }
353        let x = self.ln_f.forward(&x)?;
354        let logits = self.lm_head.forward(&x)?;
355        logits.to_dtype(DType::F32)
356    }
357
358    pub fn load(vb: VarBuilder, cfg: Config) -> Result<Self> {
359        let wte = embedding(cfg.vocab_size, cfg.dim, vb.pp("model.embed_tokens"))?;
360        let lm_head = linear(cfg.dim, cfg.vocab_size, vb.pp("lm_head"))?;
361        let ln_f = rms_norm(cfg.dim, cfg.norm_eps, vb.pp("model.norm"))?;
362        let blocks: Vec<_> = (0..cfg.n_layers)
363            .map(|i| Block::load(vb.pp(format!("model.layers.{i}")), &cfg).unwrap())
364            .collect();
365        Ok(Self {
366            wte,
367            blocks,
368            ln_f,
369            lm_head,
370            config: cfg,
371        })
372    }
373}