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::Tensor;
use candle_nn::VarBuilder;

use super::attention::JinaBertAttention;
use super::config::JinaBertConfig;
use super::mlp::JinaBertMlp;
use super::variant::Variant;
use crate::error::Result;
use crate::layers::layer_norm::LayerNorm;
use crate::layers::mask::AttentionMask;

/// For the base and QK shapes the gated feed-forward carries the residual and norm, so this adds nothing.
pub struct JinaBertLayer {
  attention: JinaBertAttention,
  mlp: JinaBertMlp,
  /// `base-code` only: two post-norm stages (upstream's comment calls it "Pre-Norm", it is not).
  norms: Option<(LayerNorm, LayerNorm)>,
}

impl JinaBertLayer {
  /// `vb` is `encoder.layer.{i}`.
  pub fn load(config: &JinaBertConfig, variant: Variant, vb: VarBuilder) -> Result<Self> {
    let norms = match variant.split_norms {
      true => Some((
        LayerNorm::load(
          vb.pp("layer_norm_1"),
          config.hidden_size,
          config.layer_norm_eps,
        )?,
        LayerNorm::load(
          vb.pp("layer_norm_2"),
          config.hidden_size,
          config.layer_norm_eps,
        )?,
      )),
      false => None,
    };
    Ok(Self {
      attention: JinaBertAttention::load(config, variant, vb.pp("attention"))?,
      mlp: JinaBertMlp::load(config, variant.split_norms, vb)?,
      norms,
    })
  }

  /// Split-norm: `residual = layer_norm_1(x + attention(x))`, then `layer_norm_2(residual + mlp(residual))`;
  /// the feed-forward takes the normalized `residual`, not `attended`.
  pub fn forward(&self, hidden: &Tensor, mask: &AttentionMask) -> Result<Tensor> {
    let attended = self.attention.forward(hidden, mask)?;
    let Some((first, second)) = &self.norms else {
      return self.mlp.forward(&attended);
    };
    let residual = first.forward(&(hidden + &attended)?)?;
    let projected = self.mlp.forward(&residual)?;
    Ok(second.forward(&(residual + projected)?)?)
  }
}