rill_ml/evaluate/
progressive.rs1use crate::error::{RillError, ensure_finite, ensure_finite_target};
4use crate::traits::{Metric, OnlineBinaryClassifier, OnlineRegressor};
5
6#[derive(Debug, Clone)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct RegressionSample {
10 pub features: Vec<f64>,
12 pub target: f64,
14}
15
16#[derive(Debug, Clone)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct BinaryClassificationSample {
20 pub features: Vec<f64>,
22 pub target: bool,
24}
25
26#[derive(Debug, Clone)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct ProgressiveStep {
31 pub index: usize,
33 pub metric_value: Option<f64>,
35}
36
37pub fn evaluate_regression<M, Met>(
43 model: &mut M,
44 metric: &mut Met,
45 samples: impl IntoIterator<Item = RegressionSample>,
46) -> Result<Option<f64>, RillError>
47where
48 M: OnlineRegressor,
49 Met: Metric<Truth = f64, Prediction = f64>,
50{
51 evaluate_regression_with_steps(model, metric, samples).map(|(v, _)| v)
52}
53
54pub fn evaluate_regression_with_steps<M, Met>(
56 model: &mut M,
57 metric: &mut Met,
58 samples: impl IntoIterator<Item = RegressionSample>,
59) -> Result<(Option<f64>, Vec<ProgressiveStep>), RillError>
60where
61 M: OnlineRegressor,
62 Met: Metric<Truth = f64, Prediction = f64>,
63{
64 let mut steps = Vec::new();
65 for (i, sample) in samples.into_iter().enumerate() {
66 ensure_finite_target(sample.target)?;
68 let prediction = model.predict(&sample.features)?;
70 ensure_finite("prediction", prediction)?;
72 metric.update(sample.target, prediction)?;
74 model.learn(&sample.features, sample.target)?;
76 steps.push(ProgressiveStep {
77 index: i,
78 metric_value: metric.value(),
79 });
80 }
81 Ok((metric.value(), steps))
82}
83
84pub fn evaluate_binary_classification<M, Met>(
86 model: &mut M,
87 metric: &mut Met,
88 samples: impl IntoIterator<Item = BinaryClassificationSample>,
89) -> Result<Option<f64>, RillError>
90where
91 M: OnlineBinaryClassifier,
92 Met: Metric<Truth = bool, Prediction = bool>,
93{
94 for sample in samples.into_iter() {
95 let prediction = model.predict(&sample.features)?;
97 metric.update(sample.target, prediction)?;
98 model.learn(&sample.features, sample.target)?;
99 }
100 Ok(metric.value())
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::metrics::Mae;
107 use crate::models::MeanRegressor;
108
109 #[test]
110 fn progressive_evaluates_before_learning() {
111 let mut model = MeanRegressor::default();
114 let mut mae = Mae::default();
115 let samples = vec![
116 RegressionSample {
117 features: vec![],
118 target: 10.0,
119 },
120 RegressionSample {
121 features: vec![],
122 target: 20.0,
123 },
124 RegressionSample {
125 features: vec![],
126 target: 30.0,
127 },
128 ];
129 let (final_mae, steps) =
130 evaluate_regression_with_steps(&mut model, &mut mae, samples).unwrap();
131
132 assert!((final_mae.unwrap() - 35.0 / 3.0).abs() < 1e-9);
137 assert_eq!(steps.len(), 3);
138 }
139}