use nove_tensor::{DType, Device, Tensor};
use std::{
collections::HashMap,
fmt::Display,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{Model, ModelError};
static ID: AtomicUsize = AtomicUsize::new(1);
#[derive(Debug, Clone)]
pub struct Dropout {
probability: f32,
id: usize,
}
impl Dropout {
pub fn new(probability: f32) -> Result<Self, ModelError> {
if !(0.0..1.0).contains(&probability) {
return Err(ModelError::InvalidArgument(
"Dropout probability must be in range [0, 1)".to_string(),
));
}
Ok(Self {
probability,
id: ID.fetch_add(1, Ordering::Relaxed),
})
}
}
impl Model for Dropout {
type Input = (Tensor, bool);
type Output = Tensor;
fn forward(&mut self, input: Self::Input) -> Result<Self::Output, crate::ModelError> {
let (xs, training) = input;
if !training {
return Ok(xs);
}
let xs_shape = xs.shape()?;
let xs_device = xs.device()?;
let xs_dtype = xs.dtype()?;
let scale = 1.0 / (1.0 - self.probability) as f64;
let mask = Tensor::rand(0.0f32, 1.0f32, &xs_shape, &xs_device, false)?
.ge(&Tensor::from_scalar(self.probability, &xs_device, false)?
.broadcast(&xs_shape)?
.to_dtype(&xs_dtype)?)?
.to_dtype(&xs_dtype)?
.affine(scale, 0.0)?;
Ok(xs.mul(&mask)?)
}
fn require_grad(&mut self, _: bool) -> Result<(), crate::ModelError> {
Ok(())
}
fn to_device(&mut self, _: &Device) -> Result<(), crate::ModelError> {
Ok(())
}
fn to_dtype(&mut self, _: &DType) -> Result<(), crate::ModelError> {
Ok(())
}
fn parameters(&self) -> Result<Vec<nove_tensor::Tensor>, crate::ModelError> {
Ok(vec![])
}
fn named_parameters(&self) -> Result<HashMap<String, Tensor>, ModelError> {
Ok(HashMap::new())
}
}
impl Display for Dropout {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "dropout.{}(probability={})", self.id, self.probability)
}
}