use candle_core::Module;
use candle_core::Tensor;
use candle_nn::Linear;
use candle_nn::VarBuilder;
use candle_nn::linear;
use super::config::XlmRobertaFlashConfig;
use crate::error::Result;
use crate::layers::attention::scaled_dot_product;
use crate::layers::attention::split_fused_qkv;
use crate::layers::mask::AttentionMask;
use crate::layers::rope::RotaryEmbedding;
pub struct XlmRobertaFlashAttention {
q: Linear,
k: Linear,
v: Linear,
out: Linear,
heads: usize,
head_dim: usize,
scale: f64,
}
impl XlmRobertaFlashAttention {
pub fn load(config: &XlmRobertaFlashConfig, vb: VarBuilder) -> Result<Self> {
let hidden = config.hidden_size;
let wqkv = vb.pp("Wqkv");
let bound = 1.0 / (hidden as f64).sqrt();
let bias_init = candle_nn::Init::Uniform {
lo: -bound,
up: bound,
};
let fused_weight = wqkv.get_with_hints(
(3 * hidden, hidden),
"weight",
candle_nn::init::DEFAULT_KAIMING_NORMAL,
)?;
let fused_bias = wqkv.get_with_hints(3 * hidden, "bias", bias_init)?;
let (qw, kw, vw) = split_fused_qkv(&fused_weight, hidden)?;
let (qb, kb, vbias) = split_fused_qkv(&fused_bias, hidden)?;
Ok(Self {
q: Linear::new(qw, Some(qb)),
k: Linear::new(kw, Some(kb)),
v: Linear::new(vw, Some(vbias)),
out: linear(hidden, hidden, vb.pp("out_proj"))?,
heads: config.num_attention_heads,
head_dim: config.head_dim(),
scale: 1.0 / (config.head_dim() as f64).sqrt(),
})
}
pub fn forward(
&self,
hidden: &Tensor,
mask: &AttentionMask,
rope: Option<&RotaryEmbedding>,
) -> Result<Tensor> {
let (batch, seq, _) = hidden.dims3()?;
let split = |xs: Tensor| -> Result<Tensor> {
Ok(
xs.reshape((batch, seq, self.heads, self.head_dim))?
.transpose(1, 2)?
.contiguous()?,
)
};
let q = split(self.q.forward(hidden)?)?;
let k = split(self.k.forward(hidden)?)?;
let v = split(self.v.forward(hidden)?)?;
let (q, k) = match rope {
Some(rope) => rope.apply(&q, &k)?,
None => (q, k),
};
let context = scaled_dot_product(&q, &k, &v, mask, self.scale)?
.transpose(1, 2)?
.reshape((batch, seq, self.heads * self.head_dim))?;
Ok(self.out.forward(&context)?)
}
}