Skip to main content

incremental_rs/
monitoring.rs

1use crate::error::IncrementalError;
2use crate::IncrementalSupervisedEstimator;
3use ndarray::{Array1, Array2};
4
5/// Metrics recorded after each batch execution.
6#[derive(Debug, Clone, Copy)]
7pub struct BatchStats {
8    pub step: usize,
9    pub loss: f64,
10    pub learning_rate: Option<f64>,
11}
12
13/// A wrapper that attaches a monitoring callback to any supervised incremental model.
14pub struct MonitoredEstimator<'a, E: IncrementalSupervisedEstimator, F>
15where
16    F: FnMut(BatchStats),
17{
18    pub estimator: &'a mut E,
19    pub callback: F,
20    pub step_counter: usize,
21}
22
23impl<'a, E: IncrementalSupervisedEstimator, F> MonitoredEstimator<'a, E, F>
24where
25    F: FnMut(BatchStats),
26{
27    pub fn new(estimator: &'a mut E, callback: F) -> Self {
28        Self {
29            estimator,
30            callback,
31            step_counter: 0,
32        }
33    }
34
35    /// Calculates Mean Squared Error loss between true and predicted targets.
36    fn calculate_mse(y_true: &Array1<f64>, y_pred: &Array1<f64>) -> f64 {
37        if y_true.is_empty() {
38            return 0.0;
39        }
40        y_true
41            .iter()
42            .zip(y_pred.iter())
43            .map(|(t, p)| (t - p).powi(2))
44            .sum::<f64>()
45            / (y_true.len() as f64)
46    }
47}
48
49impl<'a, E: IncrementalSupervisedEstimator, F> IncrementalSupervisedEstimator
50    for MonitoredEstimator<'a, E, F>
51where
52    F: FnMut(BatchStats),
53{
54    fn partial_fit(
55        &mut self,
56        batch_x: &Array2<f64>,
57        batch_y: &Array1<f64>,
58    ) -> Result<(), IncrementalError> {
59        // 1. Predict current batch before parameter update to evaluate pre-fit step loss
60        let predictions = self.estimator.predict(batch_x).unwrap_or_default();
61        let loss = Self::calculate_mse(batch_y, &predictions);
62
63        // 2. Perform parameter update step
64        self.estimator.partial_fit(batch_x, batch_y)?;
65
66        // 3. Emit step metrics via callback
67        (self.callback)(BatchStats {
68            step: self.step_counter,
69            loss,
70            learning_rate: None,
71        });
72
73        self.step_counter += 1;
74        Ok(())
75    }
76
77    fn predict(&self, x: &Array2<f64>) -> Result<Array1<f64>, IncrementalError> {
78        self.estimator.predict(x)
79    }
80}