use candle_core::DType;
use candle_core::Tensor;
use candle_nn::VarBuilder;
use super::config::BertConfig;
use super::embeddings::BertEmbeddings;
use super::layer::BertLayer;
use crate::error::Result;
use crate::layers::mask::AttentionMask;
use crate::models::Encoder;
use crate::models::EncoderInput;
pub struct BertModel {
embeddings: BertEmbeddings,
layers: Vec<BertLayer>,
config: BertConfig,
}
impl BertModel {
pub fn load(config: BertConfig, vb: VarBuilder) -> Result<Self> {
let vb = if vb.contains_tensor("embeddings.word_embeddings.weight") {
vb
} else {
vb.pp("bert")
};
let embeddings = BertEmbeddings::load(&config, vb.pp("embeddings"))?;
let layers = (0..config.num_hidden_layers)
.map(|i| BertLayer::load(&config, vb.pp(format!("encoder.layer.{i}"))))
.collect::<Result<Vec<_>>>()?;
Ok(Self {
embeddings,
layers,
config,
})
}
pub(crate) fn embed_tokens(&self) -> &Tensor {
self.embeddings.word_embeddings()
}
fn embed_and_mask(&self, input: &EncoderInput) -> Result<(Tensor, AttentionMask)> {
let hidden = self
.embeddings
.forward(input.input_ids, input.token_type_ids)?;
let mask = AttentionMask::padding(
input.attention_mask,
input.lengths,
self.config.num_attention_heads,
self.config.head_dim(),
hidden.dtype(),
)?;
Ok((hidden, mask))
}
pub fn forward_with_attention(&self, input: &EncoderInput) -> Result<(Tensor, Tensor)> {
let (mut hidden, mask) = self.embed_and_mask(input)?;
let (last, rest) = self
.layers
.split_last()
.expect("a loaded BertModel always has at least one layer");
for layer in rest {
hidden = layer.forward(&hidden, &mask)?;
}
last.forward_with_weights(&hidden, &mask)
}
}
impl Encoder for BertModel {
fn forward(&self, input: &EncoderInput) -> Result<Tensor> {
let (mut hidden, mask) = self.embed_and_mask(input)?;
for layer in &self.layers {
hidden = layer.forward(&hidden, &mask)?;
}
Ok(hidden)
}
fn hidden_size(&self) -> usize {
self.config.hidden_size
}
fn max_position_embeddings(&self) -> usize {
self.config.max_position_embeddings
}
fn weights_dtype(&self) -> DType {
self.embeddings.weights_dtype()
}
}
#[cfg(test)]
mod tests;