1use candle_core::{DType, Device, IndexOp, Module, Result, Tensor, D};
12use candle_nn::{embedding, ops::softmax, Embedding, Linear, VarBuilder};
13
14pub const D_MODEL: usize = 512;
15pub const N_HEADS: usize = 8;
16pub const N_KV: usize = 4;
17pub const HEAD_DIM: usize = 64;
18pub const VOCAB: usize = 8192;
19pub const N_ENC: usize = 12;
20pub const N_DEC: usize = 8;
21const ROPE_THETA: f64 = 10000.0;
22const EPS: f64 = 1e-6;
23
24struct ZCRMSNorm {
26 scale: Tensor, }
28impl ZCRMSNorm {
29 fn load(dim: usize, vb: VarBuilder) -> Result<Self> {
30 Ok(Self { scale: vb.get(dim, "weight")? })
31 }
32 fn forward(&self, x: &Tensor) -> Result<Tensor> {
33 let dt = x.dtype();
34 let x = x.to_dtype(DType::F32)?;
35 let rms = (x.sqr()?.mean_keepdim(D::Minus1)? + EPS)?.sqrt()?;
36 let scale = (self.scale.to_dtype(DType::F32)? + 1.0)?;
37 x.broadcast_div(&rms)?.broadcast_mul(&scale)?.to_dtype(dt)
38 }
39}
40
41fn no_bias_linear(inp: usize, out: usize, vb: VarBuilder) -> Result<Linear> {
42 Ok(Linear::new(vb.get((out, inp), "weight")?, None))
43}
44
45fn rope_tables(seq_len: usize, dev: &Device) -> Result<(Tensor, Tensor)> {
47 let half = HEAD_DIM / 2;
48 let inv: Vec<f32> = (0..half).map(|i| 1f32 / (ROPE_THETA as f32).powf((2 * i) as f32 / HEAD_DIM as f32)).collect();
49 let inv = Tensor::from_vec(inv, (1, half), dev)?;
50 let t: Vec<f32> = (0..seq_len).map(|i| i as f32).collect();
51 let t = Tensor::from_vec(t, (seq_len, 1), dev)?;
52 let ang = t.broadcast_mul(&inv)?; Ok((ang.cos()?, ang.sin()?))
54}
55
56fn apply_rope(x: &Tensor, cos: &Tensor, sin: &Tensor) -> Result<Tensor> {
58 let (_b, _h, t, d) = x.dims4()?;
59 let half = d / 2;
60 let cos = cos.i((..t, ..))?.reshape((1, 1, t, half))?;
61 let sin = sin.i((..t, ..))?.reshape((1, 1, t, half))?;
62 let x1 = x.narrow(D::Minus1, 0, half)?;
63 let x2 = x.narrow(D::Minus1, half, half)?;
64 let r1 = (x1.broadcast_mul(&cos)? - x2.broadcast_mul(&sin)?)?;
65 let r2 = (x2.broadcast_mul(&cos)? + x1.broadcast_mul(&sin)?)?;
66 Tensor::cat(&[r1, r2], D::Minus1)
67}
68
69struct Attention {
70 q_proj: Linear,
71 k_proj: Linear,
72 v_proj: Linear,
73 out_proj: Linear,
74 q_norm: ZCRMSNorm,
75 k_norm: ZCRMSNorm,
76}
77impl Attention {
78 fn load(vb: VarBuilder) -> Result<Self> {
79 Ok(Self {
80 q_proj: no_bias_linear(D_MODEL, N_HEADS * HEAD_DIM, vb.pp("q_proj"))?,
81 k_proj: no_bias_linear(D_MODEL, N_KV * HEAD_DIM, vb.pp("k_proj"))?,
82 v_proj: no_bias_linear(D_MODEL, N_KV * HEAD_DIM, vb.pp("v_proj"))?,
83 out_proj: no_bias_linear(D_MODEL, D_MODEL, vb.pp("out_proj"))?,
84 q_norm: ZCRMSNorm::load(HEAD_DIM, vb.pp("q_norm"))?,
85 k_norm: ZCRMSNorm::load(HEAD_DIM, vb.pp("k_norm"))?,
86 })
87 }
88
89 fn forward(&self, q_in: &Tensor, kv_in: &Tensor, causal: bool, rope: Option<&(Tensor, Tensor)>) -> Result<Tensor> {
91 let (b, tq, _) = q_in.dims3()?;
92 let tk = kv_in.dim(1)?;
93 let q = self.q_proj.forward(q_in)?.reshape((b, tq, N_HEADS, HEAD_DIM))?.transpose(1, 2)?;
94 let k = self.k_proj.forward(kv_in)?.reshape((b, tk, N_KV, HEAD_DIM))?.transpose(1, 2)?;
95 let v = self.v_proj.forward(kv_in)?.reshape((b, tk, N_KV, HEAD_DIM))?.transpose(1, 2)?;
96 let q = self.q_norm.forward(&q)?;
97 let k = self.k_norm.forward(&k)?;
98 let (q, k) = match rope {
99 Some((cos, sin)) => (apply_rope(&q, cos, sin)?, apply_rope(&k, cos, sin)?),
100 None => (q, k),
101 };
102 let rep = N_HEADS / N_KV;
104 let k = repeat_kv(&k, rep)?;
105 let v = repeat_kv(&v, rep)?;
106 let scale = 1.0 / (HEAD_DIM as f64).sqrt();
107 let mut att = (q.contiguous()?.matmul(&k.transpose(2, 3)?.contiguous()?)? * scale)?; if causal {
109 att = att.broadcast_add(&causal_mask(tq, tk, q_in.device())?)?;
110 }
111 let att = softmax(&att, D::Minus1)?;
112 let out = att.matmul(&v.contiguous()?)?; let out = out.transpose(1, 2)?.reshape((b, tq, N_HEADS * HEAD_DIM))?;
114 self.out_proj.forward(&out)
115 }
116}
117
118fn repeat_kv(x: &Tensor, rep: usize) -> Result<Tensor> {
119 if rep == 1 {
120 return Ok(x.clone());
121 }
122 let (b, kv, t, d) = x.dims4()?;
123 x.unsqueeze(2)?.expand((b, kv, rep, t, d))?.reshape((b, kv * rep, t, d))
124}
125
126fn causal_mask(tq: usize, tk: usize, dev: &Device) -> Result<Tensor> {
128 let mut v = vec![0f32; tq * tk];
129 for i in 0..tq {
130 for j in 0..tk {
131 if j > i {
132 v[i * tk + j] = f32::NEG_INFINITY;
133 }
134 }
135 }
136 Tensor::from_vec(v, (1, 1, tq, tk), dev)
137}
138
139fn gate(vb: &VarBuilder, name: &str) -> Result<Tensor> {
140 let g = vb.get(1, name)?;
142 candle_nn::ops::sigmoid(&g.to_dtype(DType::F32)?)
143}
144
145struct EncoderLayer {
146 ln: ZCRMSNorm,
147 attn: Attention,
148 gate: Tensor,
149}
150impl EncoderLayer {
151 fn load(vb: VarBuilder) -> Result<Self> {
152 Ok(Self {
153 ln: ZCRMSNorm::load(D_MODEL, vb.pp("input_layernorm"))?,
154 attn: Attention::load(vb.pp("self_attn"))?,
155 gate: gate(&vb, "attn_gate")?,
156 })
157 }
158 fn forward(&self, x: &Tensor, rope: &(Tensor, Tensor)) -> Result<Tensor> {
159 let h = self.ln.forward(x)?;
160 let a = self.attn.forward(&h, &h, false, Some(rope))?;
161 x + a.broadcast_mul(&self.gate)?
162 }
163}
164
165struct DecoderLayer {
166 ln: ZCRMSNorm,
167 self_attn: Attention,
168 self_gate: Tensor,
169 cross_ln: ZCRMSNorm,
170 cross_attn: Attention,
171 cross_gate: Tensor,
172}
173impl DecoderLayer {
174 fn load(vb: VarBuilder) -> Result<Self> {
175 Ok(Self {
176 ln: ZCRMSNorm::load(D_MODEL, vb.pp("input_layernorm"))?,
177 self_attn: Attention::load(vb.pp("self_attn"))?,
178 self_gate: gate(&vb, "self_attn_gate")?,
179 cross_ln: ZCRMSNorm::load(D_MODEL, vb.pp("encoder_attn_layer_norm"))?,
180 cross_attn: Attention::load(vb.pp("encoder_attn"))?,
181 cross_gate: gate(&vb, "cross_attn_gate")?,
182 })
183 }
184 fn forward(&self, x: &Tensor, enc: &Tensor, rope: &(Tensor, Tensor)) -> Result<Tensor> {
185 let h = self.ln.forward(x)?;
186 let sa = self.self_attn.forward(&h, &h, true, Some(rope))?;
187 let x = (x + sa.broadcast_mul(&self.self_gate)?)?;
188 let hd = self.cross_ln.forward(&x)?;
189 let ca = self.cross_attn.forward(&hd, enc, false, None)?;
190 x + ca.broadcast_mul(&self.cross_gate)?
191 }
192}
193
194pub struct NeedleModel {
195 embed: Embedding,
196 enc: Vec<EncoderLayer>,
197 enc_final: ZCRMSNorm,
198 dec: Vec<DecoderLayer>,
199 dec_norm: ZCRMSNorm,
200 lm_head: Linear,
201 device: Device,
202}
203
204impl NeedleModel {
205 pub fn load(safetensors: &std::path::Path, device: &Device) -> Result<Self> {
207 let vb = unsafe { VarBuilder::from_mmaped_safetensors(&[safetensors], DType::F32, device)? };
208 let m = vb.pp("model");
209 let embed = embedding(VOCAB, D_MODEL, m.pp("embed_tokens"))?;
210 let enc = (0..N_ENC).map(|i| EncoderLayer::load(m.pp("encoder").pp("layers").pp(i))).collect::<Result<_>>()?;
211 let enc_final = ZCRMSNorm::load(D_MODEL, m.pp("encoder").pp("final_norm"))?;
212 let dec = (0..N_DEC).map(|i| DecoderLayer::load(m.pp("decoder").pp("layers").pp(i))).collect::<Result<_>>()?;
213 let dec_norm = ZCRMSNorm::load(D_MODEL, m.pp("decoder").pp("norm"))?;
214 let lm_head = no_bias_linear(D_MODEL, VOCAB, vb.pp("lm_head"))?;
215 Ok(Self { embed, enc, enc_final, dec, dec_norm, lm_head, device: device.clone() })
216 }
217
218 pub fn encode(&self, enc_ids: &Tensor) -> Result<Tensor> {
220 let t = enc_ids.dim(1)?;
221 let rope = rope_tables(t, &self.device)?;
222 let mut x = self.embed.forward(enc_ids)?;
223 for l in &self.enc {
224 x = l.forward(&x, &rope)?;
225 }
226 self.enc_final.forward(&x)
227 }
228
229 pub fn decode(&self, dec_ids: &Tensor, enc_out: &Tensor) -> Result<Tensor> {
231 let t = dec_ids.dim(1)?;
232 let rope = rope_tables(t, &self.device)?;
233 let mut x = self.embed.forward(dec_ids)?;
234 for l in &self.dec {
235 x = l.forward(&x, enc_out, &rope)?;
236 }
237 let x = self.dec_norm.forward(&x)?;
238 self.lm_head.forward(&x)
239 }
240
241 pub fn forward(&self, enc_ids: &Tensor, dec_ids: &Tensor) -> Result<Tensor> {
243 let enc = self.encode(enc_ids)?;
244 self.decode(dec_ids, &enc)
245 }
246
247 pub fn device(&self) -> &Device {
248 &self.device
249 }
250}