mirror of
https://github.com/Magnus167/rustframe.git
synced 2025-08-20 19:20:01 +00:00
Compare commits
2 Commits
75d07371b2
...
96f434bf94
Author | SHA1 | Date | |
---|---|---|---|
![]() |
96f434bf94 | ||
![]() |
46abeb12a7 |
@ -1,5 +1,5 @@
|
|||||||
|
use crate::compute::activations::{drelu, relu, sigmoid};
|
||||||
use crate::matrix::{Matrix, SeriesOps};
|
use crate::matrix::{Matrix, SeriesOps};
|
||||||
use crate::compute::activations::{relu, drelu, sigmoid};
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
/// Supported activation functions
|
/// Supported activation functions
|
||||||
@ -167,7 +167,11 @@ impl DenseNN {
|
|||||||
LossKind::BCE => self.loss.gradient(&y_hat, y),
|
LossKind::BCE => self.loss.gradient(&y_hat, y),
|
||||||
LossKind::MSE => {
|
LossKind::MSE => {
|
||||||
let grad = self.loss.gradient(&y_hat, y);
|
let grad = self.loss.gradient(&y_hat, y);
|
||||||
let dz = self.activations.last().unwrap().derivative(zs.last().unwrap());
|
let dz = self
|
||||||
|
.activations
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.derivative(zs.last().unwrap());
|
||||||
grad.zip(&dz, |g, da| g * da)
|
grad.zip(&dz, |g, da| g * da)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -203,15 +207,22 @@ impl DenseNN {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------
|
|
||||||
// Simple tests
|
|
||||||
// ------------------------------
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::matrix::Matrix;
|
use crate::matrix::Matrix;
|
||||||
|
|
||||||
|
/// Compute MSE = 1/m * Σ (ŷ - y)²
|
||||||
|
fn mse_loss(y_hat: &Matrix<f64>, y: &Matrix<f64>) -> f64 {
|
||||||
|
let m = y.rows() as f64;
|
||||||
|
y_hat
|
||||||
|
.zip(y, |yh, yv| (yh - yv).powi(2))
|
||||||
|
.data()
|
||||||
|
.iter()
|
||||||
|
.sum::<f64>()
|
||||||
|
/ m
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_predict_shape() {
|
fn test_predict_shape() {
|
||||||
let config = DenseNNConfig {
|
let config = DenseNNConfig {
|
||||||
@ -232,7 +243,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_train_no_epochs() {
|
fn test_train_no_epochs_does_nothing() {
|
||||||
let config = DenseNNConfig {
|
let config = DenseNNConfig {
|
||||||
input_size: 1,
|
input_size: 1,
|
||||||
hidden_layers: vec![2],
|
hidden_layers: vec![2],
|
||||||
@ -244,35 +255,86 @@ mod tests {
|
|||||||
epochs: 0,
|
epochs: 0,
|
||||||
};
|
};
|
||||||
let mut model = DenseNN::new(config);
|
let mut model = DenseNN::new(config);
|
||||||
let x = Matrix::from_vec(vec![1.0, 2.0], 2, 1);
|
let x = Matrix::from_vec(vec![0.0, 1.0], 2, 1);
|
||||||
|
let y = Matrix::from_vec(vec![0.0, 1.0], 2, 1);
|
||||||
|
|
||||||
let before = model.predict(&x);
|
let before = model.predict(&x);
|
||||||
model.train(&x, &before);
|
model.train(&x, &y);
|
||||||
let after = model.predict(&x);
|
let after = model.predict(&x);
|
||||||
|
|
||||||
for i in 0..before.rows() {
|
for i in 0..before.rows() {
|
||||||
assert!((before[(i, 0)] - after[(i, 0)]).abs() < 1e-12);
|
for j in 0..before.cols() {
|
||||||
|
assert!(
|
||||||
|
(before[(i, j)] - after[(i, j)]).abs() < 1e-12,
|
||||||
|
"prediction changed despite 0 epochs"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dense_nn_step() {
|
fn test_train_one_epoch_changes_predictions() {
|
||||||
|
// Single-layer sigmoid regression so gradients flow.
|
||||||
let config = DenseNNConfig {
|
let config = DenseNNConfig {
|
||||||
input_size: 1,
|
input_size: 1,
|
||||||
hidden_layers: vec![2],
|
hidden_layers: vec![],
|
||||||
activations: vec![ActivationKind::Relu, ActivationKind::Sigmoid],
|
activations: vec![ActivationKind::Sigmoid],
|
||||||
output_size: 1,
|
output_size: 1,
|
||||||
initializer: InitializerKind::He,
|
initializer: InitializerKind::Uniform(0.1),
|
||||||
loss: LossKind::BCE,
|
loss: LossKind::MSE,
|
||||||
learning_rate: 0.01,
|
learning_rate: 1.0,
|
||||||
epochs: 10000,
|
epochs: 1,
|
||||||
};
|
};
|
||||||
let mut model = DenseNN::new(config);
|
let mut model = DenseNN::new(config);
|
||||||
let x = Matrix::from_vec(vec![1.0, 2.0, 3.0, 4.0], 4, 1);
|
|
||||||
let y = Matrix::from_vec(vec![0.0, 0.0, 1.0, 1.0], 4, 1);
|
let x = Matrix::from_vec(vec![0.0, 1.0], 2, 1);
|
||||||
|
let y = Matrix::from_vec(vec![0.0, 1.0], 2, 1);
|
||||||
|
|
||||||
|
let before = model.predict(&x);
|
||||||
model.train(&x, &y);
|
model.train(&x, &y);
|
||||||
let preds = model.predict(&x);
|
let after = model.predict(&x);
|
||||||
assert!((preds[(0, 0)] - 0.0).abs() < 0.5);
|
|
||||||
assert!((preds[(1, 0)] - 0.0).abs() < 0.5);
|
// At least one of the two outputs must move by >ϵ
|
||||||
assert!((preds[(2, 0)] - 1.0).abs() < 0.5);
|
let mut moved = false;
|
||||||
assert!((preds[(3, 0)] - 1.0).abs() < 0.5);
|
for i in 0..before.rows() {
|
||||||
|
if (before[(i, 0)] - after[(i, 0)]).abs() > 1e-8 {
|
||||||
|
moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(moved, "predictions did not change after 1 epoch");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_training_reduces_mse_loss() {
|
||||||
|
// Same single‐layer sigmoid setup; check loss goes down.
|
||||||
|
let config = DenseNNConfig {
|
||||||
|
input_size: 1,
|
||||||
|
hidden_layers: vec![],
|
||||||
|
activations: vec![ActivationKind::Sigmoid],
|
||||||
|
output_size: 1,
|
||||||
|
initializer: InitializerKind::Uniform(0.1),
|
||||||
|
loss: LossKind::MSE,
|
||||||
|
learning_rate: 1.0,
|
||||||
|
epochs: 10,
|
||||||
|
};
|
||||||
|
let mut model = DenseNN::new(config);
|
||||||
|
|
||||||
|
let x = Matrix::from_vec(vec![0.0, 1.0, 0.5], 3, 1);
|
||||||
|
let y = Matrix::from_vec(vec![0.0, 1.0, 0.5], 3, 1);
|
||||||
|
|
||||||
|
let before_preds = model.predict(&x);
|
||||||
|
let before_loss = mse_loss(&before_preds, &y);
|
||||||
|
|
||||||
|
model.train(&x, &y);
|
||||||
|
|
||||||
|
let after_preds = model.predict(&x);
|
||||||
|
let after_loss = mse_loss(&after_preds, &y);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
after_loss < before_loss,
|
||||||
|
"MSE did not decrease (before: {}, after: {})",
|
||||||
|
before_loss,
|
||||||
|
after_loss
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user