use crate::error::Error;
use crate::neural_network::layers::ParamCounts;
use crate::neural_network::layers::regularization::validation::validate_rate_exclusive;
use crate::neural_network::layers::validation::start_build;
use crate::neural_network::layers::{
built_layer_shape_functions, no_trainable_parameters_layer_functions,
};
use crate::neural_network::traits::{LayerBase, UnaryLayer};
use crate::neural_network::{Ctx, Shape, StateSlot, Tensor};
use ndarray_rand::RandomExt;
use ndarray_rand::rand::rngs::StdRng;
use ndarray_rand::rand_distr::Normal;
#[derive(Debug)]
pub struct GaussianDropout {
rate: f32,
built: Option<Shape>,
rng: StdRng,
}
impl GaussianDropout {
pub fn new(rate: f32) -> Result<Self, Error> {
validate_rate_exclusive(rate, "Dropout rate")?;
let rng = crate::random::make_rng(None);
Ok(GaussianDropout {
rate,
built: None,
rng,
})
}
pub fn with_random_state(mut self, random_state: u64) -> Self {
self.rng = crate::random::make_rng(Some(random_state));
self
}
}
impl LayerBase for GaussianDropout {
fn layer_type(&self) -> &str {
"GaussianDropout"
}
built_layer_shape_functions!();
no_trainable_parameters_layer_functions!();
fn apply_state(&mut self, state: &mut StateSlot<'_>) {
if let Some(rng) = state.take::<StdRng>("rng") {
self.rng = rng;
}
}
}
impl UnaryLayer for GaussianDropout {
fn build(&mut self, input: &Shape) -> Result<(), Error> {
let Some(built) = start_build(&self.built, "GaussianDropout", input)? else {
return Ok(());
};
input.check_min_rank("GaussianDropout", 1)?;
self.built = Some(built);
Ok(())
}
fn forward(&self, input: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
if !self.is_built() {
return Err(Error::not_built("GaussianDropout"));
}
if !ctx.is_training() || self.rate == 0.0 {
return Ok(input.clone());
}
let stddev = (self.rate / (1.0 - self.rate)).sqrt();
let mut rng = ctx
.take_state::<StdRng>("rng")
.unwrap_or_else(|| self.rng.clone());
let noise =
Tensor::random_using(input.raw_dim(), Normal::new(1.0, stddev).unwrap(), &mut rng);
ctx.set_state("rng", rng);
let output = input * &noise;
ctx.push_cache("GaussianDropout", noise);
Ok(output)
}
fn backward(&self, grad_output: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
if !ctx.is_training() || self.rate == 0.0 {
return Ok(grad_output.clone());
}
let noise: Tensor = ctx.pop_cache("GaussianDropout")?;
Ok(grad_output * &noise)
}
}