use crate::linalg::Matrix;
use crate::loss::Loss;
use crate::Float;
use rayon::prelude::*;
pub struct MSE<T: Float>(T);
impl<T: Float> MSE<T> {
pub fn new(datatype_number: T) -> Self {
Self(datatype_number)
}
}
impl<T: Float> Loss<T> for MSE<T> {
fn call(&self, output: &Matrix<T>, target: &Matrix<T>) -> T {
if output.shape() != target.shape() {
panic!("!!!Size of output matrix and target must be equal!!!\nOutput size:{:?} Target size: {:?}", output.shape(), target.shape())
}
let length = output.data.len();
let difference = target - output;
let mut total_loss = T::default();
for i in 0..difference.data.len() {
total_loss += difference.data[i] * difference.data[i];
}
total_loss / (T::from_usize(length))
}
fn gradient(&self, output: &Matrix<T>, target: &Matrix<T>) -> Matrix<T> {
if output.shape() != target.shape() {
panic!("!!!Size of output matrix and target must be equal!!!")
}
let length = output.data.len();
let diff = target - output;
let mut grad = vec![T::default(); length];
grad.par_iter_mut()
.enumerate()
.for_each(|(i, x)| *x = (T::from_usize(2) * diff.data[i]) / (T::from_usize(length)));
Matrix::new(grad, diff.rows, diff.cols)
}
}
#[cfg(test)]
mod tests {
use crate::linalg::{Matrix, Vector};
use crate::loss::mse::MSE;
use crate::loss::Loss;
use crate::{matrix, vector, DataType};
#[test]
fn mse_loss_test() {
let input = matrix![[1.0, 2.0, 3.0, 4.0]];
let output = matrix![[1.0, 3.0, 3.5, 4.5]];
let mse = MSE::new(DataType::f64());
assert_eq!(mse.call(&input, &output), 0.375);
}
#[test]
fn grad_test() {
let answer = vector![3f64, 2f64];
let data: Vec<f64> = (0..10).into_iter().map(|x1| x1 as f64).collect();
let target: Vec<f64> = data
.clone()
.into_iter()
.map(|x1| vector![x1, 1f64].scalar(&answer))
.collect();
let data: Vec<Vec<f64>> = (0..10)
.into_iter()
.map(|x1| vec![x1 as f64, 1f64])
.collect();
let x = Matrix::from(data);
let y = Vector::from(target);
let mut w = vector![0.5f64, 0.5f64];
let n = y.length as f64;
let mut attempts = 0usize;
println!("{}\n{}", x, y);
while (answer.clone() - &w).abs_sum() > 0.1f64 && attempts < 10000 {
let y_pred = x.clone() * &w;
let error = y_pred - &y;
w = w - x.clone().transpose() * &error * (2f64 / n) * 0.01;
attempts += 1;
}
println!("weights:{w}\n for {attempts} attempts"); }
#[test]
fn stochastic_gradient_descent_test() {
let n = 1000;
let m = 3;
let mut x = vec![vec![0f64; m]; n];
let mut y = vec![0f64; n];
for i in 0..n {
let i_num = rand::random::<f64>();
let j_num = rand::random::<f64>();
x[i] = vec![i_num, 1f64 - i_num, j_num];
y[i] = 2_f64 * i_num + 5_f64 * (1f64 - i_num) + 8_f64 * j_num;
}
let b = 100;
let alpha = 0.1;
let e = 500;
let mut w = Vector::from(vec![0.0f64; m]);
let mse = MSE::new(DataType::f64());
for _ in 0..e {
for i in (b..n).step_by(b) {
let x_batch = Matrix::from(x[i - b..i].to_vec()); let y_batch = Vector::from(y[i - b..i].to_vec()); let f = &x_batch * &w;
let loss = mse.call(&Matrix::from(f.clone()), &Matrix::from(y_batch.clone()));
let err = mse.gradient(&Matrix::from(f), &Matrix::from(y_batch));
let grad = x_batch.transpose() * &Vector::from(err);
w = w - grad * alpha;
if loss < 0.01 {
break;
};
}
}
println!("Веса {}", w);
assert_eq!(1, 1)
}
}