use candle_core::Tensor;
use candle_nn::VarBuilder;
use crate::error::Result;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RmsNormStyle {
Scale,
OnePlusScale,
}
pub struct RmsNorm {
weight: Tensor,
eps: f32,
}
impl RmsNorm {
pub fn load(vb: VarBuilder, size: usize, eps: f64, style: RmsNormStyle) -> Result<Self> {
let weight = vb.get_with_hints(size, "weight", candle_nn::Init::Const(1.0))?;
let weight = match style {
RmsNormStyle::Scale => weight,
RmsNormStyle::OnePlusScale => (weight + 1.0)?,
};
Ok(Self {
weight,
eps: eps as f32,
})
}
pub fn forward(&self, xs: &Tensor) -> Result<Tensor> {
let weight = self.weight.to_dtype(xs.dtype())?;
Ok(candle_nn::ops::rms_norm(
&xs.contiguous()?,
&weight,
self.eps,
)?)
}
}
#[cfg(test)]
mod tests {
use candle_core::DType;
use candle_core::Device;
use candle_core::Tensor;
use candle_nn::VarBuilder;
use candle_nn::VarMap;
use super::*;
fn norm(style: RmsNormStyle, weight: &[f32]) -> RmsNorm {
let mut varmap = VarMap::new();
let vb = VarBuilder::from_varmap(&varmap, DType::F32, &Device::Cpu);
RmsNorm::load(vb.pp("n"), weight.len(), 1e-6, RmsNormStyle::Scale).unwrap();
varmap
.set_one("n.weight", Tensor::new(weight, &Device::Cpu).unwrap())
.unwrap();
RmsNorm::load(vb.pp("n"), weight.len(), 1e-6, style).unwrap()
}
fn rows(t: Tensor) -> Vec<Vec<f32>> {
t.to_vec2::<f32>().unwrap()
}
#[test]
fn scale_style_multiplies_by_the_weight() {
let xs = Tensor::new(&[[3f32, 4.0]], &Device::Cpu).unwrap();
let out = rows(norm(RmsNormStyle::Scale, &[1.0, 2.0]).forward(&xs).unwrap());
let rms = (12.5f32 + 1e-6).sqrt();
assert!(
(out[0][0] - 3.0 / rms).abs() < 1e-5 && (out[0][1] - 8.0 / rms).abs() < 1e-5,
"{out:?}"
);
}
#[test]
fn one_plus_style_adds_one_to_the_weight() {
let xs = Tensor::new(&[[3f32, 4.0]], &Device::Cpu).unwrap();
let out = rows(
norm(RmsNormStyle::OnePlusScale, &[0.0, 1.0])
.forward(&xs)
.unwrap(),
);
let rms = (12.5f32 + 1e-6).sqrt();
assert!(
(out[0][0] - 3.0 / rms).abs() < 1e-5 && (out[0][1] - 8.0 / rms).abs() < 1e-5,
"{out:?}"
);
}
#[test]
fn half_precision_input_keeps_its_dtype() {
let xs = Tensor::new(&[[3f32, 4.0]], &Device::Cpu)
.unwrap()
.to_dtype(DType::F16)
.unwrap();
let out = norm(RmsNormStyle::Scale, &[1.0, 1.0]).forward(&xs).unwrap();
assert_eq!(out.dtype(), DType::F16);
}
}