Struct neuralneat::Pool
source · pub struct Pool { /* private fields */ }
Expand description
Implementations§
source§impl Pool
impl Pool
sourcepub fn new(
inputs: usize,
outputs: usize,
population_size: usize,
connection_mut_rate: f32,
node_mut_rate: f32,
weight_mut_rate: f32,
perturb_rate: f32,
weight_mut_step_size: f32,
disable_mut_rate: f32,
enable_mut_rate: f32,
excess_coef: f32,
disjoint_coef: f32,
weight_diff_coef: f32,
species_threshold: f32,
mut_only_rate: f32,
mate_only_rate: f32,
crossover_rate: f32,
species_dropoff_age: usize,
age_significance: f32,
survival_threshold: f32
) -> Self
pub fn new( inputs: usize, outputs: usize, population_size: usize, connection_mut_rate: f32, node_mut_rate: f32, weight_mut_rate: f32, perturb_rate: f32, weight_mut_step_size: f32, disable_mut_rate: f32, enable_mut_rate: f32, excess_coef: f32, disjoint_coef: f32, weight_diff_coef: f32, species_threshold: f32, mut_only_rate: f32, mate_only_rate: f32, crossover_rate: f32, species_dropoff_age: usize, age_significance: f32, survival_threshold: f32 ) -> Self
Initialize a new Pool with the given number of inputs
, outputs
, and
overriding all of the default constants. (If you wish to use some of
the defaults they are accessible in the defaults module.)
sourcepub fn with_defaults(inputs: usize, outputs: usize) -> Self
pub fn with_defaults(inputs: usize, outputs: usize) -> Self
Initialize a new Pool with the given number of inputs
, outputs
,
and use all of the default constants.
Examples found in repository?
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the training data structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
let mut trainer = Trainer::new(training_data);
// This function will be called once per Genome per piece of
// TrainingData in each generation, passing the values of the
// output nodes of the Genome as well as the expected result
// from the TrainingData.
trainer.evaluate_fn = adding_fitness_func;
trainer.hidden_activation = linear_activation;
trainer.output_activation = linear_activation;
// Train over the course of 100 generations
trainer.train(&mut gene_pool, 100);
let best_genome = gene_pool.get_best_genome();
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome).unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 2;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We use a label here to allow us to break out if we find a genome
// with a perfect score before we run through all of the generations.
'outer: for generation in 0..1000 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(&td.inputs[0..2].to_vec(), None, None);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += fitness_func(genome.get_outputs()[0], td.expected[0]);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
if fitness == PERFECT_SCORE {
println!("Found a perfect genome!");
break 'outer;
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 5 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
genome.evaluate(&vec![input1, input2], None, None);
if genome.get_outputs()[0] > 0.5 {
println!("Predicted that {} is greater than {}!", input1, input2);
} else {
println!(
"Predicted that {} is equal or less than {}!",
input1, input2
);
}
}
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We will test genomes from 100 generations
for generation in 0..100 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
// As genomes diverge in structure and configuration, they will
// be divided into separate species.
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(
&td.inputs[0..4].to_vec(),
Some(linear_activation),
Some(linear_activation),
);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += adding_fitness_func(&genome.get_outputs(), &td.expected);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
sourcepub fn train_population(
&mut self,
generations: usize,
training_data: &Vec<TrainingData>,
evaluate_fn: EvaluationFn,
hidden_activation: Option<ActivationFn>,
output_activation: Option<ActivationFn>
)
pub fn train_population( &mut self, generations: usize, training_data: &Vec<TrainingData>, evaluate_fn: EvaluationFn, hidden_activation: Option<ActivationFn>, output_activation: Option<ActivationFn> )
Trains a population of Genomes over generations
generations.
training_data
must be a &Vec of TrainingData. Each Genome will be
gevn the inputs from each TrainingData as inputs
to its network. evaluate_fn
will be called after each item of TrainingData
is fed to the Genome. This function will be passed a Vec of the Genome’s
outputs and the expected value or values from the
TrainingData. This function is expected to assess the Genome’s performance
by comparing the two, and return an f32 representing its “score”. The scores
from each call to evaluate_fn
will be summed together to form the final
fitness value of the Genome.
sourcepub fn stats(&self) -> PoolStats
pub fn stats(&self) -> PoolStats
Returns the PoolStats at a moment in time. (If you need updated statistics you must call this method each time you need them.)
sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of Species in the Pool.
Examples found in repository?
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 2;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We use a label here to allow us to break out if we find a genome
// with a perfect score before we run through all of the generations.
'outer: for generation in 0..1000 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(&td.inputs[0..2].to_vec(), None, None);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += fitness_func(genome.get_outputs()[0], td.expected[0]);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
if fitness == PERFECT_SCORE {
println!("Found a perfect genome!");
break 'outer;
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 5 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
genome.evaluate(&vec![input1, input2], None, None);
if genome.get_outputs()[0] > 0.5 {
println!("Predicted that {} is greater than {}!", input1, input2);
} else {
println!(
"Predicted that {} is equal or less than {}!",
input1, input2
);
}
}
}
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We will test genomes from 100 generations
for generation in 0..100 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
// As genomes diverge in structure and configuration, they will
// be divided into separate species.
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(
&td.inputs[0..4].to_vec(),
Some(linear_activation),
Some(linear_activation),
);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += adding_fitness_func(&genome.get_outputs(), &td.expected);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
sourcepub fn population_size(&self) -> usize
pub fn population_size(&self) -> usize
sourcepub fn get_best_genome(&self) -> Genome
pub fn get_best_genome(&self) -> Genome
Returns the best Genome in the current population. (Earlier generations could theoretically have had a better Genome. If it is important to have the best Genome ever, you should call this method once per generation to check each generation’s best Genome.)
Examples found in repository?
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the training data structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
let mut trainer = Trainer::new(training_data);
// This function will be called once per Genome per piece of
// TrainingData in each generation, passing the values of the
// output nodes of the Genome as well as the expected result
// from the TrainingData.
trainer.evaluate_fn = adding_fitness_func;
trainer.hidden_activation = linear_activation;
trainer.output_activation = linear_activation;
// Train over the course of 100 generations
trainer.train(&mut gene_pool, 100);
let best_genome = gene_pool.get_best_genome();
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome).unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}
sourcepub fn new_generation(&mut self)
pub fn new_generation(&mut self)
Spawn the next generation of Genomes. This should only be done after you’ve assessed all Genomes in the current generation and updated their fitness scores. Calling this function will use the top performing existing Genomes as the basis of the next generation.
Examples found in repository?
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 2;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We use a label here to allow us to break out if we find a genome
// with a perfect score before we run through all of the generations.
'outer: for generation in 0..1000 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(&td.inputs[0..2].to_vec(), None, None);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += fitness_func(genome.get_outputs()[0], td.expected[0]);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
if fitness == PERFECT_SCORE {
println!("Found a perfect genome!");
break 'outer;
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 5 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
genome.evaluate(&vec![input1, input2], None, None);
if genome.get_outputs()[0] > 0.5 {
println!("Predicted that {} is greater than {}!", input1, input2);
} else {
println!(
"Predicted that {} is equal or less than {}!",
input1, input2
);
}
}
}
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: '{} train' to train a new comparer.", args[0]);
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
if args[1] == "train" {
// One input node for each input in the TrainingData structure
let input_nodes = 4;
// One output node with the "prediction"
let output_nodes = 1;
// Create a new gene pool with an initial population of genomes
let mut gene_pool = Pool::with_defaults(input_nodes, output_nodes);
let training_data = load_training_data(TRAINING_DATA_STRING, 4, 1);
// These variables are used to keep track of the top performer, so we
// can write it out later.
let mut best_genome: Option<Genome> = None;
let mut best_fitness = 0.0;
// We will test genomes from 100 generations
for generation in 0..100 {
println!("Evaluating generation {}", generation + 1);
let total_species = gene_pool.len();
// As genomes diverge in structure and configuration, they will
// be divided into separate species.
for s in 0..total_species {
let species = &mut gene_pool[s];
let genomes_in_species = species.len();
for g in 0..genomes_in_species {
let genome = &mut species[g];
let mut fitness = 0.0;
for td in &training_data {
// Evaluate the genome using the training data as the
// initial inputs.
genome.evaluate(
&td.inputs[0..4].to_vec(),
Some(linear_activation),
Some(linear_activation),
);
// We add this to the existing fitness for the genome
// to ensure that the genomes with the best score across
// all tests will have the highest overall fitness.
fitness += adding_fitness_func(&genome.get_outputs(), &td.expected);
}
// Update the genome with the calculate fitness score.
// (This is important, as this fitness score is needed to
// spawn the next generation correctly.)
genome.update_fitness(fitness);
if fitness > best_fitness {
println!(
"Species {} Genome {} increased best fitness to {}",
s, g, best_fitness
);
best_fitness = fitness;
best_genome = Some(genome.clone());
}
}
}
// Spawn the next generation.
gene_pool.new_generation();
}
println!("Serializing best genome to winner.json");
serde_json::to_writer(&File::create("winner.json").unwrap(), &best_genome.unwrap())
.unwrap();
} else {
if args.len() < 7 {
println!("Usage: '{} evaluate serialized_genome.json input1 input2 input3 input4' to evaluate with an existing genome.", args[0]);
return;
}
let mut genome: Genome = serde_json::from_reader(File::open(&args[2]).unwrap()).unwrap();
let input1 = args[3]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input2 = args[4]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
let input3 = args[5]
.parse::<f32>()
.expect("Couldn't parse input1 as f32");
let input4 = args[6]
.parse::<f32>()
.expect("Couldn't parse input2 as f32");
// Note that this is the exact same function we used in training
// further up!
genome.evaluate(
&vec![input1, input2, input3, input4],
Some(linear_activation),
Some(linear_activation),
);
println!(
"Sum of inputs is..........{}",
genome.get_outputs()[0] as u32
);
}
}