use crate::error::Error;
use crate::neural_network::Tensor;
use crate::neural_network::losses::{clip_probabilities, validate_same_shape};
use crate::neural_network::traits::Loss;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct BinaryCrossEntropy;
impl BinaryCrossEntropy {
pub fn new() -> Self {
Self {}
}
}
impl Loss for BinaryCrossEntropy {
fn compute_loss(&self, y_true: &Tensor, y_pred: &Tensor) -> Result<f32, Error> {
validate_same_shape(y_true, y_pred)?;
let y_pred_clipped = clip_probabilities(y_pred);
let log_pred = y_pred_clipped.mapv(|y_p| y_p.ln());
let log_one_minus_pred = (1.0 - &y_pred_clipped).mapv(|y_p| y_p.ln());
let losses = y_true * &log_pred + (1.0 - y_true) * &log_one_minus_pred;
let n = losses.len() as f32;
Ok(-losses.sum() / n)
}
fn compute_grad(&self, y_true: &Tensor, y_pred: &Tensor) -> Result<Tensor, Error> {
validate_same_shape(y_true, y_pred)?;
let y_pred_clipped = clip_probabilities(y_pred);
let grad = -y_true / &y_pred_clipped + (1.0 - y_true) / (1.0 - &y_pred_clipped);
let n = grad.len() as f32;
Ok(grad / n)
}
}