use hessboost::metric::{CustomMetric, EvalMetric};
use hessboost::objective::{CustomLoss, GradPair};
use hessboost::prelude::*;
use std::num::NonZeroUsize;
mod common;
use common::{fill_random, lcg};
fn main() -> Result<()> {
let (n, f) = (800usize, 3usize);
let mut rng = lcg(5);
let mut x = vec![0f32; n * f];
let mut y = vec![0f32; n];
fill_random(&mut rng, &mut x);
for i in 0..n {
y[i] = 1.5 * x[i * f] - x[i * f + 1];
}
let d = DMatrix::from_dense(&x, n, f)?.with_labels(&y)?;
let obj = CustomLoss::new("my:squarederror", 1, |preds, labels, w, out| {
for i in 0..preds.len() {
let wi = w.map_or(1.0, |ws| ws[i]);
out[i] = GradPair::new((preds[i] - labels[i]) * wi, wi); }
});
let params = TrainingParams::builder()
.objective(Objective::custom(obj))
.max_depth(3)
.eta(0.2)
.build()?;
let model = Trainer::new(¶ms, &d, 60).train()?.model;
let preds = model.predict(&d, Iterations::Best)?;
let rmse = EvalMetric::Rmse.build(1)?.eval(preds.as_slice(), &y, None);
println!("custom-objective RMSE: {rmse:.4}");
let mae = CustomMetric::new("my:mae", false, |p, l, _w| {
p.iter()
.zip(l)
.map(|(a, b)| (f64::from(*a) - f64::from(*b)).abs())
.sum::<f64>()
/ p.len() as f64
});
let builtin = TrainingParams::builder()
.objective(Objective::SquaredError(RegLoss::default()))
.max_depth(3)
.eta(0.2)
.build()?;
let out = Trainer::new(&builtin, &d, 100)
.eval(&d, "train")
.early_stopping_rounds(NonZeroUsize::new(10).unwrap())
.custom_metric(Box::new(mae))
.train()?;
println!(
"custom-metric run: {} trees, last MAE = {:.4}",
out.model.num_trees(),
out.history.last().unwrap().values().last().unwrap()
);
Ok(())
}