use candle_core::DType;
use candle_core::Tensor;
use candle_nn::VarBuilder;
use super::config::ModernBertConfig;
use super::embeddings::ModernBertEmbeddings;
use super::layer::ModernBertLayer;
use crate::error::Result;
use crate::layers::layer_norm::LayerNorm;
use crate::layers::mask::AttentionMask;
use crate::layers::rope::RotaryEmbedding;
use crate::models::Encoder;
use crate::models::EncoderInput;
pub struct ModernBertModel {
embeddings: ModernBertEmbeddings,
layers: Vec<ModernBertLayer>,
global_rope: RotaryEmbedding,
local_rope: RotaryEmbedding,
final_norm: LayerNorm,
config: ModernBertConfig,
}
impl std::fmt::Debug for ModernBertModel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ModernBertModel").finish_non_exhaustive()
}
}
impl ModernBertModel {
pub fn load(config: ModernBertConfig, vb: VarBuilder) -> Result<Self> {
config.ensure_supported()?;
let embeddings = ModernBertEmbeddings::load(&config, vb.clone())?;
let layers = (0..config.num_hidden_layers)
.map(|i| ModernBertLayer::load(&config, vb.pp(format!("layers.{i}")), i))
.collect::<Result<Vec<_>>>()?;
let global_rope = RotaryEmbedding::new(
config.head_dim(),
config.max_position_embeddings,
config.global_rope_theta,
vb.dtype(),
vb.device(),
)?;
let local_rope = RotaryEmbedding::new(
config.head_dim(),
config.max_position_embeddings,
config.local_rope_theta,
vb.dtype(),
vb.device(),
)?;
let final_norm =
LayerNorm::load_without_bias(vb.pp("final_norm"), config.hidden_size, config.norm_eps)?;
Ok(Self {
embeddings,
layers,
global_rope,
local_rope,
final_norm,
config,
})
}
pub(super) fn rope_for(&self, layer: usize) -> &RotaryEmbedding {
if self.config.is_global(layer) {
&self.global_rope
} else {
&self.local_rope
}
}
}
impl Encoder for ModernBertModel {
fn forward(&self, input: &EncoderInput) -> Result<Tensor> {
let mut hidden = self.embeddings.forward(input.input_ids)?;
let dtype = hidden.dtype();
let heads = self.config.num_attention_heads;
let head_dim = self.config.head_dim();
let global =
AttentionMask::padding(input.attention_mask, input.lengths, heads, head_dim, dtype)?;
let sliding = AttentionMask::sliding(
input.attention_mask,
input.lengths,
self.config.attention_window(),
heads,
head_dim,
dtype,
)?;
for (i, layer) in self.layers.iter().enumerate() {
let mask = if self.config.is_global(i) {
&global
} else {
&sliding
};
hidden = layer.forward(&hidden, mask, self.rope_for(i))?;
}
Ok(self.final_norm.forward(&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;