nncombinator 0.9.8

A library of neural networks that can be written type-safely
Documentation
use std::cell::RefCell;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::DerefMut;
use std::path::Path;
use std::rc::Rc;
use rand::{prelude, Rng, SeedableRng};
use rand::prelude::{Distribution, SliceRandom};
use rand_distr::Normal;
use rand_xorshift::XorShiftRng;
use nncombinator::activation::{ReLu, Sigmoid};
use nncombinator::arr::Arr;
use nncombinator::device::{DeviceGpu};
use nncombinator::layer::activation::ActivationLayer;
use nncombinator::layer::{AddLayer, BatchTrain, Train};
use nncombinator::layer::input::InputLayer;
use nncombinator::layer::linear::LinearLayerBuilder;
use nncombinator::layer::logging::{LoggingLayer};
use nncombinator::layer::output::LinearOutputLayer;
use nncombinator::lossfunction::CrossEntropy;
use nncombinator::optimizer::MomentumSGDBuilder;
use crate::common::SHARED_MEMORY_POOL;

#[test]
fn test_logger() {
    let (sender,receiver) = std::sync::mpsc::channel();

    let mut rnd = prelude::thread_rng();
    let rnd_base = Rc::new(RefCell::new(XorShiftRng::from_seed(rnd.gen())));

    let n1 = Normal::<f32>::new(0.0, (2f32/14f32).sqrt()).unwrap();
    let n2 = Normal::<f32>::new(0.0, (2f32/100f32).sqrt()).unwrap();
    let n3 = Normal::<f32>::new(0.0, 1f32/100f32.sqrt()).unwrap();

    let memory_pool = &SHARED_MEMORY_POOL.clone();

    let device = DeviceGpu::new(memory_pool).unwrap();

    let net:InputLayer<f32,Arr<f32,14>,_,_> = InputLayer::new(&device);

    let rnd = rnd_base.clone();

    let optimizer_builder = MomentumSGDBuilder::new(&device).lr(0.001);

    let mut net = net.add_layer(|l| {
        let rnd = rnd.clone();
        LinearLayerBuilder::<14,100>::new().build(l,&device,
                                                  move || n1.sample(&mut rnd.borrow_mut().deref_mut()), || 0.,
                                                  &optimizer_builder

        ).unwrap()
    }).add_layer(|l| {
        ActivationLayer::new(l,ReLu::new(&device),&device)
    }).add_layer(|l| {
        let rnd = rnd.clone();
        LinearLayerBuilder::<100,100>::new().build(l,&device,
                                                   move || n2.sample(&mut rnd.borrow_mut().deref_mut()), || 0.,
                                                   &optimizer_builder

        ).unwrap()
    }).add_layer(|l| {
        ActivationLayer::new(l,ReLu::new(&device),&device)
    }).add_layer(|l| {
        let rnd = rnd.clone();
        LinearLayerBuilder::<100, 1>::new().build(l, &device,
                                                  move || n3.sample(&mut rnd.borrow_mut().deref_mut()), || 0.,
                                                  &optimizer_builder

        ).unwrap()
    }).add_layer(|l| {
        let mut l = LoggingLayer::new(l,&device);

        {
            let sender = sender.clone();

            l.add_forward_logger(move |_| {
                println!("forward");
                sender.send("forward").unwrap();

                Ok(())
            });
        }

        {
            let sender = sender.clone();

            l.add_backward_logger(move |_| {
                println!("backward");
                sender.send("backward").unwrap();

                Ok(())
            });
        }

        {
            let sender = sender.clone();

            l.add_gradient_logger(move |_| {
                println!("gradient");
                sender.send("gradient").unwrap();

                Ok(())
            });
        }

        {
            let sender = sender.clone();

            l.add_batch_forward_logger(move |_| {
                println!("batch forward");
                sender.send("batch forward").unwrap();

                Ok(())
            });
        }

        {
            let sender = sender.clone();

            l.add_batch_backward_logger(move |_| {
                println!("batch backward");
                sender.send("batch backward").unwrap();

                Ok(())
            });
        }

        l
    }).add_layer(|l| {
        ActivationLayer::new(l,Sigmoid::new(&device),&device)
    }).add_layer(|l| {
        LinearOutputLayer::new(l,&device).unwrap()
    });

    let mut teachers:Vec<(bool,Vec<f32>)> = Vec::new();

    let mut reader =  BufReader::new(
        File::open(Path::new("data")
            .join("weather")
            .join("training")
            .join("weather.csv")).unwrap());

    let mut line:String = String::new();

    loop {
        if reader.read_line(&mut line).unwrap() == 0 {
            break;
        }

        let columns = line.trim().split(',').map(|c| c.to_string()).collect::<Vec<String>>();

        line.clear();

        if columns.len() != 16 {
            continue;
        }

        let t = columns[1].find("晴").is_some();

        let columns = columns.iter().skip(2)
            .filter(|c| !c.parse::<f32>().is_err())
            .map(|c| c.parse::<f32>().unwrap() / 10000.)
            .collect::<Vec<f32>>();
        if columns.len() < 14 {
            continue;
        }

        teachers.push((t,columns));
    }

    let mut rng = rand::thread_rng();

    let lossf = CrossEntropy::new();

    let mut iter = teachers.chunks_mut(10).take(10);

    if let Some(chunk) = iter.next() {
        for (t, columns) in chunk.iter_mut() {
            let t = *t;

            let mut input = Arr::<f32, 14>::new();

            for (it, p) in input.iter_mut().zip(columns.iter()) {
                *it = *p;
            }

            let mut expected = Arr::new();

            expected[0] = if t {
                1.
            } else {
                0.
            };

            let _ = net.train(expected,input,&lossf).unwrap();
        }
    }

    while let Some(teachers) = iter.next()  {
        teachers.shuffle(&mut rng);

        let mut train_data = Vec::new();

        for (t, columns) in teachers.iter() {
            let t = *t;

            let mut input = Arr::<f32, 14>::new();

            for (it, p) in input.iter_mut().zip(columns.iter()) {
                *it = *p;
            }

            let mut expected = Arr::new();

            expected[0] = if t {
                1.
            } else {
                0.
            };

            train_data.push((expected,input));
        }

        let train_data = train_data.into_iter().fold((Vec::new(),Vec::new()),|mut acc,(e,input)| {
            acc.0.push(e);
            acc.1.push(input);

            acc
        });
        let _ = net.batch_train(train_data.0.into(),train_data.1.into(),&lossf).unwrap();
    }

    for _ in 0..10 {
        assert_eq!("forward", receiver.recv().unwrap());
        assert_eq!("backward", receiver.recv().unwrap());
        assert_eq!("gradient", receiver.recv().unwrap());
    }

    for _ in 0..9 {
        assert_eq!("batch forward", receiver.recv().unwrap());
        assert_eq!("batch backward", receiver.recv().unwrap());
        assert_eq!("gradient", receiver.recv().unwrap());
    }
}