1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use crate::{loss::LossFunction, Layer, NeuralNetwork};
impl<'a> NeuralNetwork<'a> {
/// Creates a new instance of the `NeuralNetwork`.
/// This method initializes the network with the provided layers and performs basic validation.
///
/// # Arguments
/// * `layers` - A mutable slice of `Layer` instances that defines the structure of the neural network.
///
/// # Returns
/// A new `NeuralNetwork` instance.
///
/// # Panics
/// This method will panic if there are no layers provided (i.e., if the slice is empty).
///
/// # Example
/// ```
/// let layers = vec![
/// Layer::new(3, 5, activation::relu),
/// Layer::new(5, 2, activation::softmax),
/// ];
/// let mut nn = NeuralNetwork::new(&mut layers);
/// assert_eq!(nn.input_size, 3);
/// assert_eq!(nn.output_size, 2);
/// ```
pub fn new(layers: &'a mut [Layer]) -> Self {
let n: usize = layers.len();
assert!(n > 0, "NeuralNetwork must have at least one layer.");
NeuralNetwork {
input_size: layers[0].input_size,
output_size: layers[n - 1].output_size,
layers,
}
}
/// Performs a forward pass through the entire neural network.
/// The method iteratively passes the input through each layer of the network,
/// returning the output of the last layer.
///
/// # Arguments
/// * `input` - A slice of `f32` representing the input data to the network.
///
/// # Returns
/// A `Vec<f32>` representing the final output of the network after passing through all layers.
///
/// # Panics
/// This method will panic if the input size does not match the expected input size of the network.
///
/// # Example
/// ```
/// let layers = vec![
/// Layer::new(3, 5, activation::relu),
/// Layer::new(5, 2, activation::softmax),
/// ];
/// let mut nn = NeuralNetwork::new(&mut layers);
/// let input = vec![0.5, -0.2, 1.1];
/// let output = nn.forward(&input);
/// assert_eq!(output.len(), 2); // Output size matches the final layer's output size
/// ```
pub fn forward(&self, input: &[f32]) -> Vec<f32> {
assert_eq!(input.len(), self.input_size, "Input size mismatch.");
let n = self.layers.len();
let mut buffer1 = input.to_vec();
let mut buffer2 = vec![0.0; self.output_size];
let mut input_buffer = &mut buffer1;
let mut output_buffer = &mut buffer2;
for (i, layer) in self.layers.iter().enumerate() {
if i < n - 1 {
output_buffer.resize(layer.output_size, 0.0);
}
*output_buffer = layer.forward(input_buffer);
std::mem::swap(&mut input_buffer, &mut output_buffer);
}
input_buffer.clone()
}
/// Trains the neural network using the provided training data, target values, and loss function.
/// The method performs backpropagation to update the weights and biases of each layer,
/// minimizing the loss function over multiple epochs.
///
/// # Arguments
/// * `inputs` - A slice of `Vec<f32>` representing the input data for training.
/// * `targets` - A slice of `Vec<f32>` representing the target labels corresponding to the inputs.
/// * `learning_rate` - A `f32` value representing the learning rate used for gradient descent.
/// * `loss_function` - A `LossFunction` instance that computes the loss and its gradient.
/// * `epochs` - The number of training epochs (iterations over the entire dataset).
///
/// # Example
/// ```
/// let layers = vec![
/// Layer::new(3, 5, activation::relu),
/// Layer::new(5, 2, activation::softmax),
/// ];
/// let mut nn = NeuralNetwork::new(&mut layers);
///
/// let inputs = vec![
/// vec![0.5, -0.2, 1.1],
/// vec![0.9, 0.4, -0.1],
/// ];
/// let targets = vec![
/// vec![1.0, 0.0],
/// vec![0.0, 1.0],
/// ];
///
/// nn.train(&inputs, &targets, 0.01, loss::MSE, 1000);
/// ```
pub fn train(
&mut self,
inputs: &[Vec<f32>],
targets: &[Vec<f32>],
learning_rate: f32,
loss_function: LossFunction,
epochs: usize,
) {
for epoch in 0..epochs {
let mut total_loss = 0.0;
for (input, target) in inputs.iter().zip(targets.iter()) {
let mut activations: Vec<Vec<f32>> = Vec::new();
let mut temp_input = input.clone();
activations.push(temp_input.clone());
for layer in &mut *self.layers {
temp_input = layer.forward(&temp_input);
activations.push(temp_input.clone());
}
total_loss += (loss_function.function)(activations.last().unwrap(), target);
let mut grad = (loss_function.derivative)(activations.last().unwrap(), target);
for i in (0..self.layers.len()).rev() {
grad = self.layers[i].backward(&activations[i], &grad, learning_rate);
}
}
let average_loss = total_loss / inputs.len() as f32;
println!("Epoch {}: Loss = {}", epoch + 1, average_loss);
}
}
}