1use super::with_tracing::{layer_norm, linear, linear_no_bias, LayerNorm, Linear};
15use candle::{DType, Device, Result, Tensor, D};
16use candle_nn::{embedding, Embedding, Module, VarBuilder};
17use serde::Deserialize;
18
19#[derive(Debug, Clone, PartialEq, Deserialize)]
21#[serde(default)]
22pub struct Config {
23 pub vocab_size: usize,
24 pub n_embd: usize,
25 pub n_head: usize,
26 pub n_layer: usize,
27 pub n_inner: usize,
28 pub n_positions: usize,
29 pub type_vocab_size: usize,
30 pub layer_norm_epsilon: f64,
31 pub rotary_emb_fraction: f64,
32 pub rotary_emb_base: f64,
33 pub rotary_emb_interleaved: bool,
34 pub qkv_proj_bias: bool,
35 pub mlp_fc1_bias: bool,
36 pub mlp_fc2_bias: bool,
37 pub activation_function: String,
38 pub prenorm: bool,
39 pub model_type: Option<String>,
40}
41
42impl Default for Config {
43 fn default() -> Self {
44 Self {
45 vocab_size: 30528,
46 n_embd: 768,
47 n_head: 12,
48 n_layer: 12,
49 n_inner: 3072,
50 n_positions: 8192,
51 type_vocab_size: 2,
52 layer_norm_epsilon: 1e-12,
53 rotary_emb_fraction: 1.0,
54 rotary_emb_base: 1000.0,
55 rotary_emb_interleaved: false,
56 qkv_proj_bias: false,
57 mlp_fc1_bias: false,
58 mlp_fc2_bias: false,
59 activation_function: "swiglu".to_string(),
60 prenorm: false,
61 model_type: Some("nomic_bert".to_string()),
62 }
63 }
64}
65
66impl Config {
67 fn head_dim(&self) -> usize {
68 self.n_embd / self.n_head
69 }
70
71 fn rotary_emb_dim(&self) -> usize {
72 (self.head_dim() as f64 * self.rotary_emb_fraction) as usize
73 }
74}
75
76#[derive(Clone, Debug)]
79struct RotaryEmbedding {
80 cos: Tensor,
81 sin: Tensor,
82 interleaved: bool,
83}
84
85impl RotaryEmbedding {
86 fn new(
87 dim: usize,
88 max_seq_len: usize,
89 base: f64,
90 interleaved: bool,
91 device: &Device,
92 ) -> Result<Self> {
93 let half_dim = dim / 2;
94 let inv_freq: Vec<f32> = (0..half_dim)
95 .map(|i| 1f32 / (base as f32).powf(2.0 * i as f32 / dim as f32))
96 .collect();
97 let inv_freq = Tensor::new(inv_freq.as_slice(), device)?;
98 let positions = Tensor::arange(0u32, max_seq_len as u32, device)?
99 .to_dtype(DType::F32)?
100 .reshape((max_seq_len, 1))?;
101 let freqs = positions.matmul(&inv_freq.unsqueeze(0)?)?;
102 let cos = freqs.cos()?;
103 let sin = freqs.sin()?;
104 Ok(Self {
105 cos,
106 sin,
107 interleaved,
108 })
109 }
110
111 fn apply(&self, x: &Tensor) -> Result<Tensor> {
115 let cos = self.cos.to_dtype(x.dtype())?;
116 let sin = self.sin.to_dtype(x.dtype())?;
117 if self.interleaved {
118 candle_nn::rotary_emb::rope_i(x, &cos, &sin)
119 } else {
120 candle_nn::rotary_emb::rope(x, &cos, &sin)
121 }
122 }
123}
124
125#[derive(Clone, Debug)]
128struct NomicBertEmbeddings {
129 word_embeddings: Embedding,
130 token_type_embeddings: Option<Embedding>,
131 span: tracing::Span,
132}
133
134impl NomicBertEmbeddings {
135 fn new(vb: VarBuilder, config: &Config) -> Result<Self> {
136 let word_embeddings =
137 embedding(config.vocab_size, config.n_embd, vb.pp("word_embeddings"))?;
138 let token_type_embeddings = if config.type_vocab_size > 0 {
139 Some(embedding(
140 config.type_vocab_size,
141 config.n_embd,
142 vb.pp("token_type_embeddings"),
143 )?)
144 } else {
145 None
146 };
147 Ok(Self {
148 word_embeddings,
149 token_type_embeddings,
150 span: tracing::span!(tracing::Level::TRACE, "embeddings"),
151 })
152 }
153
154 fn forward(&self, input_ids: &Tensor, token_type_ids: Option<&Tensor>) -> Result<Tensor> {
155 let _enter = self.span.enter();
156 let embeddings = self.word_embeddings.forward(input_ids)?;
157 if let Some(tte) = &self.token_type_embeddings {
158 let tt_ids = match token_type_ids {
159 Some(ids) => ids.clone(),
160 None => {
161 let (b, s) = input_ids.dims2()?;
162 Tensor::zeros((b, s), DType::U32, input_ids.device())?
163 }
164 };
165 let tt_emb = tte.forward(&tt_ids)?;
166 embeddings + tt_emb
167 } else {
168 Ok(embeddings)
169 }
170 }
171}
172
173#[derive(Clone, Debug)]
175struct NomicBertAttention {
176 wqkv: Linear,
177 out_proj: Linear,
178 num_heads: usize,
179 head_dim: usize,
180 n_embd: usize,
181 span: tracing::Span,
182}
183
184impl NomicBertAttention {
185 fn new(vb: VarBuilder, config: &Config) -> Result<Self> {
186 let wqkv = if config.qkv_proj_bias {
187 linear(config.n_embd, 3 * config.n_embd, vb.pp("Wqkv"))?
188 } else {
189 linear_no_bias(config.n_embd, 3 * config.n_embd, vb.pp("Wqkv"))?
190 };
191
192 let out_proj = if config.qkv_proj_bias {
193 linear(config.n_embd, config.n_embd, vb.pp("out_proj"))?
194 } else {
195 linear_no_bias(config.n_embd, config.n_embd, vb.pp("out_proj"))?
196 };
197
198 Ok(Self {
199 wqkv,
200 out_proj,
201 num_heads: config.n_head,
202 head_dim: config.head_dim(),
203 n_embd: config.n_embd,
204 span: tracing::span!(tracing::Level::TRACE, "attn"),
205 })
206 }
207
208 fn forward(
209 &self,
210 hidden_states: &Tensor,
211 attention_mask: &Tensor,
212 rotary_emb: &RotaryEmbedding,
213 ) -> Result<Tensor> {
214 let _enter = self.span.enter();
215 let (batch_size, seq_len, _) = hidden_states.dims3()?;
216
217 let qkv = self.wqkv.forward(hidden_states)?;
218 let q = qkv.narrow(D::Minus1, 0, self.n_embd)?;
219 let k = qkv.narrow(D::Minus1, self.n_embd, self.n_embd)?;
220 let v = qkv.narrow(D::Minus1, 2 * self.n_embd, self.n_embd)?;
221
222 let q = q
225 .reshape((batch_size, seq_len, self.num_heads, self.head_dim))?
226 .transpose(1, 2)?
227 .contiguous()?;
228 let k = k
229 .reshape((batch_size, seq_len, self.num_heads, self.head_dim))?
230 .transpose(1, 2)?
231 .contiguous()?;
232 let v = v
233 .reshape((batch_size, seq_len, self.num_heads, self.head_dim))?
234 .transpose(1, 2)?;
235
236 let q = rotary_emb.apply(&q)?;
237 let k = rotary_emb.apply(&k)?;
238
239 let scale = (self.head_dim as f64).sqrt();
240 let attn_scores = (q.matmul(&k.t()?)? / scale)?;
241 let attn_scores = attn_scores.broadcast_add(attention_mask)?;
242 let attn_probs = candle_nn::ops::softmax_last_dim(&attn_scores)?;
243
244 let attn_output = attn_probs.matmul(&v.contiguous()?)?;
245 let attn_output = attn_output.transpose(1, 2)?.contiguous()?;
246 let attn_output = attn_output.flatten_from(D::Minus2)?;
247
248 self.out_proj.forward(&attn_output)
249 }
250}
251
252#[derive(Clone, Debug)]
256struct NomicBertSwiGLU {
257 fc11: Linear,
258 fc12: Linear,
259 fc2: Linear,
260 span: tracing::Span,
261}
262
263impl NomicBertSwiGLU {
264 fn new(vb: VarBuilder, config: &Config) -> Result<Self> {
265 let (fc11, fc12) = if config.mlp_fc1_bias {
266 (
267 linear(config.n_embd, config.n_inner, vb.pp("fc11"))?,
268 linear(config.n_embd, config.n_inner, vb.pp("fc12"))?,
269 )
270 } else {
271 (
272 linear_no_bias(config.n_embd, config.n_inner, vb.pp("fc11"))?,
273 linear_no_bias(config.n_embd, config.n_inner, vb.pp("fc12"))?,
274 )
275 };
276 let fc2 = if config.mlp_fc2_bias {
277 linear(config.n_inner, config.n_embd, vb.pp("fc2"))?
278 } else {
279 linear_no_bias(config.n_inner, config.n_embd, vb.pp("fc2"))?
280 };
281 Ok(Self {
282 fc11,
283 fc12,
284 fc2,
285 span: tracing::span!(tracing::Level::TRACE, "swiglu"),
286 })
287 }
288}
289
290impl Module for NomicBertSwiGLU {
291 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
292 let _enter = self.span.enter();
293 let y = self.fc11.forward(xs)?;
294 let gate = self.fc12.forward(xs)?.silu()?;
295 self.fc2.forward(&(y * gate)?)
296 }
297}
298
299#[derive(Clone, Debug)]
302struct NomicBertBlock {
303 attn: NomicBertAttention,
304 mlp: NomicBertSwiGLU,
305 norm1: LayerNorm,
306 norm2: LayerNorm,
307 prenorm: bool,
308 span: tracing::Span,
309}
310
311impl NomicBertBlock {
312 fn new(vb: VarBuilder, config: &Config) -> Result<Self> {
313 let attn = NomicBertAttention::new(vb.pp("attn"), config)?;
314 let mlp = NomicBertSwiGLU::new(vb.pp("mlp"), config)?;
315 let norm1 = layer_norm(config.n_embd, config.layer_norm_epsilon, vb.pp("norm1"))?;
316 let norm2 = layer_norm(config.n_embd, config.layer_norm_epsilon, vb.pp("norm2"))?;
317 Ok(Self {
318 attn,
319 mlp,
320 norm1,
321 norm2,
322 prenorm: config.prenorm,
323 span: tracing::span!(tracing::Level::TRACE, "block"),
324 })
325 }
326
327 fn forward(
328 &self,
329 hidden_states: &Tensor,
330 attention_mask: &Tensor,
331 rotary_emb: &RotaryEmbedding,
332 ) -> Result<Tensor> {
333 let _enter = self.span.enter();
334 if self.prenorm {
335 let residual = hidden_states;
336 let hidden_states = self.norm1.forward(hidden_states)?;
337 let attn_out = self
338 .attn
339 .forward(&hidden_states, attention_mask, rotary_emb)?;
340 let hidden_states = (residual + attn_out)?;
341
342 let residual = hidden_states.clone();
343 let hidden_states = self.norm2.forward(&hidden_states)?;
344 let mlp_out = self.mlp.forward(&hidden_states)?;
345 residual + mlp_out
346 } else {
347 let attn_out = self
348 .attn
349 .forward(hidden_states, attention_mask, rotary_emb)?;
350 let hidden_states = self.norm1.forward(&(hidden_states + attn_out)?)?;
351 let mlp_out = self.mlp.forward(&hidden_states)?;
352 self.norm2.forward(&(hidden_states + mlp_out)?)
353 }
354 }
355}
356
357#[derive(Clone, Debug)]
358struct NomicBertEncoder {
359 layers: Vec<NomicBertBlock>,
360 rotary_emb: RotaryEmbedding,
361 span: tracing::Span,
362}
363
364impl NomicBertEncoder {
365 fn new(vb: VarBuilder, config: &Config) -> Result<Self> {
366 let layers = (0..config.n_layer)
367 .map(|i| NomicBertBlock::new(vb.pp(format!("layers.{i}")), config))
368 .collect::<Result<Vec<_>>>()?;
369 let rotary_emb = RotaryEmbedding::new(
370 config.rotary_emb_dim(),
371 config.n_positions,
372 config.rotary_emb_base,
373 config.rotary_emb_interleaved,
374 vb.device(),
375 )?;
376 Ok(Self {
377 layers,
378 rotary_emb,
379 span: tracing::span!(tracing::Level::TRACE, "encoder"),
380 })
381 }
382
383 fn forward(&self, hidden_states: &Tensor, attention_mask: &Tensor) -> Result<Tensor> {
384 let _enter = self.span.enter();
385 let mut xs = hidden_states.clone();
386 for layer in &self.layers {
387 xs = layer.forward(&xs, attention_mask, &self.rotary_emb)?;
388 }
389 Ok(xs)
390 }
391}
392
393fn get_extended_attention_mask(attention_mask: &Tensor, dtype: DType) -> Result<Tensor> {
397 let mask = attention_mask.unsqueeze(1)?.unsqueeze(1)?;
398 let on_true = mask.zeros_like()?.to_dtype(dtype)?;
399 let on_false = Tensor::new(-1e4f32, mask.device())?
400 .to_dtype(dtype)?
401 .broadcast_as(mask.shape())?;
402 mask.where_cond(&on_true, &on_false)
403}
404
405pub struct NomicBertModel {
410 embeddings: NomicBertEmbeddings,
411 emb_ln: LayerNorm,
412 encoder: NomicBertEncoder,
413 pub device: Device,
414 span: tracing::Span,
415}
416
417impl NomicBertModel {
418 pub fn load(vb: VarBuilder, config: &Config) -> Result<Self> {
419 let load_inner = |vb: VarBuilder| -> Result<Self> {
420 let embeddings = NomicBertEmbeddings::new(vb.pp("embeddings"), config)?;
421 let emb_ln = layer_norm(config.n_embd, config.layer_norm_epsilon, vb.pp("emb_ln"))?;
422 let encoder = NomicBertEncoder::new(vb.pp("encoder"), config)?;
423 Ok(Self {
424 embeddings,
425 emb_ln,
426 encoder,
427 device: vb.device().clone(),
428 span: tracing::span!(tracing::Level::TRACE, "nomic-bert"),
429 })
430 };
431
432 load_inner(vb.clone()).or_else(|err| {
434 if let Some(model_type) = &config.model_type {
435 load_inner(vb.pp(model_type)).map_err(|_| err)
436 } else {
437 Err(err)
438 }
439 })
440 }
441
442 pub fn forward(
443 &self,
444 input_ids: &Tensor,
445 token_type_ids: Option<&Tensor>,
446 attention_mask: Option<&Tensor>,
447 ) -> Result<Tensor> {
448 let _enter = self.span.enter();
449 let hidden_states = self.embeddings.forward(input_ids, token_type_ids)?;
450 let hidden_states = self.emb_ln.forward(&hidden_states)?;
451
452 let attention_mask = match attention_mask {
453 Some(mask) => mask.clone(),
454 None => input_ids.ones_like()?,
455 };
456 let extended_mask = get_extended_attention_mask(&attention_mask, hidden_states.dtype())?;
457
458 self.encoder.forward(&hidden_states, &extended_mask)
459 }
460}
461
462pub fn mean_pooling(hidden_states: &Tensor, attention_mask: &Tensor) -> Result<Tensor> {
468 let (batch, seq_len, hidden_dim) = hidden_states.dims3()?;
469 let mask = attention_mask.to_dtype(hidden_states.dtype())?;
470 let mask_expanded = mask
471 .unsqueeze(2)?
472 .broadcast_as((batch, seq_len, hidden_dim))?;
473 let sum_hidden = (hidden_states * &mask_expanded)?.sum(1)?;
474 let sum_mask = mask
475 .sum(1)?
476 .unsqueeze(1)?
477 .broadcast_as((batch, hidden_dim))?
478 .clamp(1e-9, f64::MAX)?;
479 sum_hidden / sum_mask
480}
481
482pub fn l2_normalize(x: &Tensor) -> Result<Tensor> {
484 let norm = x.sqr()?.sum_keepdim(D::Minus1)?.sqrt()?;
485 x.broadcast_div(&norm)
486}