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::dropout::{
dropout_backward, dropout_output_shape,
};
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,
};
use crate::neural_network::traits::Layer;
use crate::parallel_gates::cheap_map_parallel_threshold;
use ndarray_rand::rand::rngs::StdRng;
use ndarray_rand::{RandomExt, rand_distr::Uniform};
#[derive(Debug)]
pub struct Dropout {
rate: f32,
input_shape: Vec<usize>,
mask: Option<Tensor>,
training: bool,
rng: StdRng,
}
impl Dropout {
pub fn new(rate: f32, input_shape: Vec<usize>) -> Result<Self, Error> {
validate_rate(rate, "Dropout rate")?;
Ok(Dropout {
rate,
input_shape,
mask: None,
training: true,
rng: crate::random::make_rng(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 Dropout {
fn forward(&mut self, input: &Tensor) -> Result<Tensor, Error> {
validate_input_shape(input.shape(), &self.input_shape)?;
if !self.training {
return Ok(input.clone());
}
if self.rate == 0.0 {
return Ok(input.clone());
}
if self.rate == 1.0 {
return Ok(Tensor::zeros(input.raw_dim()));
}
let mut mask = Tensor::random_using(
input.raw_dim(),
Uniform::new(0.0, 1.0).unwrap(),
&mut self.rng,
);
if input.len() >= cheap_map_parallel_threshold() {
mask.par_mapv_inplace(|x| if x >= self.rate { 1.0 } else { 0.0 });
} else {
mask.mapv_inplace(|x| if x >= self.rate { 1.0 } else { 0.0 });
}
let scale = 1.0 / (1.0 - self.rate);
let output = input * &mask * scale;
self.mask = Some(mask);
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> {
dropout_backward(grad_output, &self.mask, self.training, self.rate, "Dropout")
}
fn layer_type(&self) -> &str {
"Dropout"
}
fn output_shape(&self) -> String {
dropout_output_shape(&self.input_shape)
}
no_trainable_parameters_layer_functions!();
mode_dependent_layer_trait!();
}