use candle_core::DType;
use candle_core::Tensor;
use candle_nn::Linear;
use candle_nn::Module;
use candle_nn::VarBuilder;
use super::RerankModel;
use crate::error::CanddingError;
use crate::error::Result;
use crate::models::Encoder;
use crate::models::EncoderInput;
use crate::models::bert;
use crate::models::jina_bert;
use crate::pooling;
use crate::pooling::Pooling;
use crate::rerank::RerankScore;
use crate::rerank::ScoreActivation;
pub struct BertPoolerRerank {
encoder: Box<dyn Encoder>,
pooler: Linear,
classifier: Linear,
score_activation: ScoreActivation,
}
impl BertPoolerRerank {
pub fn load(
model_type: &str,
config_json: &str,
vb: VarBuilder,
score_activation: ScoreActivation,
jina_alibi_variant: Option<jina_bert::AlibiSlopeVariant>,
) -> Result<Self> {
let (encoder, bert_vb): (Box<dyn Encoder>, VarBuilder) = match model_type {
"bert" => {
let config: bert::BertConfig = serde_json::from_str(config_json)?;
let encoder = bert::BertModel::load(config, vb.clone())?;
(Box::new(encoder), vb.pp("bert"))
}
"jina_bert" => {
let config: jina_bert::JinaBertConfig = serde_json::from_str(config_json)?;
let bert_vb = vb.pp("bert");
let variant = jina_alibi_variant.unwrap_or(jina_bert::AlibiSlopeVariant::Canonical);
let encoder = jina_bert::JinaBertModel::load(config, bert_vb.clone(), variant)?;
(Box::new(encoder), bert_vb)
}
other => return Err(CanddingError::UnsupportedArchitecture(other.to_string())),
};
let hidden = encoder.hidden_size();
let pooler_vb = bert_vb.pp("pooler").pp("dense");
let pooler = Linear::new(
pooler_vb.get((hidden, hidden), "weight")?,
Some(pooler_vb.get(hidden, "bias")?),
);
let classifier_vb = vb.pp("classifier");
let classifier = Linear::new(
classifier_vb.get((1, hidden), "weight")?,
Some(classifier_vb.get(1, "bias")?),
);
Ok(Self {
encoder,
pooler,
classifier,
score_activation,
})
}
}
impl RerankModel for BertPoolerRerank {
fn forward(&self, input: &EncoderInput) -> Result<Vec<RerankScore>> {
let hidden = self.encoder.forward(input)?;
let cls =
pooling::pool(Pooling::Cls, &hidden, input.attention_mask, input.lengths)?.contiguous()?;
let pooled = self.pooler.forward(&cls)?.tanh()?;
let logits: Vec<f32> = raw_logits(&self.classifier.forward(&pooled)?)?;
Ok(
logits
.into_iter()
.map(|raw| RerankScore::new(self.score_activation.apply(raw)))
.collect(),
)
}
fn max_position_embeddings(&self) -> usize {
self.encoder.max_position_embeddings()
}
fn weights_dtype(&self) -> DType {
self.encoder.weights_dtype()
}
}
fn raw_logits(logits: &Tensor) -> Result<Vec<f32>> {
Ok(logits.squeeze(1)?.to_dtype(DType::F32)?.to_vec1::<f32>()?)
}
#[cfg(test)]
mod tests;