libdt/trainer/
gd_trainer.rs

1use nalgebra::DVector;
2use nalgebra::RowDVector;
3use nalgebra::Matrix;
4use nalgebra::base::dimension as dim;
5
6use super::super::network::Network;
7use super::Trainer;
8
9use super::common::cost;
10use super::common::apply_step;
11use super::common::choose_step;
12
13/// Simple gradient descent trainer.
14pub struct GDTrainer<N: Network>
15{
16    p: Vec<f64>,
17    x_values: Vec<DVector<f64>>,
18    d_values: Vec<DVector<f64>>,
19    nn: N,
20}
21
22fn net_eval<N: Network>(p: &[f64], x_values: &[DVector<f64>])
23    -> Vec<DVector<f64>>
24{
25    let mut y_values: Vec<DVector<f64>> = Vec::new();
26    for x in x_values.into_iter() {
27        assert_eq!(x.len(), N::NEURONS_IN);
28        y_values.push(N::eval(&p, x.clone()));
29    }
30
31    y_values
32}
33
34impl<N: Network> Trainer<N> for GDTrainer<N> {
35    fn new(nn: N, p: Vec<f64>,
36           x_values: Vec<DVector<f64>>,
37           d_values: Vec<DVector<f64>>) -> Self
38    {
39        assert_eq!(p.len(), N::PARAMS_CNT);
40        assert_eq!(x_values.len(), d_values.len());
41
42        GDTrainer {
43            p,
44            x_values,
45            d_values,
46            nn,
47        }
48    }
49
50    fn make_step(&mut self) {
51        let direction = -(self.grad()).clone();
52
53        let step = choose_step::<N>(
54            &mut self.p, &self.x_values,
55            &self.d_values, direction);
56        apply_step(&mut self.p, &step);
57    }
58
59    fn cost(&self) -> f64 {
60        let y_values =
61            net_eval::<N>(self.p.as_slice(),
62                          self.x_values.as_slice());
63        let y_values = y_values.as_slice();
64
65        cost(y_values, self.d_values.as_slice())
66    }
67
68    fn grad(&mut self) -> RowDVector<f64> {
69            let mut grad_sum: RowDVector<f64> =
70                Matrix::from_element_generic(
71                    dim::U1, dim::Dyn(N::PARAMS_CNT), 0f64);
72    
73            for i in 0..self.x_values.len() {
74                let x = &self.x_values[i];
75                let d = &self.d_values[i];
76    
77                let y = self.nn.forward(&self.p, x.clone());
78                self.nn.backward(&self.p);
79                let jm = self.nn.jacobian(x);
80                let g = 2f64 * (y - d).transpose() * jm;
81    
82                grad_sum += g;
83            }
84    
85            grad_sum
86    }
87
88    fn grad_norm(&mut self) -> f64 {
89        self.grad().norm()
90    }
91
92    fn params(&self) -> &[f64] {
93        &self.p
94    }
95}