use std::collections::HashMap;
use crate::Optimizer;
use crate::common::zeros_entry;
#[derive(Debug, Clone)]
pub struct Sgd {
pub lr: f32,
pub momentum: f32,
pub nesterov: bool,
pub weight_decay: f32,
v: HashMap<String, Vec<f32>>,
}
impl Sgd {
pub fn new(lr: f32) -> Self {
Self {
lr,
momentum: 0.0,
nesterov: false,
weight_decay: 0.0,
v: HashMap::new(),
}
}
pub fn with_momentum(mut self, momentum: f32, nesterov: bool) -> Self {
self.momentum = momentum;
self.nesterov = nesterov;
self
}
pub fn with_weight_decay(mut self, wd: f32) -> Self {
self.weight_decay = wd;
self
}
}
impl Sgd {
pub(crate) fn snapshot(&self) -> crate::OptimizerState {
let mut out = crate::OptimizerState {
step: 0,
buffers: Vec::new(),
};
out.extend_slot("v", &self.v);
out
}
pub(crate) fn restore(&mut self, state: &crate::OptimizerState) {
state.take_slot("v", &mut self.v);
}
}
impl Optimizer for Sgd {
fn set_lr(&mut self, lr: f32) {
self.lr = lr;
}
fn state_dict(&self) -> Option<crate::OptimizerState> {
Some(self.snapshot())
}
fn load_state_dict(&mut self, state: &crate::OptimizerState) -> bool {
self.restore(state);
true
}
fn step(&mut self, name: &str, _shape: &[usize], param: &mut [f32], grad: &[f32]) {
debug_assert_eq!(param.len(), grad.len());
let mu = self.momentum;
let wd = self.weight_decay;
let lr = self.lr;
let nesterov = self.nesterov;
let v = zeros_entry(&mut self.v, name, param.len());
if mu == 0.0 {
for (p, g) in param.iter_mut().zip(grad) {
let d = *g + wd * *p;
*p -= lr * d;
}
} else if nesterov {
for ((p, vi), g) in param.iter_mut().zip(v.iter_mut()).zip(grad) {
let d = *g + wd * *p;
*vi = mu * *vi + d;
*p -= lr * (d + mu * *vi);
}
} else {
for ((p, vi), g) in param.iter_mut().zip(v.iter_mut()).zip(grad) {
let d = *g + wd * *p;
*vi = mu * *vi + d;
*p -= lr * *vi;
}
}
}
}