use scirs2_core::ndarray::{Array, Dimension, ScalarOperand};
use scirs2_core::numeric::Float;
use std::fmt::Debug;
use scirs2_core::ScientificNumber;
use scirs2_optimize::stochastic::{minimize_sgd, SGDOptions};
use crate::error::Result;
use crate::optimizers::Optimizer;
#[derive(Debug, Clone)]
pub struct SGD<A: Float + ScalarOperand + Debug> {
learning_rate: A,
momentum: A,
weight_decay: A,
velocity: Option<Vec<Array<A, scirs2_core::ndarray::IxDyn>>>,
}
impl<A: Float + ScalarOperand + Debug + Send + Sync> SGD<A> {
pub fn new(learning_rate: A) -> Self {
Self {
learning_rate,
momentum: A::zero(),
weight_decay: A::zero(),
velocity: None,
}
}
pub fn new_with_config(learning_rate: A, momentum: A, weight_decay: A) -> Self {
Self {
learning_rate,
momentum,
weight_decay,
velocity: None,
}
}
pub fn set_momentum(&mut self, momentum: A) -> &mut Self {
self.momentum = momentum;
self
}
pub fn with_momentum(mut self, momentum: A) -> Self {
self.momentum = momentum;
self
}
pub fn get_momentum(&self) -> A {
self.momentum
}
pub fn learning_rate(&self) -> A {
self.learning_rate
}
pub fn set_weight_decay(&mut self, weight_decay: A) -> &mut Self {
self.weight_decay = weight_decay;
self
}
pub fn with_weight_decay(mut self, weight_decay: A) -> Self {
self.weight_decay = weight_decay;
self
}
pub fn get_weight_decay(&self) -> A {
self.weight_decay
}
}
impl<A, D> Optimizer<A, D> for SGD<A>
where
A: Float + ScalarOperand + Debug + Send + Sync,
D: Dimension,
{
fn step(&mut self, params: &Array<A, D>, gradients: &Array<A, D>) -> Result<Array<A, D>> {
let params_dyn = params.to_owned().into_dyn();
let gradients_dyn = gradients.to_owned().into_dyn();
if self.velocity.is_none() {
self.velocity = Some(vec![Array::zeros(params_dyn.raw_dim())]);
}
let velocity = self.velocity.as_mut().expect("unwrap failed");
if velocity.is_empty() {
velocity.push(Array::zeros(params_dyn.raw_dim()));
} else if velocity[0].raw_dim() != params_dyn.raw_dim() {
velocity[0] = Array::zeros(params_dyn.raw_dim());
}
let adjusted_gradients = if self.weight_decay > A::zero() {
&gradients_dyn + &(¶ms_dyn * self.weight_decay)
} else {
gradients_dyn
};
if self.momentum > A::zero() {
velocity[0] =
&velocity[0] * self.momentum + &(&adjusted_gradients * self.learning_rate);
} else {
velocity[0] = &adjusted_gradients * self.learning_rate;
}
let updated_params = ¶ms_dyn - &velocity[0];
Ok(updated_params
.into_dimensionality::<D>()
.expect("unwrap failed"))
}
fn get_learning_rate(&self) -> A {
self.learning_rate
}
fn set_learning_rate(&mut self, learning_rate: A) {
self.learning_rate = learning_rate;
}
}