use crate::error::Error;
use crate::neural_network::Tensor;
use crate::neural_network::layers::TrainingParameters;
use crate::neural_network::layers::layer_weight::LayerWeight;
use crate::neural_network::layers::no_trainable_parameters_layer_functions;
use crate::neural_network::layers::regularization::mode_dependent_layer_set_training;
use crate::neural_network::layers::regularization::mode_dependent_layer_trait;
use crate::neural_network::layers::regularization::validation::{
validate_input_shape, validate_rate_exclusive,
};
use crate::neural_network::traits::Layer;
use ndarray_rand::RandomExt;
use ndarray_rand::rand::rngs::StdRng;
use ndarray_rand::rand_distr::Normal;
#[derive(Debug)]
pub struct GaussianDropout {
rate: f32,
input_shape: Vec<usize>,
training: bool,
rng: StdRng,
noise_cache: Option<Tensor>,
}
impl GaussianDropout {
pub fn new(rate: f32, input_shape: Vec<usize>) -> Result<Self, Error> {
validate_rate_exclusive(rate, "Dropout rate")?;
let rng = crate::random::make_rng(None);
Ok(GaussianDropout {
rate,
input_shape,
training: true,
rng,
noise_cache: None,
})
}
pub fn with_random_state(mut self, random_state: u64) -> Self {
self.rng = crate::random::make_rng(Some(random_state));
self
}
mode_dependent_layer_set_training!();
}
impl Layer for GaussianDropout {
fn forward(&mut self, input: &Tensor) -> Result<Tensor, Error> {
validate_input_shape(input.shape(), &self.input_shape)?;
if !self.training || self.rate == 0.0 {
return Ok(input.clone());
}
let stddev = (self.rate / (1.0 - self.rate)).sqrt();
let noise = Tensor::random_using(
input.raw_dim(),
Normal::new(1.0, stddev).unwrap(),
&mut self.rng,
);
let output = input * &noise;
self.noise_cache = Some(noise);
Ok(output)
}
fn predict(&self, input: &Tensor) -> Result<Tensor, Error> {
validate_input_shape(input.shape(), &self.input_shape)?;
Ok(input.clone())
}
fn backward(&mut self, grad_output: &Tensor) -> Result<Tensor, Error> {
if !self.training || self.rate == 0.0 {
return Ok(grad_output.clone());
}
match self.noise_cache.as_ref() {
Some(noise) => Ok(grad_output * noise),
None => Err(Error::forward_pass_not_run("GaussianDropout")),
}
}
fn layer_type(&self) -> &str {
"GaussianDropout"
}
fn output_shape(&self) -> String {
if self.input_shape.is_empty() {
String::from("Unknown")
} else {
format!(
"({})",
self.input_shape
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
no_trainable_parameters_layer_functions!();
mode_dependent_layer_trait!();
}