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::{
apply_spatial_dropout_threshold, dropout_output_shape, spatial_dropout_backward,
spatial_dropout_scale,
};
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_ndim, validate_input_shape, validate_rate,
};
use crate::neural_network::traits::Layer;
use crate::parallel_gates::{
cheap_map_parallel_threshold, spatial_dropout_scale_parallel_min_elems,
};
use ndarray::IxDyn;
use ndarray_rand::rand::rngs::StdRng;
use ndarray_rand::{RandomExt, rand_distr::Uniform};
#[derive(Debug)]
pub struct SpatialDropout3D {
rate: f32,
input_shape: Vec<usize>,
mask: Option<Tensor>,
training: bool,
rng: StdRng,
}
impl SpatialDropout3D {
pub fn new(rate: f32, input_shape: Vec<usize>) -> Result<Self, Error> {
validate_rate(rate, "Dropout rate")?;
Ok(SpatialDropout3D {
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 SpatialDropout3D {
fn forward(&mut self, input: &Tensor) -> Result<Tensor, Error> {
validate_input_shape(input.shape(), &self.input_shape)?;
validate_input_ndim(
input.ndim(),
5,
"SpatialDropout3D (batch_size, channels, depth, height, width)",
)?;
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 shape = input.shape();
let batch_size = shape[0];
let channels = shape[1];
let mut mask_2d = Tensor::random_using(
IxDyn(&[batch_size, channels]),
Uniform::new(0.0, 1.0).unwrap(),
&mut self.rng,
);
apply_spatial_dropout_threshold(&mut mask_2d, self.rate, cheap_map_parallel_threshold());
let channel_mask = mask_2d.as_slice().expect("per-channel mask is contiguous");
let output = spatial_dropout_scale(
input,
channel_mask,
self.rate,
spatial_dropout_scale_parallel_min_elems(),
);
self.mask = Some(mask_2d);
Ok(output)
}
fn predict(&self, input: &Tensor) -> Result<Tensor, Error> {
validate_input_shape(input.shape(), &self.input_shape)?;
validate_input_ndim(
input.ndim(),
5,
"SpatialDropout3D (batch_size, channels, depth, height, width)",
)?;
Ok(input.clone())
}
fn backward(&mut self, grad_output: &Tensor) -> Result<Tensor, Error> {
spatial_dropout_backward(
grad_output,
&self.mask,
self.training,
self.rate,
"SpatialDropout3D",
spatial_dropout_scale_parallel_min_elems(),
)
}
fn layer_type(&self) -> &str {
"SpatialDropout3D"
}
fn output_shape(&self) -> String {
dropout_output_shape(&self.input_shape)
}
no_trainable_parameters_layer_functions!();
mode_dependent_layer_trait!();
}