1use burn::tensor::{activation, Tensor};
2#[cfg(test)]
3use burn::tensor::{Device, TensorData};
4
5pub fn burn_rms_norm(x: Tensor<2>, w: Tensor<1>, eps: f32) -> Tensor<2> {
7 let mean_sq = x.clone().square().mean_dim(1);
8 let rms = (mean_sq.add_scalar(eps)).sqrt();
9 let normed = x / rms;
10 normed * w.unsqueeze_dim::<2>(0)
11}
12
13pub fn burn_layer_norm(x: Tensor<2>, w: Tensor<1>, b: Tensor<1>, eps: f32) -> Tensor<2> {
15 let mean = x.clone().mean_dim(1);
16 let centered = x - mean;
17 let var = centered.clone().square().mean_dim(1);
18 let std = (var.add_scalar(eps)).sqrt();
19 let normed = centered / std;
20 normed * w.unsqueeze_dim::<2>(0) + b.unsqueeze_dim::<2>(0)
21}
22
23pub fn burn_softmax_last_dim(x: Tensor<2>) -> Tensor<2> {
25 activation::softmax(x, 1)
26}
27
28pub fn burn_rms_norm_nd<const D: usize>(x: Tensor<D>, w: Tensor<1>, eps: f32) -> Tensor<D> {
30 let dims = x.dims();
31 let last = dims[D - 1];
32 let lead: usize = dims[..D - 1].iter().product();
33 burn_rms_norm(x.reshape([lead, last]), w, eps).reshape(dims)
34}
35
36pub fn burn_layer_norm_nd<const D: usize>(
38 x: Tensor<D>,
39 w: Tensor<1>,
40 b: Tensor<1>,
41 eps: f32,
42) -> Tensor<D> {
43 let dims = x.dims();
44 let last = dims[D - 1];
45 let lead: usize = dims[..D - 1].iter().product();
46 burn_layer_norm(x.reshape([lead, last]), w, b, eps).reshape(dims)
47}
48
49pub fn burn_layer_norm_no_affine(x: Tensor<2>, eps: f32) -> Tensor<2> {
51 let mean = x.clone().mean_dim(1);
52 let centered = x - mean;
53 let var = centered.clone().square().mean_dim(1);
54 let std = (var.add_scalar(eps)).sqrt();
55 centered / std
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 fn dev() -> Device {
63 Device::flex()
64 }
65
66 #[test]
67 fn rms_matches_manual() {
68 let device = dev();
69 let x = Tensor::<2>::from_data(TensorData::from([[1.0f32, 2.0, 3.0, -4.0]]), &device);
70 let w = Tensor::<1>::from_data(TensorData::from([2.0f32, 2.0, 2.0, 2.0]), &device);
71 let got: Vec<f32> = burn_rms_norm(x, w, 1e-6).to_data().try_to_vec().unwrap();
72 let vals = [1.0f32, 2.0, 3.0, -4.0];
73 let mean_sq: f32 = vals.iter().map(|v| v * v).sum::<f32>() / 4.0;
74 let rms = (mean_sq + 1e-6).sqrt();
75 for (g, v) in got.iter().zip(vals.iter()) {
76 assert!((g - v / rms * 2.0).abs() < 1e-5, "{g} vs {v}");
77 }
78 }
79
80 #[test]
81 fn softmax_sums_to_one() {
82 let device = dev();
83 let x = Tensor::<2>::from_data(TensorData::from([[1.0f32, 2.0, 3.0]]), &device);
84 let p: Vec<f32> = burn_softmax_last_dim(x).to_data().try_to_vec().unwrap();
85 assert!((p.iter().sum::<f32>() - 1.0).abs() < 1e-5);
86 }
87}