use crate::error::Error;
use crate::neural_network::LayerPath;
use crate::neural_network::ctx::Grads;
use crate::neural_network::optimizers::kernels;
use crate::neural_network::optimizers::validation::{
validate_decay_rate, validate_epsilon, validate_global_clipnorm, validate_learning_rate,
validate_non_negative_finite,
};
use crate::neural_network::traits::{LayerBase, Optimizer, ParamId};
use ahash::AHashMap;
#[derive(Debug)]
pub struct RMSprop {
learning_rate: f32,
rho: f32,
epsilon: f32,
caches: AHashMap<ParamId, Vec<f32>>,
global_clipnorm: Option<f32>,
weight_decay: f32,
}
impl RMSprop {
pub fn new(
learning_rate: f32,
rho: f32,
epsilon: f32,
weight_decay: f32,
) -> Result<Self, Error> {
validate_learning_rate(learning_rate)?;
validate_decay_rate(rho, "rho")?;
validate_epsilon(epsilon)?;
validate_non_negative_finite(weight_decay, "weight_decay")?;
Ok(Self {
learning_rate,
rho,
epsilon,
caches: AHashMap::new(),
global_clipnorm: None,
weight_decay,
})
}
pub fn with_global_clipnorm(mut self, global_clipnorm: f32) -> Result<Self, Error> {
validate_global_clipnorm(Some(global_clipnorm))?;
self.global_clipnorm = Some(global_clipnorm);
Ok(self)
}
}
impl Optimizer for RMSprop {
fn global_clipnorm(&self) -> Option<f32> {
self.global_clipnorm
}
fn learning_rate(&self) -> f32 {
self.learning_rate
}
fn set_learning_rate(&mut self, learning_rate: f32) {
self.learning_rate = learning_rate;
}
fn update(
&mut self,
path: &LayerPath,
layer: &mut dyn LayerBase,
grads: &Grads,
grad_scale: f32,
) {
for pg in layer.parameters_mut() {
let Some(grad) = grads.get(&path.param(pg.name)) else {
continue;
};
let grad = grad
.as_slice()
.expect("a stored gradient is in the standard memory order");
debug_assert_eq!(grad.len(), pg.value.len());
let cache = self.caches.entry(path.param(pg.name)).or_default();
if cache.len() != pg.value.len() {
*cache = vec![0.0; pg.value.len()];
}
let grad = kernels::scaled_grad(grad, grad_scale);
if pg.decays {
kernels::apply_weight_decay(pg.value, self.learning_rate, self.weight_decay);
}
kernels::rmsprop_step(
pg.value,
&grad,
cache,
self.rho,
self.learning_rate,
self.epsilon,
);
}
}
}