1use luma_io::lpk::LumaPack;
2use luma_tensor::{Device, DynTensor, GradStore, Scalar, Tensor, no_grad};
3
4use super::Optimizer;
5
6#[derive(Clone, Debug)]
7pub struct RMSPropConfig {
8 pub lr: f64,
9 pub alpha: f64, pub eps: f64, pub weight_decay: f64,
12}
13
14impl Default for RMSPropConfig {
15 fn default() -> Self {
16 Self { lr: 1e-2, alpha: 0.99, eps: 1e-8, weight_decay: 0.0 }
17 }
18}
19
20struct RMSPropParam<D: Device> {
21 param: Tensor<D>,
22 square_avg: Tensor<D>,
23}
24
25pub struct RMSProp<D: Device> {
26 params: Vec<RMSPropParam<D>>,
27 config: RMSPropConfig,
28}
29
30impl<D: Device> RMSProp<D> {
31 pub fn new(params: impl Into<Vec<Tensor<D>>>, config: RMSPropConfig) -> luma_tensor::Result<Self> {
32 let params = params
33 .into()
34 .into_iter()
35 .map(|param| {
36 let square_avg = param.zeros_like()?;
37 Ok(RMSPropParam { param, square_avg })
38 })
39 .collect::<luma_tensor::Result<Vec<_>>>()?;
40 Ok(Self { params, config })
41 }
42}
43
44impl<D: Device> Optimizer for RMSProp<D> {
45 type Device = D;
46
47 fn set_lr(&mut self, lr: f64) {
48 self.config.lr = lr;
49 }
50
51 fn get_lr(&self) -> f64 {
52 self.config.lr
53 }
54
55 fn step(&mut self, grads: &GradStore<Self::Device>) -> luma_tensor::Result<()> {
56 no_grad!();
57 let lr = self.config.lr;
58 let alpha = self.config.alpha;
59 let eps = self.config.eps;
60 let weight_decay = self.config.weight_decay;
61
62 for RMSPropParam { param, square_avg } in self.params.iter_mut() {
63 if let Some(g) = grads.get(¶m) {
64 let g = g.clone();
65
66 if weight_decay != 0.0 {
76 g.add_(¶m.mul_scalar(weight_decay)?)?;
77 }
78
79 square_avg.mul_scalar_(alpha)?;
82 square_avg.add_(&g.pow(2.0)?.mul_scalar(1. - alpha)?)?;
83
84 let denom = square_avg.sqrt()?.add_scalar(eps)?;
86 param.sub_(&g.div(&denom)?.mul_scalar(lr)?)?
87 }
88 }
89
90 Ok(())
91 }
92
93 fn state_dict(&self) -> luma_tensor::Result<LumaPack<Self::Device>> {
94 let mut pack = LumaPack::new();
95 for (i, p) in self.params.iter().enumerate() {
96 pack.tensors.insert(format!("{i}.square_avg"), DynTensor::Float(p.square_avg.clone()));
97 }
98 pack.scalars.insert("lr".into(), Scalar::F64(self.config.lr));
99 pack.scalars.insert("alpha".into(), Scalar::F64(self.config.alpha));
100 pack.scalars.insert("eps".into(), Scalar::F64(self.config.eps));
101 pack.scalars.insert("weight_decay".into(), Scalar::F64(self.config.weight_decay));
102 Ok(pack)
103 }
104
105 fn load_state_dict(&mut self, pack: &LumaPack<Self::Device>) -> luma_tensor::Result<()> {
106 if let Some(v) = pack.scalars.get("lr").and_then(|s| s.to_f64()) {
107 self.config.lr = v;
108 }
109 if let Some(v) = pack.scalars.get("alpha").and_then(|s| s.to_f64()) {
110 self.config.alpha = v;
111 }
112 if let Some(v) = pack.scalars.get("eps").and_then(|s| s.to_f64()) {
113 self.config.eps = v;
114 }
115 if let Some(v) = pack.scalars.get("weight_decay").and_then(|s| s.to_f64()) {
116 self.config.weight_decay = v;
117 }
118 for (i, p) in self.params.iter_mut().enumerate() {
119 let key = format!("{i}.square_avg");
120 if let Some(dt) = pack.tensors.get(&key) {
121 if let Some(src) = dt.as_float() {
122 p.square_avg.copy_(src)?;
123 }
124 }
125 }
126 Ok(())
127 }
128}