use crate::optim::optimizer::Optimizer;
use crate::tensor::Tensor;
pub struct Adam {
parameters: Vec<Tensor>,
lr: f32, beta1: f32, beta2: f32, epsilon: f32, t: i32,
m: Vec<Tensor>, v: Vec<Tensor>, }
impl Adam {
pub fn new(
parameters: Vec<Tensor>,
lr: f32,
betas: Option<(f32, f32)>,
epsilon: Option<f32>,
) -> Self {
let (beta1, beta2) = betas.unwrap_or((0.9, 0.999));
let epsilon_val = epsilon.unwrap_or(1e-8);
let mut m = Vec::with_capacity(parameters.len());
let mut v = Vec::with_capacity(parameters.len());
for p in ¶meters {
let shape = p.data.borrow().shape().to_vec();
m.push(Tensor::zeros(&shape, false));
v.push(Tensor::zeros(&shape, false));
}
Self {
parameters,
lr,
beta1,
beta2,
epsilon: epsilon_val,
t: 0,
m,
v,
}
}
}
impl Optimizer for Adam {
fn step(&mut self) {
self.t += 1;
for ((p, m_p), v_p) in self.parameters.iter()
.zip(self.m.iter_mut())
.zip(self.v.iter_mut())
{
if let Some(grad_tensor) = &p.grad {
let grad = grad_tensor.borrow();
let mut m_p_data = m_p.data.borrow_mut();
let mut v_p_data = v_p.data.borrow_mut();
*m_p_data = &*m_p_data * self.beta1 + &*grad * (1.0 - self.beta1);
let grad_sq = &*grad * &*grad;
*v_p_data = &*v_p_data * self.beta2 + &grad_sq * (1.0 - self.beta2);
let m_hat = &*m_p_data / (1.0 - self.beta1.powi(self.t));
let v_hat = &*v_p_data / (1.0 - self.beta2.powi(self.t));
let v_hat_sqrt = v_hat.mapv(f32::sqrt);
let update = &m_hat / &(v_hat_sqrt + self.epsilon);
let mut p_data = p.data.borrow_mut();
*p_data -= &(&update * self.lr);
}
}
}
fn zero_grad(&self) {
for p in &self.parameters {
if let Some(grad) = &p.grad {
grad.borrow_mut().fill(0.0);
}
}
}
}