use std::cmp;
use crate::layers::layer::Layer;
use crate::layers::*;
use crate::layers::LayerEnum;
use crate::metrics::Metric;
use crate::metrics::*;
use crate::tensor::Tensor;
use crate::dataset::{Dataset, RowType, ColumnType};
use crate::{loss::loss::Loss, random::Rand, optimizers::optimizer::OptimizerStep, optimizers::sgd::SGD};
use crate::loss::categorical_entropy::CategoricalEntropy;
use crate::utils;
struct Batch {
inputs: Tensor,
targets: Tensor
}
pub struct Sequential {
pub layers_enum: Vec<LayerEnum>,
pub layers: Vec<Box<dyn Layer>>,
loss: Box<dyn Loss>,
optim: Box<dyn OptimizerStep>,
metrics: Vec<Metric>,
seed: u32,
}
impl Sequential {
pub fn new() -> Sequential {
Sequential {
layers_enum: vec![],
layers: vec![],
loss: Box::new(CategoricalEntropy{}),
optim: Box::new(SGD::new(0.02)),
metrics: vec![],
seed: 0,
}
}
pub fn set_seed(&mut self, s: u32) {
self.seed = s;
}
pub fn add(&mut self, layer: LayerEnum) {
self.layers_enum.push(layer);
}
pub fn summary(&self) {
println!("Sequential model ({} layers) :", self.layers.len());
for (i, layer) in self.layers.iter().enumerate() {
println!("{} - {}", i+1, layer.get_info());
}
}
pub fn compile<T: 'static + Loss, U: 'static + OptimizerStep>(&mut self, loss: T, optim: U, metrics: Vec<Metric>) {
self.loss = Box::new(loss);
self.optim = Box::new(optim);
self.metrics = metrics;
self.layers.clear();
for layer in &self.layers_enum {
self.layers.push(
match layer {
LayerEnum::Dense { input_units, output_units } => {
Box::new(dense::Dense::new(*input_units, *output_units, self.seed))
}
LayerEnum::ReLU => {
Box::new(relu::ReLU::new())
}
LayerEnum::Softmax => {
Box::new(softmax::Softmax::new())
}
LayerEnum::TanH => {
Box::new(tanh::TanH::new())
}
LayerEnum::Sigmoid => {
Box::new(sigmoid::Sigmoid::new())
}
LayerEnum::Dropout { prob } => {
Box::new(dropout::Dropout::new(*prob, self.seed))
}
}
);
}
}
fn forward_propagation(&mut self, input: Tensor, train: bool) -> Tensor {
let mut activations: Vec<Tensor> = Vec::new();
activations.push(input);
for layer in self.layers.iter_mut() {
let activation = layer.forward(activations.last().unwrap().clone(), train);
activations.push(activation);
}
assert_eq!(activations.len(), self.layers.len() + 1);
activations.last().unwrap().clone()
}
fn backward_propagation(&mut self, gradient: Tensor) -> Tensor {
let mut gradients = Vec::new();
gradients.push(gradient);
for layer in self.layers.iter_mut().rev() {
let gradient = layer.backward(gradients.last().unwrap());
gradients.push(gradient);
}
gradients.last().unwrap().clone()
}
fn get_batches(&mut self, dataset: &Dataset, batch_size: usize, shuffle: bool) -> Vec<Batch> {
let x_train = dataset.get_tensor(RowType::Train, ColumnType::Feature);
let y_train = dataset.get_tensor(RowType::Train, ColumnType::Target);
let mut indices = (0..x_train.shape[0]).collect::<Vec<usize>>();
if shuffle {
let mut rand = Rand::new(self.seed);
rand.shuffle(&mut indices[..]);
self.seed += 1;
}
let mut result = Vec::new();
for batch_index in (0..x_train.shape[0]).rev().skip(batch_size - 1).step_by(batch_size).rev() {
let batch_indices: &[usize] = &indices[batch_index..batch_index + batch_size];
let x_batch = x_train.get_rows(batch_indices);
let y_batch = y_train.get_rows(batch_indices);
result.push(Batch {inputs: x_batch, targets: y_batch});
}
result
}
pub fn fit(&mut self, dataset: &Dataset, epochs: u32, verbose: bool) {
let batch_size = cmp::min(dataset.get_row_count(), 128);
for epoch in 0..epochs {
let mut epoch_loss = 0.0;
let batches = self.get_batches(dataset, batch_size, false);
let batches_len = batches.len() as f64;
for batch in batches {
let predicted = self.forward_propagation(batch.inputs, true);
epoch_loss += self.loss.compute_loss(&batch.targets, &predicted);
let loss_grad = self.loss.compute_loss_grad(&batch.targets, &predicted);
self.backward_propagation(loss_grad);
self.optim.step(&mut self.layers);
}
if verbose {
println!("\n------\nEpoch: {}", epoch);
println!("Train loss: {:.4}", epoch_loss/ batches_len);
if dataset.count_row_type(&RowType::Test) > 0 {
let test_predictions = self.predict_tensor(dataset.get_tensor(RowType::Test, ColumnType::Feature));
let test_true_values = &dataset.get_tensor(RowType::Test, ColumnType::Target);
assert_eq!(test_predictions.shape, test_true_values.shape, "Something wrong happened... o_O");
let test_loss = self.loss.compute_loss(test_true_values, &test_predictions);
println!("Test loss: {:.4}", test_loss);
for metric in &self.metrics {
let cm = confusion_matrix::ConfusionMatrix::new(
test_true_values.clone(),
test_predictions.clone()
);
match metric {
Metric::Accuracy => {
let acc_score = cm.accuracy_score();
println!("Accuracy : {:.2}%", acc_score);
}
Metric::Recall => {
let class = 1;
let recall_score = cm.recall_score(class);
}
Metric::Precision => {
let class = 1;
let precision_score = cm.precision_score(class);
}
Metric::F1 => {
let class = 1;
let f1_score = cm.f1_score(class);
}
}
}
}
}
}
}
pub fn predict(&mut self, input: &Vec<f64>) -> Tensor {
let tensor_input = Tensor::new(input.to_vec(), vec![1, input.to_vec().len()]);
self.predict_tensor(tensor_input)
}
pub fn predict_tensor(&mut self, input: Tensor) -> Tensor {
self.forward_propagation(input, false)
}
}