diff --git a/DIRECTORY.md b/DIRECTORY.md index dc709a72f69..839f82db8f0 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -147,6 +147,7 @@ * [Linear Regression](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/linear_regression.rs) * Loss Function * [Hinge Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/hinge_loss.rs) + * [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/huber_loss.rs) * [Kl Divergence Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/kl_divergence_loss.rs) * [Mean Absolute Error Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/mean_absolute_error_loss.rs) * [Mean Squared Error Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/mean_squared_error_loss.rs) diff --git a/src/machine_learning/loss_function/huber_loss.rs b/src/machine_learning/loss_function/huber_loss.rs new file mode 100644 index 00000000000..f81e8deb85c --- /dev/null +++ b/src/machine_learning/loss_function/huber_loss.rs @@ -0,0 +1,74 @@ +/// Computes the Huber loss between arrays of true and predicted values. +/// +/// # Arguments +/// +/// * `y_true` - An array of true values. +/// * `y_pred` - An array of predicted values. +/// * `delta` - The threshold parameter that controls the linear behavior of the loss function. +/// +/// # Returns +/// +/// The average Huber loss for all pairs of true and predicted values. +pub fn huber_loss(y_true: &[f64], y_pred: &[f64], delta: f64) -> Option { + if y_true.len() != y_pred.len() || y_pred.is_empty() { + return None; + } + + let loss: f64 = y_true + .iter() + .zip(y_pred.iter()) + .map(|(&true_val, &pred_val)| { + let residual = (true_val - pred_val).abs(); + match residual { + r if r <= delta => 0.5 * r.powi(2), + _ => delta * residual - 0.5 * delta.powi(2), + } + }) + .sum(); + + Some(loss / (y_pred.len() as f64)) +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! huber_loss_tests { + ($($name:ident: $test_case:expr,)*) => { + $( + #[test] + fn $name() { + let (y_true, y_pred, delta, expected_loss) = $test_case; + assert_eq!(huber_loss(&y_true, &y_pred, delta), expected_loss); + } + )* + }; + } + + huber_loss_tests! { + test_huber_loss_residual_less_than_delta: ( + vec![10.0, 8.0, 12.0], + vec![9.0, 7.0, 11.0], + 1.0, + Some(0.5) + ), + test_huber_loss_residual_greater_than_delta: ( + vec![3.0, 5.0, 7.0], + vec![2.0, 4.0, 8.0], + 0.5, + Some(0.375) + ), + test_huber_loss_invalid_length: ( + vec![10.0, 8.0, 12.0], + vec![7.0, 6.0], + 1.0, + None + ), + test_huber_loss_empty_prediction: ( + vec![10.0, 8.0, 12.0], + vec![], + 1.0, + None + ), + } +} diff --git a/src/machine_learning/loss_function/mod.rs b/src/machine_learning/loss_function/mod.rs index 45d19b5a946..c0196666dbf 100644 --- a/src/machine_learning/loss_function/mod.rs +++ b/src/machine_learning/loss_function/mod.rs @@ -1,9 +1,11 @@ mod hinge_loss; +mod huber_loss; mod kl_divergence_loss; mod mean_absolute_error_loss; mod mean_squared_error_loss; pub use self::hinge_loss::hng_loss; +pub use self::huber_loss::huber_loss; pub use self::kl_divergence_loss::kld_loss; pub use self::mean_absolute_error_loss::mae_loss; pub use self::mean_squared_error_loss::mse_loss; diff --git a/src/machine_learning/mod.rs b/src/machine_learning/mod.rs index 8fdc0e57b1a..a88a811e0ba 100644 --- a/src/machine_learning/mod.rs +++ b/src/machine_learning/mod.rs @@ -8,6 +8,7 @@ pub use self::cholesky::cholesky; pub use self::k_means::k_means; pub use self::linear_regression::linear_regression; pub use self::loss_function::hng_loss; +pub use self::loss_function::huber_loss; pub use self::loss_function::kld_loss; pub use self::loss_function::mae_loss; pub use self::loss_function::mse_loss;