Advertisement
443eb9

Untitled

Feb 5th, 2024
340
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 7.31 KB | None | 0 0
  1. pub fn activate(x: f64) -> f64 {
  2.     1.0 / (1.0 + (-x).exp())
  3. }
  4.  
  5. pub fn deriv_activate(x: f64) -> f64 {
  6.     let a = activate(x);
  7.     a * (1.0 - a)
  8. }
  9.  
  10. pub struct NeuralNetwork {
  11.     layer_count: usize,
  12.     layers: Vec<Layer>,
  13.     layer_data: Vec<LayerData>,
  14. }
  15.  
  16. impl NeuralNetwork {
  17.     pub fn new(
  18.         input: usize,
  19.         output: usize,
  20.         hidden_layer_sizes: &[usize],
  21.         rng: &mut impl Rng,
  22.     ) -> Self {
  23.         let mut layers = Vec::with_capacity(hidden_layer_sizes.len());
  24.         let mut layer_data = Vec::with_capacity(hidden_layer_sizes.len());
  25.         let mut prev_size = input;
  26.  
  27.         let mut i = 1;
  28.  
  29.         for &size in hidden_layer_sizes {
  30.             layers.push(Layer::new(prev_size, size, rng, i));
  31.             layer_data.push(LayerData::new(prev_size, size, i));
  32.             prev_size = size;
  33.             i += 1;
  34.         }
  35.  
  36.         layers.push(Layer::new(prev_size, output, rng, i));
  37.         layer_data.push(LayerData::new(prev_size, output, i));
  38.  
  39.         NeuralNetwork {
  40.             layer_count: layers.len(),
  41.             layers,
  42.             layer_data,
  43.         }
  44.     }
  45. }
  46.  
  47. pub struct Layer {
  48.     index: usize,
  49.     inputs: usize,
  50.     outputs: usize,
  51.     weights: Vec<f64>,
  52.     biases: Vec<f64>,
  53. }
  54.  
  55. impl Layer {
  56.     pub fn new(inputs: usize, outputs: usize, rng: &mut impl Rng, index: usize) -> Self {
  57.         let distr = Uniform::new(0., 1.);
  58.         Layer {
  59.             index,
  60.             inputs,
  61.             outputs,
  62.             weights: (0..inputs * outputs)
  63.                 .into_iter()
  64.                 .map(|_| rng.sample(distr))
  65.                 .collect(),
  66.             biases: vec![0.0; outputs],
  67.         }
  68.     }
  69.  
  70.     pub fn think(&self, inputs: &[f64], output: &mut LayerData) {
  71.         output
  72.             .weighted_inputs
  73.             .par_iter_mut()
  74.             .zip(output.activations.par_iter_mut())
  75.             .enumerate()
  76.             .for_each(|(output_index, (weighted_input, activation))| {
  77.                 *weighted_input = self.biases[output_index];
  78.                 self.weight_slice(output_index).iter().for_each(|weight| {
  79.                     *weighted_input += weight * inputs[output_index];
  80.                 });
  81.                 *activation = activate(*weighted_input);
  82.             });
  83.     }
  84.  
  85.     pub fn weight(&self, input: usize, output: usize) -> f64 {
  86.         self.weights[output * self.inputs + input]
  87.     }
  88.  
  89.     pub fn weight_slice(&self, output: usize) -> &[f64] {
  90.         &self.weights[output * self.inputs..(output + 1) * self.inputs]
  91.     }
  92. }
  93.  
  94. pub struct LayerData {
  95.     index: usize,
  96.     /// z_n = a_{n-1} * w_n + b_n
  97.     weighted_inputs: Vec<f64>,
  98.     /// a_n = Activation(z_n)
  99.     activations: Vec<f64>,
  100.     /// ∂c/∂a_n * ∂a_n/∂z_n
  101.     backprop: Vec<f64>,
  102.     grad_w: Vec<f64>,
  103.     grad_b: Vec<f64>,
  104. }
  105.  
  106. impl LayerData {
  107.     pub fn new(inputs: usize, outputs: usize, index: usize) -> Self {
  108.         LayerData {
  109.             index,
  110.             weighted_inputs: vec![0.; outputs],
  111.             activations: vec![0.; outputs],
  112.             backprop: vec![0.; outputs],
  113.             grad_w: vec![0.; outputs * inputs],
  114.             grad_b: vec![0.; outputs],
  115.         }
  116.     }
  117.  
  118.     pub fn flatten_index(&self, input: usize, output: usize) -> usize {
  119.         output * self.activations.len() + input
  120.     }
  121. }
  122.  
  123. pub struct DataPoint {
  124.     pub inputs: Vec<f64>,
  125.     pub targets: Vec<f64>,
  126. }
  127.  
  128. fn deriv_cross_entropy(activations: f64, targets: f64) -> f64 {
  129.     (-activations + targets) / (activations * (1. - activations))
  130. }
  131.  
  132. impl NeuralNetwork {
  133.     pub fn learn(&mut self, data: &[DataPoint], batch_size: usize, learn_rate: f64) {
  134.         for batch in data.chunks(batch_size) {
  135.             self.learn_batch(batch, learn_rate);
  136.         }
  137.     }
  138.  
  139.     fn learn_batch(&mut self, batch: &[DataPoint], learn_rate: f64) {
  140.         batch.iter().for_each(|data| {
  141.             self.think(data);
  142.             self.calc_backprop_cache(data);
  143.             self.calc_grad(data);
  144.         });
  145.  
  146.         self.apply_and_clear_grad(learn_rate);
  147.     }
  148.  
  149.     fn think(&mut self, data: &DataPoint) {
  150.         self.layers[0].think(&data.inputs, &mut self.layer_data[0]);
  151.         for i in 1..self.layer_count {
  152.             let [prev, cur, ..] = &mut self.layer_data[i - 1..] else {
  153.                 unreachable!()
  154.             };
  155.             self.layers[i].think(&prev.activations, cur);
  156.         }
  157.     }
  158.  
  159.     fn calc_backprop_cache(&mut self, data: &DataPoint) {
  160.         self.layer_data[self.layer_count - 1].calc_backprop_cache_output(&data.targets);
  161.         for i in (0..self.layer_count - 1).rev() {
  162.             let [cur, next, ..] = &mut self.layer_data[i..] else {
  163.                 unreachable!()
  164.             };
  165.             cur.calc_backprop_cache_hidden(&self.layers[i + 1], &next);
  166.         }
  167.     }
  168.  
  169.     fn calc_grad(&mut self, data: &DataPoint) {
  170.         self.layer_data[0].calc_grad(&data.inputs);
  171.         for i in 1..self.layer_count {
  172.             let [prev, cur, ..] = &mut self.layer_data[i - 1..] else {
  173.                 unreachable!()
  174.             };
  175.             cur.calc_grad(&prev.activations);
  176.         }
  177.     }
  178.  
  179.     fn apply_and_clear_grad(&mut self, learn_rate: f64) {
  180.         self.layers
  181.             .par_iter_mut()
  182.             .zip(self.layer_data.par_iter_mut())
  183.             .for_each(|(layer, data)| {
  184.                 data.apply_and_clear_grad(layer, learn_rate);
  185.             });
  186.     }
  187. }
  188.  
  189. impl LayerData {
  190.     pub fn calc_backprop_cache_output(&mut self, targets: &[f64]) {
  191.         self.backprop
  192.             .par_iter_mut()
  193.             .enumerate()
  194.             .for_each(|(neuron_index, backprop)| {
  195.                 let cost_deriv =
  196.                     deriv_cross_entropy(self.activations[neuron_index], targets[neuron_index]);
  197.                 let acti_deriv = super::deriv_activate(self.weighted_inputs[neuron_index]);
  198.                 *backprop = cost_deriv * acti_deriv;
  199.             });
  200.     }
  201.  
  202.     pub fn calc_backprop_cache_hidden(&mut self, next: &Layer, next_data: &LayerData) {
  203.         self.backprop
  204.             .par_iter_mut()
  205.             .enumerate()
  206.             .for_each(|(neuron_index, backprop)| {
  207.                 *backprop = next_data
  208.                     .backprop
  209.                     .iter()
  210.                     .map(|next_backprop| *next_backprop * next.weights[neuron_index])
  211.                     .sum::<f64>()
  212.                     * super::deriv_activate(self.weighted_inputs[neuron_index]);
  213.             });
  214.     }
  215.  
  216.     pub fn calc_grad(&mut self, inputs: &[f64]) {
  217.         for output in 0..self.activations.len() {
  218.             for input in 0..inputs.len() {
  219.                 self.grad_w[output * inputs.len() + input] += inputs[input] * self.backprop[output];
  220.                 self.grad_b[output] += self.backprop[output];
  221.             }
  222.         }
  223.     }
  224.  
  225.     pub fn apply_and_clear_grad(&mut self, layer: &mut Layer, learn_rate: f64) {
  226.         self.grad_w
  227.             .par_iter_mut()
  228.             .zip(layer.weights.par_iter_mut())
  229.             .for_each(|(grad, weight)| {
  230.                 *weight -= learn_rate * *grad;
  231.                 *grad = 0.;
  232.             });
  233.  
  234.         self.grad_b
  235.             .par_iter_mut()
  236.             .zip(layer.biases.par_iter_mut())
  237.             .for_each(|(grad, bias)| {
  238.                 *bias -= learn_rate * *grad;
  239.                 *grad = 0.;
  240.             });
  241.     }
  242. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement