Created
October 5, 2023 05:48
-
-
Save 0xm00n/38dacd364fac4a656b003530ac5e197a to your computer and use it in GitHub Desktop.
Multi-layer perceptron with an input layer, 1 hidden layer, and an output layer written from scratch in rust. The sigmoid activation function is used for both the hidden and output layers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::f64; | |
// sigmoid activation function | |
fn sigmoid(x: f64) -> f64 { | |
1.0 / (1.0 + (-x).exp()) | |
} | |
// derivative of the sigmoid function | |
fn sigmoid_prime(x: f64) -> f64 { | |
sigmoid(x) * (1.0 - sigmoid(x)) | |
} | |
struct NeuralNetwork { | |
// weights and biases | |
input_hidden_weights: Vec<Vec<f64>>, | |
hidden_output_weights: Vec<f64>, | |
hidden_biases: Vec<f64>, | |
output_bias: f64, | |
} | |
impl NeuralNetwork { | |
// initialize the neural network with random weights and biases | |
fn new(input_size: usize, hidden_size: usize) -> Self { | |
let input_hidden_weights = vec![vec![0.5; input_size]; hidden_size]; | |
let hidden_output_weights = vec![0.5; hidden_size]; | |
let hidden_biases = vec![0.5; hidden_size]; | |
let output_bias = 0.5; | |
NeuralNetwork { | |
input_hidden_weights, | |
hidden_output_weights, | |
hidden_biases, | |
output_bias, | |
} | |
} | |
// forward pass | |
fn forward(&self, input: &Vec<f64>) -> (Vec<f64>, f64) { | |
let hidden_activations: Vec<f64> = self.input_hidden_weights.iter().zip(&self.hidden_biases) | |
.map(|(weights, bias)| { | |
let sum: f64 = weights.iter().zip(input).map(|(w, i)| w * i).sum(); | |
sigmoid(sum + bias) | |
}) | |
.collect(); | |
let output_sum: f64 = hidden_activations.iter().zip(&self.hidden_output_weights) | |
.map(|(h, w)| h * w).sum(); | |
let output = sigmoid(output_sum + self.output_bias); | |
(hidden_activations, output) | |
} | |
// training using backpropagation | |
fn train(&mut self, input: &Vec<f64>, target: f64, learning_rate: f64) { | |
let (hidden_activations, output) = self.forward(input); | |
// print the output of the neural network | |
println!("Output: {}", output); | |
// calculate the output error | |
let output_error = target - output; | |
println!("Error: {}", output_error); | |
// calculate the gradient for the output layer | |
let output_gradient = output_error * sigmoid_prime(output); | |
// update the weights and bias for the hidden-output layer | |
for (w, h) in self.hidden_output_weights.iter_mut().zip(&hidden_activations) { | |
*w += learning_rate * output_gradient * h; | |
} | |
self.output_bias += learning_rate * output_gradient; | |
// calculate the hidden layer errors | |
let hidden_errors: Vec<f64> = self.hidden_output_weights.iter() | |
.map(|w| output_gradient * w).collect(); | |
// update the weights and biases for the input-hidden layer | |
for ((weights, bias), error) in self.input_hidden_weights.iter_mut().zip(&mut self.hidden_biases).zip(&hidden_errors) { | |
let hidden_gradient = error * sigmoid_prime(hidden_activations[0]); | |
for (w, i) in weights.iter_mut().zip(input) { | |
*w += learning_rate * hidden_gradient * i; | |
} | |
*bias += learning_rate * hidden_gradient; | |
} | |
// print the updated weights and biases | |
println!("Updated input-hidden weights: {:?}", self.input_hidden_weights); | |
println!("Updated hidden-output weights: {:?}", self.hidden_output_weights); | |
println!("Updated hidden biases: {:?}", self.hidden_biases); | |
println!("Updated output bias: {}", self.output_bias); | |
} | |
} | |
fn main() { | |
// example usage | |
let mut nn = NeuralNetwork::new(2, 2); | |
// print initial weights and biases | |
println!("Initial input-hidden weights: {:?}", nn.input_hidden_weights); | |
println!("Initial hidden-output weights: {:?}", nn.hidden_output_weights); | |
println!("Initial hidden biases: {:?}", nn.hidden_biases); | |
println!("Initial output bias: {}", nn.output_bias); | |
let input = vec![0.5, 0.25]; | |
let target = 1.0; | |
// train the neural network | |
for _ in 0..10000 { | |
nn.train(&input, target, 0.8); | |
println!("-----------------------------------"); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment