use crate::{
Sample,
Classifier,
};
use crate::common::checker;
pub trait ObjectiveFunction<H> {
fn name(&self) -> &str;
fn eval(&self, sample: &Sample, hypothesis: &H) -> f64;
}
pub struct SoftMarginObjective(f64);
impl SoftMarginObjective {
pub fn new(nu: f64) -> Self {
Self(nu)
}
}
impl<H> ObjectiveFunction<H> for SoftMarginObjective
where H: Classifier,
{
fn name(&self) -> &str {
"Soft-margin objective"
}
fn eval(
&self,
sample: &Sample,
hypothesis: &H,
) -> f64
{
checker::check_sample(sample);
let n_sample = sample.shape().0;
checker::check_nu(self.0, n_sample);
let target = sample.target();
let mut margins = hypothesis.confidence_all(sample)
.into_iter()
.zip(target.iter())
.map(|(hx, y)| y * hx)
.collect::<Vec<f64>>();
margins.sort_by(|a, b| a.partial_cmp(b).unwrap());
let unit_weight = 1.0 / self.0;
let mut weight_left = 1.0;
let mut objective_value = 0.0;
for yh in margins {
if weight_left > unit_weight {
objective_value += unit_weight * yh;
weight_left -= unit_weight;
} else {
objective_value += weight_left * yh;
break;
}
}
objective_value
}
}
pub struct HardMarginObjective(SoftMarginObjective);
impl HardMarginObjective {
pub fn new() -> Self {
let soft_margin = SoftMarginObjective::new(1.0);
Self(soft_margin)
}
}
impl Default for HardMarginObjective {
fn default() -> Self {
Self::new()
}
}
impl<H> ObjectiveFunction<H> for HardMarginObjective
where H: Classifier,
{
fn name(&self) -> &str {
"Hard-margin objective"
}
fn eval(
&self,
sample: &Sample,
hypothesis: &H,
) -> f64
{
self.0.eval(sample, hypothesis)
}
}
pub struct ExponentialLoss;
impl ExponentialLoss {
pub fn new() -> Self {
Self {}
}
}
impl<H> ObjectiveFunction<H> for ExponentialLoss
where H: Classifier
{
fn name(&self) -> &str {
"Exponential Loss"
}
fn eval(
&self,
sample: &Sample,
hypothesis: &H,
) -> f64
{
checker::check_sample(sample);
let n_sample = sample.shape().0 as f64;
let target = sample.target();
hypothesis.predict_all(sample)
.into_iter()
.zip(target.iter())
.map(|(hx, y)| (- y * hx as f64).exp())
.sum::<f64>()
/ n_sample
}
}