libdt/trainer/
gd_trainer.rs1use 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
13pub 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}