Review and Feedback of First Rust Code for a Basic Neural Network


#1

This is a rust port of the code from here.

extern crate rand;
use rand::Rng;

fn main() {

    let mut rng = rand::thread_rng();
    let mut syn_weight: Vec<f64> = Vec::new();
    for x in 0..3 {
        syn_weight.push(rng.gen_range::<f64>(-1.0, 1.0));
    }

    let input_set: Vec<Vec<f64>> = vec![vec![0.0, 0.0, 1.0],
                                        vec![1.0, 1.0, 1.0],
                                        vec![1.0, 0.0, 1.0],
                                        vec![0.0, 1.0, 1.0]];
    let output_set: Vec<Vec<f64>> = vec![vec![0.0], vec![1.0], vec![1.0], vec![0.0]];

    for x in 0..10000000 {
        vec_dot(&input_set, &syn_weight);
        let output = vec_sigmoid(vec_dot(&input_set, &syn_weight));
        let mut error: Vec<f64> = Vec::new();
        for (idx, val) in output_set.iter().enumerate() {
            error.push(val[0] - output[idx]);
        }
        let derivative = vec_sigmoid_derivative(output);
        let mut result: Vec<f64> = Vec::new();
        for (idx, val) in derivative.iter().enumerate() {
            result.push(val * error[idx]);
        }
        syn_weight = vec_add(&(vec_dot(&(vec_transpose(&input_set)), &result)),
                            &syn_weight);
    }
    let mut test_input: Vec<Vec<f64>> = vec![vec![1.0, 0.0, 0.0]];
    println!("Output");
    println!("{:?}", vec_sigmoid(vec_dot(&test_input, &syn_weight)));
}

fn vec_transpose(input: &Vec<Vec<f64>>) -> Vec<Vec<f64>> {
    let mut outer: Vec<Vec<f64>> = Vec::new();
    for z in 0..input[0].len() {
        let mut inner: Vec<f64> = Vec::new();
        for y in 0..input.len() {
            inner.push(input[y][z]);
        }
        outer.push(inner);
    }
    outer
}

fn vec_add(x: &Vec<f64>, y: &Vec<f64>) -> Vec<f64> {
    let mut sum: Vec<f64> = Vec::new();
    for (idx, val) in x.iter().enumerate() {
        sum.push(val + y[idx]);
    }
    sum
}

fn sigmoid(x: &f64) -> f64 {
    1.0 / (1.0 + ((-1.0 * x).exp()))
}

fn vec_sigmoid(x: Vec<f64>) -> Vec<f64> {
    let mut result: Vec<f64> = Vec::new();
    for val in x.iter() {
        result.push(sigmoid(val));
    }
    result
}

fn sigmoid_derivative(x: &f64) -> f64 {
    x * (1.0 - x)
}

fn vec_sigmoid_derivative(x: Vec<f64>) -> Vec<f64> {
    let mut result: Vec<f64> = Vec::new();
    for val in x.iter() {
        result.push(sigmoid_derivative(val));
    }
    result
}

fn vec_dot(x: &Vec<Vec<f64>>, y: &Vec<f64>) -> Vec<f64> {
    let mut result: Vec<f64> = Vec::new();
    for val in x.iter() {
        let mut sum: f64 = 0.0;
        for (idx_inner, value) in y.iter().enumerate() {
            sum += val[idx_inner] * value;
        }
        result.push(sum);
    }
    result
}

Gist Url: gist.github.com/be8b9123da9b0f0246ca83960fc159e8
PlaygroundUrl : play.rust-lang.org/?gist=be8b9123da9b0f0246ca83960fc159e8&version=stable&backtrace=0

Thanks in advance.
Regards
Delta


#2

If you tried ndarray, just ignore me.


#3

@Congee

Thanks for the review, its my first time using Rust, wanted to try stuff without the ndarray library, appreciate you pointing it out.


#4

Edited code based on discussion on reddit

    extern crate rand
    use rand::Rng;
    fn main() {

    let mut rng = rand::thread_rng();
    let mut syn_weight: Vec<f64> = Vec::new();
    for _ in 0..3 {
        syn_weight.push(rng.gen_range::<f64>(-1.0, 1.0));
    }

    let input_set: Vec<Vec<f64>> = vec![vec![0.0, 0.0, 1.0],
                                        vec![1.0, 1.0, 1.0],
                                        vec![1.0, 0.0, 1.0],
                                        vec![0.0, 1.0, 1.0]];
    let output_set: Vec<Vec<f64>> = vec![vec![0.0], vec![1.0], vec![1.0], vec![0.0]];

    for _ in 0..10000000 {
        vec_dot(&input_set, &syn_weight);
        let output = vec_sigmoid(&vec_dot(&input_set, &syn_weight));
        let mut error: Vec<f64> = Vec::new();
        for (idx, val) in output_set.iter().enumerate() {
            error.push(val[0] - output[idx]);
        }
        let derivative = vec_sigmoid_derivative(&output);
        let mut result: Vec<f64> = Vec::new();
        for (idx, val) in derivative.iter().enumerate() {
            result.push(val * error[idx]);
        }
        syn_weight = vec_add(&(vec_dot(&(vec_transpose(&input_set)), &result)),
                            &syn_weight);
    }
    let test_input: Vec<Vec<f64>> = vec![vec![1.0, 0.0, 0.0]];
    println!("Output");
    println!("{:?}", vec_sigmoid(&vec_dot(&test_input, &syn_weight)));
}

fn vec_transpose(input: &Vec<Vec<f64>>) -> Vec<Vec<f64>> {
    let mut outer: Vec<Vec<f64>> = Vec::new();
    for z in 0..input[0].len() {
        let mut inner: Vec<f64> = Vec::new();
        for y in 0..input.len() {
            inner.push(input[y][z]);
        }
        outer.push(inner);
    }
    outer
}

fn vec_add(x: &Vec<f64>, y: &Vec<f64>) -> Vec<f64> {
    let mut sum: Vec<f64> = Vec::new();
    for (idx, val) in x.iter().enumerate() {
        sum.push(val + y[idx]);
    }
    sum
}

fn sigmoid(x: &f64) -> f64 {
    1.0 / (1.0 + ((-1.0 * x).exp()))
}

fn vec_sigmoid(x: &Vec<f64>) -> Vec<f64> {
    x.into_iter().map(sigmoid).collect::<Vec<f64>>()
}

fn sigmoid_derivative(x: &f64) -> f64 {
    x * (1.0 - x)
}

fn vec_sigmoid_derivative(x: &Vec<f64>) -> Vec<f64> {
    x.into_iter().map(sigmoid_derivative).collect::<Vec<f64>>()
}

fn vec_dot(x: &Vec<Vec<f64>>, y: &Vec<f64>) -> Vec<f64> {
    let mut result: Vec<f64> = Vec::new();
    for val in x.iter() {
        let mut sum: f64 = 0.0;
        for (idx_inner, value) in y.iter().enumerate() {
            sum += val[idx_inner] * value;
        }
        result.push(sum);
    }
    result
}