candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use candle_core::Module;
use candle_core::Tensor;
use candle_nn::Linear;
use candle_nn::VarBuilder;
use candle_nn::linear;

use super::config::MPNetConfig;
use crate::error::Result;
use crate::layers::attention::scaled_dot_product;
use crate::layers::layer_norm::LayerNorm;
use crate::layers::mask::AttentionMask;

/// MPNet's own block because four of BERT's five attention tensor paths differ (`attention.attn.{q,k,v,o}`).
pub struct MPNetAttention {
  q: Linear,
  k: Linear,
  v: Linear,
  o: Linear,
  layer_norm: LayerNorm,
  num_heads: usize,
  head_dim: usize,
}

impl MPNetAttention {
  /// `vb` is `encoder.layer.{i}.attention`.
  pub fn load(config: &MPNetConfig, vb: VarBuilder) -> Result<Self> {
    let hidden = config.hidden_size;
    Ok(Self {
      q: linear(hidden, hidden, vb.pp("attn.q"))?,
      k: linear(hidden, hidden, vb.pp("attn.k"))?,
      v: linear(hidden, hidden, vb.pp("attn.v"))?,
      o: linear(hidden, hidden, vb.pp("attn.o"))?,
      layer_norm: LayerNorm::load(vb.pp("LayerNorm"), hidden, config.layer_norm_eps)?,
      num_heads: config.num_attention_heads,
      head_dim: config.head_dim(),
    })
  }

  /// `mask` already carries the relative position bias, added after the scale as the reference does.
  pub fn forward(&self, hidden: &Tensor, mask: &AttentionMask) -> Result<Tensor> {
    let (batch, seq, _) = hidden.dims3()?;
    let split = |xs: Tensor| -> Result<Tensor> {
      Ok(
        xs.reshape((batch, seq, self.num_heads, self.head_dim))?
          .transpose(1, 2)?
          .contiguous()?,
      )
    };
    let q = split(self.q.forward(hidden)?)?;
    let k = split(self.k.forward(hidden)?)?;
    let v = split(self.v.forward(hidden)?)?;
    let scale = 1.0 / (self.head_dim as f64).sqrt();
    let context = scaled_dot_product(&q, &k, &v, mask, scale)?
      .transpose(1, 2)?
      .reshape((batch, seq, self.num_heads * self.head_dim))?;
    let projected = self.o.forward(&context)?;
    Ok(self.layer_norm.forward(&(projected + hidden)?)?)
  }
}