use candle_core::DType;
use super::pooling;
use crate::error::Result;
use crate::models::Encoder;
use crate::models::EncoderInput;
use crate::models::SparseModel;
use crate::models::bert::BertModel;
use crate::models::sparse::stopwords::Stopwords;
use crate::sparse::SparseEmbedding;
pub struct Bm42Sparse {
bert: BertModel,
vocab_size: usize,
token_text: Vec<String>,
stopwords: Stopwords,
}
impl Bm42Sparse {
pub fn new(bert: BertModel, vocab_size: usize, token_text: Vec<String>) -> Self {
Self {
bert,
vocab_size,
token_text,
stopwords: Stopwords::parse(include_str!("../english_stopwords.txt")),
}
}
fn pooled_attention(&self, input: &EncoderInput) -> Result<Vec<Vec<f32>>> {
let (_hidden, weights) = self.bert.forward_with_attention(input)?;
let cls_row = weights.narrow(2, 0, 1)?.squeeze(2)?;
let pooled = cls_row.mean(1)?.to_dtype(DType::F32)?;
Ok(pooled.to_vec2::<f32>()?)
}
}
impl SparseModel for Bm42Sparse {
fn forward(&self, input: &EncoderInput) -> Result<Vec<SparseEmbedding>> {
let pooled_rows = self.pooled_attention(input)?;
let id_rows = input.input_ids.to_vec2::<u32>()?;
Ok(
pooled_rows
.iter()
.zip(id_rows.iter())
.zip(input.lengths.iter())
.map(|((pooled, ids), &len)| {
let len = len.min(ids.len()).min(pooled.len());
pooling::pool_row(
&pooled[..len],
&ids[..len],
&self.token_text,
&self.stopwords,
)
})
.collect(),
)
}
fn forward_query(&self, input: &EncoderInput) -> Result<Vec<SparseEmbedding>> {
let id_rows = input.input_ids.to_vec2::<u32>()?;
Ok(
id_rows
.iter()
.zip(input.lengths.iter())
.map(|(ids, &len)| {
let len = len.min(ids.len());
pooling::pool_query_row(&ids[..len], &self.token_text, &self.stopwords)
})
.collect(),
)
}
fn vocab_size(&self) -> usize {
self.vocab_size
}
fn max_position_embeddings(&self) -> usize {
self.bert.max_position_embeddings()
}
fn weights_dtype(&self) -> DType {
self.bert.weights_dtype()
}
}
#[cfg(test)]
mod tests;