Expand description
§model-selection-rs
Cross-validation and model-selection utilities for Rust, filling the specific
gaps in smartcore::model_selection: stratified, group-aware, and
time-aware splitting, nested cross-validation, and learning /
validation curve utilities — as a standalone, dependency-light crate rather
than hand-rolled notebook code.
The crate is deliberately about splitting strategies and evaluation-loop
utilities, not hyperparameter search itself. Grid/random search and
Bayesian optimizers (e.g. the tpe crate) remain their own job; this crate
composes with them — most directly through
nested_cross_validate, whose tuning step
is a closure you fill with whatever search you like.
§Layout
splitters— every splitter, all implementing the oneCvSplittertrait.scoring— theScorertrait, built-in metrics, and (behind thesmartcore-metricsfeature) adapters oversmartcore::metrics.evaluate—cross_validate,nested_cross_validate,learning_curve, andvalidation_curve.
§Feature flags
parallel— fan fold execution out overrayonin theevaluateutilities. Public signatures are unchanged; only wall-clock time differs, and results are numerically identical to the serial path.smartcore-metrics— enablescoring::smartcore_adapter, wrappingsmartcore::metrics(F1, ROC-AUC) asScorers.
§Quick start
use ndarray::{Array1, Array2};
use model_selection_rs::evaluate::{cross_validate, BoxedScorer};
use model_selection_rs::scoring::R2Score;
use model_selection_rs::splitters::KFold;
// Toy: perfectly linear data, a least-squares "model".
let x = Array2::from_shape_fn((20, 1), |(i, _)| i as f64);
let y = x.column(0).mapv(|v| 2.0 * v + 1.0);
let kf = KFold::new(5).unwrap();
let scorers: Vec<BoxedScorer> = vec![Box::new(R2Score)];
let res = cross_validate(&kf, &x, &y, |xt, yt| {
// fit y = a*x + b by ordinary least squares on one feature
let n = xt.nrows() as f64;
let (xs, ys) = (xt.column(0).to_owned(), yt.to_owned());
let mx = xs.sum() / n; let my = ys.sum() / n;
let cov = xs.iter().zip(ys.iter()).map(|(a,b)| (a-mx)*(b-my)).sum::<f64>();
let var = xs.iter().map(|a| (a-mx).powi(2)).sum::<f64>();
let slope = cov / var; let intercept = my - slope*mx;
move |xq: &Array2<f64>| xq.column(0).mapv(|v| slope*v + intercept)
}, &scorers, false).unwrap();
assert!((res.mean_test_score(0) - 1.0).abs() < 1e-9); // R2 ~ 1Re-exports§
pub use error::ModelSelectionError;pub use error::Result;
Modules§
- error
- Error type shared across every splitter and evaluation utility.
- evaluate
- Evaluation-loop utilities that compose a splitter, a model-fitting closure, and one or more scorers.
- scoring
- Scoring: a lightweight metric abstraction usable standalone or, optionally,
backed by
smartcore::metrics. - splitters
- Cross-validation splitters.