#[macro_use]
extern crate prophet;
extern crate rand;
extern crate itertools;
#[macro_use]
extern crate approx;
use prophet::prelude::*;
use rand::{Open01, Rng, thread_rng};
use std::time::Duration;
fn validate_impl(mut net: NeuralNet, samples: Vec<Sample>, rounded: bool) {
use itertools::{Itertools, multizip};
for sample in samples {
let predicted = net.predict(sample.input.view());
multizip((predicted.iter(), sample.target.iter()))
.foreach(|(&predicted, &expected)| {
if rounded {
relative_eq!(predicted.round(), expected);
}
else {
relative_eq!(predicted, expected);
}
});
}
}
fn validate_rounded(net: NeuralNet, samples: Vec<Sample>) {
validate_impl(net, samples, true)
}
fn validate_exact(net: NeuralNet, samples: Vec<Sample>) {
validate_impl(net, samples, false)
}
fn gen_random_samples<F>(amount: usize,
input_size: usize,
output_size: usize,
mapping: F) -> Vec<Sample>
where F: Fn(&[f32]) -> Vec<f32>
{
let mut rng = thread_rng();
let mut samples = Vec::with_capacity(amount);
for _ in 0..amount {
let inputs = rng.gen_iter::<Open01<f32>>()
.take(input_size)
.map(|Open01(val)| val)
.collect::<Vec<f32>>();
assert_eq!(inputs.len(), input_size);
let outputs = mapping(&inputs);
assert_eq!(outputs.len(), output_size);
samples.push(Sample::from((inputs, outputs)))
}
assert_eq!(samples.len(), amount);
samples
}
#[test]
fn train_xor() {
use Activation::Tanh;
let (t, f) = (1.0, -1.0);
let samples = samples![
[f, f] => f,
[t, f] => t,
[f, t] => t,
[t, t] => f
];
let net = Topology::input(2)
.layer(4, Tanh)
.layer(3, Tanh)
.output(1, Tanh)
.train(samples.clone())
.learn_rate(0.6)
.log_config(LogConfig::TimeSteps(Duration::from_secs(1)))
.go()
.unwrap();
validate_rounded(net, samples);
}
#[test]
fn train_constant() {
use Activation::Identity;
let learn_samples = samples![
0.0 => 1.0,
0.2 => 1.0,
0.4 => 1.0,
0.6 => 1.0,
0.8 => 1.0,
1.0 => 1.0
];
let test_samples = samples![
0.1 => 1.0,
0.3 => 1.0,
0.5 => 1.0,
0.7 => 1.0,
0.9 => 1.0
];
let net = Topology::input(1)
.output(1, Identity)
.train(learn_samples)
.log_config(LogConfig::TimeSteps(Duration::from_secs(1)))
.go()
.unwrap();
validate_rounded(net, test_samples)
}
#[test]
fn train_and() {
use Activation::Tanh;
let (t, f) = (1.0, -1.0);
let samples = samples![
[f, f] => f,
[f, t] => f,
[t, f] => f,
[t, t] => t
];
let net = Topology::input(2)
.output(1, Tanh)
.train(samples.clone())
.log_config(LogConfig::TimeSteps(Duration::from_secs(1)))
.go()
.unwrap();
validate_rounded(net, samples)
}
#[test]
fn train_triple_add() {
use Activation::Identity;
let count_learn_samples = 10_000;
let count_test_samples = 100;
let inputs = 3;
let outputs = 1;
fn mapper(inputs: &[f32]) -> Vec<f32> {
vec![inputs[0] + inputs[1] + inputs[2]]
}
let learn_samples = gen_random_samples(
count_learn_samples, inputs, outputs, mapper);
let test_samples = gen_random_samples(
count_test_samples, inputs, outputs, mapper);
let net = Topology::input(inputs)
.output(outputs, Identity)
.train(learn_samples)
.log_config(LogConfig::TimeSteps(Duration::from_secs(1)))
.go()
.unwrap();
validate_exact(net, test_samples)
}
#[test]
fn train_compare() {
use Activation::Tanh;
let count_learn_samples = 10_000;
let count_test_samples = 100;
let inputs = 2;
let outputs = 1;
fn mapper(inputs: &[f32]) -> Vec<f32> {
if inputs[0] < inputs[1] {
vec![-1.0]
}
else {
vec![1.0]
}
}
let learn_samples = gen_random_samples(
count_learn_samples, inputs, outputs, mapper);
let test_samples = gen_random_samples(
count_test_samples, inputs, outputs, mapper);
let net = Topology::input(inputs)
.layer(4, Tanh)
.layer(3, Tanh)
.output(outputs, Tanh)
.train(learn_samples)
.log_config(LogConfig::TimeSteps(Duration::from_secs(1)))
.go()
.unwrap();
validate_exact(net, test_samples)
}