use crate::error::Error;
use crate::neural_network::Tensor;
use crate::neural_network::layers::TrainingParameters;
use crate::neural_network::layers::convolution::PaddingType;
use crate::neural_network::layers::layer_weight::LayerWeight;
use crate::neural_network::layers::pooling::layer_functions_2d_pooling;
use crate::neural_network::layers::pooling::pooling_engine::{
PoolKind, windowed_pool_backward, windowed_pool_forward,
};
use crate::neural_network::layers::pooling::validation::{
validate_all_dims_positive, validate_input_shape_dims, validate_pool_size_2d,
validate_strides_2d,
};
use crate::neural_network::layers::shape_helpers::calculate_output_shape_2d_pooling;
use crate::neural_network::traits::Layer;
#[derive(Debug)]
pub struct AveragePooling2D {
pool_size: (usize, usize),
strides: (usize, usize),
input_shape: Vec<usize>,
padding: PaddingType,
forward_input_shape: Option<Vec<usize>>,
}
impl AveragePooling2D {
pub fn new(pool_size: (usize, usize), input_shape: Vec<usize>) -> Result<Self, Error> {
validate_input_shape_dims(&input_shape, 4, "AveragePooling2D")?;
validate_all_dims_positive(&input_shape)?;
validate_pool_size_2d(pool_size, input_shape[2], input_shape[3])?;
Ok(AveragePooling2D {
pool_size,
strides: pool_size,
input_shape,
padding: PaddingType::Valid,
forward_input_shape: None,
})
}
pub fn with_strides(mut self, strides: (usize, usize)) -> Result<Self, Error> {
validate_strides_2d(strides)?;
self.strides = strides;
Ok(self)
}
pub fn with_padding(mut self, padding: PaddingType) -> Self {
self.padding = padding;
self
}
}
impl Layer for AveragePooling2D {
fn forward(&mut self, input: &Tensor) -> Result<Tensor, Error> {
if input.ndim() != 4 {
return Err(Error::invalid_input("input tensor is not 4D"));
}
self.forward_input_shape = Some(input.shape().to_vec());
let (output, _) = windowed_pool_forward(
input,
&[self.pool_size.0, self.pool_size.1],
&[self.strides.0, self.strides.1],
PoolKind::Average,
self.padding,
);
Ok(output)
}
fn predict(&self, input: &Tensor) -> Result<Tensor, Error> {
if input.ndim() != 4 {
return Err(Error::invalid_input("input tensor is not 4D"));
}
Ok(windowed_pool_forward(
input,
&[self.pool_size.0, self.pool_size.1],
&[self.strides.0, self.strides.1],
PoolKind::Average,
self.padding,
)
.0)
}
fn backward(&mut self, grad_output: &Tensor) -> Result<Tensor, Error> {
let input_shape = self
.forward_input_shape
.as_ref()
.ok_or_else(|| Error::forward_pass_not_run("AveragePooling2D"))?;
Ok(windowed_pool_backward(
grad_output,
input_shape,
&[self.pool_size.0, self.pool_size.1],
&[self.strides.0, self.strides.1],
PoolKind::Average,
None,
self.padding,
))
}
fn layer_type(&self) -> &str {
"AveragePooling2D"
}
layer_functions_2d_pooling!();
}