Rustオンリーで簡素なニューラルネットワークを構成

表題の通り、Rustオンリーで簡素なニューラルネットワークを構築してみました
下記のソースコードではXORを学習するようになっています。

連鎖律を用いた誤差逆伝播などは行っておらず、損失関数より算出された誤差を元に
weightをちまちま更新していく作りになっています(なので遅いです…)

#ソースコード
外部ライブラリ等は必要なく、ベタ貼り後rustcなりcargoなりで
動きます

```rust

use std::ops::Fn;

// general
fn relu(x : &Vec<f64>) -> Vec<f64>{
    let mut ret = x.to_vec();
    for i in &mut ret{
        *i = if 0.0 < *i {*i} else {0.0};
    }
    ret
}

fn num_diff<F>(f:&F,w:&mut Vec<Vec<Vec<f64>>>,tgt_p1:usize,tgt_p2:usize) -> Vec<f64> where F : (Fn(&Vec<Vec<Vec<f64>>>) -> f64){
    let h = 1e-04;
    let mut grad = vec![0.0_f64;w[tgt_p1][tgt_p2].len()];
    
    for i in 0..w[tgt_p1][tgt_p2].len(){
        let tmp = w[tgt_p1][tgt_p2][i];

        w[tgt_p1][tgt_p2][i] = tmp + h;
        let fw = f(&w);

        w[tgt_p1][tgt_p2][i] = tmp - h;
        let bk = f(&w);

        grad[i] = (fw - bk) / (2.0 * h);
        w[tgt_p1][tgt_p2][i] = tmp;
    }

    grad
}

fn mean_squared_error(y:&Vec<f64>,t:&Vec<f64>) -> f64{
    if y.len() != t.len(){
        panic!("unmatch dim. y:{},t:{}",y.len(),t.len());
    }
    
    let mut ret= 0.0_f64;
    for i in 0..y.len(){
        ret += (y[i] - t[i]).powf(2.0);
    }

    let ret_val = ret / 2.0;

    return ret_val;
}

fn dot(x:&Vec<f64>,w:&Vec<Vec<f64>>) -> Vec<f64>{
    if w.len() <= 0 || x.len() != w.len(){
        panic!("unmatch dim. x:{},y:{}",x.len(),w.len());
    }

    let mut ret = vec![0.0_f64;w[0].len()];

    for i in 0..w[0].len(){
        for j in 0..x.len(){
            ret[i] += x[j] * w[j][i];
        }
    }

    ret
}

fn predict(x:&Vec<f64>,w:&Vec<Vec<Vec<f64>>>) -> Vec<f64>{
    let x1 = dot(x,&w[0]);
    let z1 = relu(&x1);
    let x2 = dot(&z1,&w[1]);
    let z2 = relu(&x2);
    let x3 = dot(&z2,&w[2]);

    x3
}

fn backward(x:&Vec<f64>,t:&Vec<f64>,w:&mut Vec<Vec<Vec<f64>>>) -> Vec<Vec<Vec<f64>>>{
    let lt = 0.05;
    let f = |wh:&Vec<Vec<Vec<f64>>>| -> f64{
        let pr = predict(x,wh);
        let ret = mean_squared_error(&pr,t);
        return ret;
    };

    let mut diff_ret : Vec<Vec<Vec<f64>>> = Vec::new();

    for i in 0..w.len(){
        diff_ret.push(vec![Vec::new();w[i].len()]);
        for j in 0..w[i].len(){
            diff_ret[i][j] = num_diff(&f,w,i,j);
            for k in 0..diff_ret[i][j].len(){
                w[i][j][k] -= lt * diff_ret[i][j][k];
            }
        }
    }
    

    diff_ret
}

fn forward(x:&Vec<f64>,w:&Vec<Vec<Vec<f64>>>) -> Vec<f64>{
    predict(x,w)
}

fn training(input : &Vec<Vec<f64>>,answer : &Vec<Vec<f64>>,weight : &mut Vec<Vec<Vec<f64>>>,epoch :usize,view_status :usize){
    let epoch_val = epoch + 1;
    let view_status_val = if view_status <= 0 {1} else {view_status};
    for i in 1..epoch_val{
        for dt in 0..input.len(){
            backward(&input[dt],&answer[dt],weight);
        }
        if i % view_status_val == 0{
            println!("epoch:{},end",i);
        }
    }
}

fn test(input : &Vec<Vec<f64>>,weight : &Vec<Vec<Vec<f64>>>){
    for inp in input{
        let ret = forward(&inp,&weight);
        for i in ret{
            println!("ret[{},{}]:{}",inp[0],inp[1],i);
        }
    }
}

fn main(){
    let input = vec![
        vec![0.0_f64,0.0_f64],
        vec![1.0_f64,0.0_f64],
        vec![0.0_f64,1.0_f64],
        vec![1.0_f64,1.0_f64]
    ];
    let mut weight = vec![
        vec![vec![0.1_f64,0.10_f64],vec![0.1_f64,0.1_f64]],
        vec![vec![0.1_f64,0.14_f64],vec![0.1_f64,0.1_f64]],
        vec![vec![0.1_f64;1],vec![0.1_f64;1]]];
    let res = vec![
        vec![0.0_f64],
        vec![1.0_f64],
        vec![1.0_f64],
        vec![0.0_f64]
    ];

    training(&input,&res,&mut weight,30000,100);
    test(&input,&weight);
}

```

reluで伝播させた後、恒等関数で結果を出力し
平均2乗誤差にて誤差を算出しています。


結果は以下のようになります

```
epoch:100,end
epoch:200,end
epoch:300,end
…(省略)
epoch:29800,end
epoch:29900,end
epoch:30000,end
ret[0,0]:0
ret[1,0]:0.9999999999911916
ret[0,1]:1.0000000000002078
ret[1,1]:0

```

各値に対するXORが算出されてますね！
正しく学習できたようです！

# おわりに

上記ソースのライセンスは
よく分からないのですが一先ずMITとします

また、下記githubにもソースコードをアップしました
https://github.com/kakisaouns/Rust_Easy_NeuralNet

誤り、指摘などありましたらコメント頂けると嬉しいです。