Skip to main content

cross_validate

Function cross_validate 

Source
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>
where S: CvSplitter, F: Fn(&Array2<f64>, &Array1<f64>) -> M + Sync, M: Fn(&Array2<f64>) -> Array1<f64>,
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);