use crate::data_loader::DataFrameTransformer;
use crate::layers::layer::{Layer, LinearLayer};
use crate::model::activation_functions::{ActivationFunction, ActivationFunctionType};
use crate::model::kernel_functions::{KernelFunction, KernelFunctionType};
use crate::model::loss_functions::{LossFunction, LossFunctionType};
use crate::model::*;
use polars::prelude::*;
use polars::series::Series;
pub struct MLP {
pub loss_function: LossFunctionType,
pub activation_functions: Vec<ActivationFunctionType>,
pub layers: Vec<LinearLayer>,
}
impl MLP {
pub fn new(loss_function: LossFunctionType) -> MLP {
MLP {
loss_function,
activation_functions: Vec::new(),
layers: Vec::new(),
}
}
pub fn new_with_layers(
loss_function: LossFunctionType,
layers: Vec<LinearLayer>,
activation_functions: Vec<ActivationFunctionType>,
) -> MLP {
assert_eq!(layers.len(), activation_functions.len());
MLP {
loss_function,
activation_functions,
layers,
}
}
pub fn add_layer(
&self,
layer: LinearLayer,
activation_function: ActivationFunctionType,
) -> MLP {
let mut new_layers: Vec<LinearLayer> = self.layers.clone();
let mut new_activation_functions: Vec<ActivationFunctionType> =
self.activation_functions.clone();
new_layers.push(layer);
new_activation_functions.push(activation_function);
MLP {
loss_function: self.loss_function.clone(),
activation_functions: new_activation_functions,
layers: new_layers,
}
}
pub fn set_layer(
&self,
i: usize,
layer: LinearLayer,
activation_function: ActivationFunctionType,
) -> MLP {
if i >= self.layers.len() {
panic!("Index out of bounds");
}
let mut new_layers: Vec<LinearLayer> = self.layers.clone();
let mut new_activation_functions: Vec<ActivationFunctionType> =
self.activation_functions.clone();
new_layers[i] = layer;
new_activation_functions[i] = activation_function;
MLP {
loss_function: self.loss_function.clone(),
activation_functions: new_activation_functions,
layers: new_layers,
}
}
pub fn forward(&self, inputs: Series) -> Series {
let mut outputs: Series = inputs.clone();
for (i, layer) in self.layers.iter().enumerate() {
outputs = layer.forward(outputs, self.activation_functions[i].clone());
}
outputs
}
pub fn backward(
&mut self,
inputs: &Series,
outputs: &Series,
true_values: &Series,
learning_rate: f64
) -> (Vec<Series>, Vec<Series>) {
let grad_outputs: f64 = self.loss_function.intercept_gradient(true_values, outputs);
let mut weights_gradients: Vec<Series> = Vec::new();
let mut biases_gradients: Vec<Series> = Vec::new();
let mut grad_outputs_series = Series::new("grad_outputs", vec![grad_outputs]);
for i in (0..self.layers.len()).rev() {
let layer = &self.layers[i];
let layer_inputs = if i == 0 {
inputs.clone()
} else {
self.layers[i - 1].forward(inputs.clone(), self.activation_functions[i - 1].clone())
};
let (new_weights, new_biases) = layer.backward(layer_inputs.clone(), grad_outputs_series.clone(), learning_rate);
weights_gradients.push(new_weights.clone());
biases_gradients.push(new_biases.clone());
grad_outputs_series = new_weights.clone();
}
weights_gradients.reverse();
biases_gradients.reverse();
(weights_gradients, biases_gradients)
}
}
impl model::SupervisedModeller for MLP {
fn fit(
&mut self,
x: &DataFrame,
y: &Series,
num_epochs: u32,
learning_rate: f64,
) -> Result<(), PolarsError> {
let x: Series = x.get_col_by_index(0)?;
for _ in 0..num_epochs {
let y_pred: Series = self.forward(x.clone());
let (updated_weights, updated_biases) = self.backward(&x, &y_pred, y, learning_rate);
for i in 0..self.layers.len() {
self.layers[i].weights = updated_weights[i].clone();
self.layers[i].biases = updated_biases[i].clone();
}
}
Ok(())
}
fn predict(&self, x: &DataFrame) -> Result<Series, PolarsError> {
let mut outputs: Series = x.get_col_by_index(0).unwrap();
for (i, layer) in self.layers.iter().enumerate() {
outputs = layer.forward(outputs, self.activation_functions[i].clone());
}
Ok(outputs)
}
fn loss(&self, x: &DataFrame, y: &Series) -> Result<f64, PolarsError> {
let y_pred: Series = self.predict(x)?;
Ok(self.loss_function.loss(&y_pred, y))
}
fn accuracy(&self, x: &DataFrame, y: &Series) -> Result<f64, PolarsError> {
let y_pred: Series = self.predict(x)?;
let accuracy: f64 = y_pred.equal(y).unwrap().sum().unwrap() as f64 / y_pred.len() as f64;
Ok(accuracy)
}
}
impl std::fmt::Display for MLP {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut layers_string = String::new();
for (i, layer) in self.layers.iter().enumerate() {
layers_string.push_str(&format!(
"Layer {}:\n\t\tNumber of neurons: {}\n",
i,
layer.weights.len()
));
layers_string.push_str(&format!(
"\t\tActivation Function: {}",
self.activation_functions[i]
));
}
write!(
f,
"MLP - {} layers\n
Loss Function: {}\n
Layers: {} \n
",
self.layers.len(),
self.loss_function,
layers_string
)
}
}