use crate::compat::Sequential;
use crate::{AutodiffError, Tape, Tensor, Var};
pub struct Mlp {
model: Sequential,
dropout: f32,
}
const DEFAULT_SEED: u64 = 0x4D4C505F53454544;
impl Mlp {
pub fn new(
input_dim: usize,
hidden_dims: &[usize],
output_dim: usize,
dropout: f32,
) -> Result<Self, AutodiffError> {
Self::with_seed(input_dim, hidden_dims, output_dim, dropout, DEFAULT_SEED)
}
pub fn with_seed(
input_dim: usize,
hidden_dims: &[usize],
output_dim: usize,
dropout: f32,
seed: u64,
) -> Result<Self, AutodiffError> {
if input_dim == 0 || output_dim == 0 || hidden_dims.contains(&0) {
return Err(AutodiffError::InvalidArgument(
"Mlp::new: input_dim・hidden_dims の各要素・output_dim はいずれも 0 \
より大きい必要がある"
.to_string(),
));
}
if !dropout.is_finite() || !(0.0..=1.0).contains(&dropout) {
return Err(AutodiffError::InvalidArgument(format!(
"Mlp::new: dropout must be finite and in [0, 1], got {dropout}"
)));
}
let mut model = Sequential::new();
let mut prev = input_dim;
for (i, &h) in hidden_dims.iter().enumerate() {
model = model
.add_linear(prev, h, seed.wrapping_add(i as u64))?
.add_relu()
.add_dropout(dropout)?;
prev = h;
}
model = model.add_linear(
prev,
output_dim,
seed.wrapping_add(hidden_dims.len() as u64),
)?;
Ok(Mlp { model, dropout })
}
pub fn forward<'t>(&self, tape: &'t Tape, x: &Var<'t>) -> Result<Var<'t>, AutodiffError> {
self.model.forward(tape, x)
}
pub fn predict(&self, x: &Tensor<f32>) -> Result<Tensor<f32>, AutodiffError> {
self.model.predict(x)
}
pub fn sequential(&self) -> &Sequential {
&self.model
}
pub fn sequential_mut(&mut self) -> &mut Sequential {
&mut self.model
}
pub fn dropout(&self) -> f32 {
self.dropout
}
}