pub fn cross_validate<S, F, M>(
splitter: &S,
x: &Array2<f64>,
y: &Array1<f64>,
fit_fn: F,
scorers: &[BoxedScorer],
return_train_scores: bool,
) -> Result<CvResults>Expand description
Cross-validate a model-fitting closure with one or more scorers.
Ties a CvSplitter, a model-fitting closure, and one or more
Scorers together — the utility you actually call day to day. fit_fn is
generic (it takes (x_train, y_train) and returns a prediction closure),
so this works with smartcore, linfa, or a hand-rolled model without this
crate depending on any of them.
Every scorer is evaluated in the same pass over the folds (re-fitting per metric would be wasteful), so asking for accuracy and F1 together costs one set of fits, not two.
With the parallel feature enabled the folds are fit and scored across a
rayon thread pool. The results are numerically identical to the serial path
— parallelism changes only wall-clock time, and the per-fold order is
preserved.
§Errors
Propagates any error from splitter.split(x.nrows()).
§Example
use ndarray::{array, Array1, Array2};
use model_selection_rs::evaluate::{cross_validate, BoxedScorer};
use model_selection_rs::scoring::MeanSquaredError;
use model_selection_rs::splitters::KFold;
// A trivial "model" that predicts the training mean.
let x: Array2<f64> = Array2::zeros((10, 1));
let y: Array1<f64> = array![1., 2., 3., 4., 5., 6., 7., 8., 9., 10.];
let kf = KFold::new(5).unwrap();
let scorers: Vec<BoxedScorer> = vec![Box::new(MeanSquaredError)];
let res = cross_validate(&kf, &x, &y, |_xt, yt| {
let mean = yt.sum() / yt.len() as f64;
move |xq: &Array2<f64>| Array1::from_elem(xq.nrows(), mean)
}, &scorers, false).unwrap();
assert_eq!(res.n_splits(), 5);