#include "GANeuralNet.h"
GANeuralNet::GANeuralNet(int NetsInGeneration, int Inputs, int HiddenLayers, int NeuronsInHidden, bool _Debug) {
NetInputs = Inputs;
Debug = _Debug;
Gene* NewGene;
for(int i = 0; i < NetsInGeneration; ++i) {
// Create a gene for the net
NewGene = new Gene(Inputs, HiddenLayers, NeuronsInHidden, 0);
GenePool.push_back(NewGene);
}
srand( (unsigned)time( NULL ) );
}
GANeuralNet::~GANeuralNet() {
for(unsigned int i = 0; i < GenePool.size(); ++i)
delete GenePool;
GenePool.clear();
}
//---------------------------------------
// This function serve as the roulette wheel and return a random gene.
// Higher fitness score of a gene equals a higher probability of it being selected
//
// Input: All the fitness scores added up
//
// Output: Returns the selected genes id
//----------------------------------------
int GANeuralNet::GetRouletteGene(double TotalFitness) {
// the number to point out a gene
double Random = TotalFitness*((float)rand()/(float)RAND_MAX);
for(int j = 0; j < GenePool.size(); ++j) {
if(j != 0) {
//cout << Random << endl << GenePool[j]->WheelSlot << endl << GenePool[j-1]->WheelSlot << endl << endl;
// are we withing the slice?
if(Random < GenePool[j]->WheelSlot && Random > GenePool[j-1]->WheelSlot)
return j;
}
else {
if(Random < GenePool[0]->WheelSlot)
return 0;
}
}
cout << "error in roulette.\n";
return 0;
}
// ------------------------------
// This function do the breeding
//
// Input: The two parent genes
//
// Output: It returns a new gene with crossovered chromosomes from the parents with added mutation
// -------------------------------
Gene* GANeuralNet::Breed(Gene* Parent1, Gene* Parent2) {
// the determine how many weights we take from each parent
int CrossoverValue = rand()%(Parent1->Net.GetWeights().size());
//cout << CrossoverValue << endl;
vector<float> NewWeights;
vector<float> p1 = Parent1->Net.GetWeights();
vector<float> p2 = Parent2->Net.GetWeights();
// copy over the weights
for(int i = 0; i < p1.size(); ++i) {
if(i <= CrossoverValue)
NewWeights.push_back(p1);
else
NewWeights.push_back(p2);
}
// mutate
for(int i = 0; i < NewWeights.size(); ++i) {
if((rand()%100) < 1) {
float MutVal = 10.0f*((float)rand()/(float)RAND_MAX)-5.0f;
NewWeights *= MutVal;
if(Debug)
cout << "Weight " << i << " mutated with " << MutVal << endl;
}
}
Gene* NewGene = new Gene; // make the new gene
NewGene->Net.SetWeights(NewWeights); // make it use the new weights
return NewGene; // return it
}
// ------------------------------------------------
// This function calculates every genes fitness from a given input,
// it also assign every gene a "wheelslot" which is used later when we spin the roulette wheel to
// randomly determine two genes that should breed. The wheelslot is the upper limit, if the random roulette wheel
// number is below this (but still above the previous genes slot of course) this gene is selected.
//
// Input: A vector with all the inputs and the desired output
//
// Output: None, it writes the data directly
// --------------------------------------------------
void GANeuralNet::CalculateFitness(vector<float> NetIn, float Target) {
if(Debug)
cout << "Input: " << NetIn[0] << " " << NetIn[1] << " Target: " << Target << endl;
for(int i = 0; i < GenePool.size(); ++i) {
if(i != 0) { // not first one
GenePool->Fitness += 1/(abs(GenePool->Net.Process(NetIn) - Target));
if(GenePool->Fitness > MAX_FITNESS) // max fitness
GenePool->Fitness = MAX_FITNESS;
GenePool->WheelSlot = GenePool->Fitness + GenePool[i-1]->WheelSlot;
}
else {
GenePool->Fitness += 1/(abs(GenePool->Net.Process(NetIn) - Target));
if(GenePool->Fitness > MAX_FITNESS) // max fitness
GenePool->Fitness = MAX_FITNESS;
GenePool->WheelSlot = GenePool->Fitness;
}
if(Debug)
cout << "Gene " << i << "\toutputted " << GenePool->Net.Process(NetIn) << "\tfitness score: " << GenePool->Fitness << endl;
}
}
// ------------------------------------------------
// This function ties the above functions together, it gets two genes and breed them. All over, until we
// have a population of genes as large as when we started. Then it saves it as the new generation.
//
// Input: None
//
// Output: None
//--------------------------------------------------
void GANeuralNet::NextGeneration() {
vector<Gene*> NewGenePool; // here we will have all the new genomes
Gene* A = NULL; // first to mate
Gene* B = NULL; // second one
int a=0, b=0;
double TotalFitness = 0;
vector<int> ChildCount; // se we know how many children each gene got
if(Debug) {
ChildCount.resize(GenePool.size(), 0);
cout << endl;
}
for(int i = 0; i < GenePool.size(); ++i)
TotalFitness += GenePool->Fitness;
for(int i = 0; i < GenePool.size(); ++i) { // we should end up with the same number of genomes as when we started
// calculate which two genomes should mate
a = GetRouletteGene(TotalFitness);
b = GetRouletteGene(TotalFitness);
if(Debug) {
ChildCount[a]++;
ChildCount++;
}
A = GenePool[ a ];
B = GenePool;
<span class="cpp-comment">// crossover and store in new gene pool</span>
NewGenePool.push_back(Breed(A, B));
}
<span class="cpp-keyword">if</span>(Debug)
cout << endl;
<span class="cpp-comment">// clear old genepool</span>
<span class="cpp-keyword">for</span>(<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">int</span> i = <span class="cpp-number">0</span>; i < GenePool.size(); ++i) {
<span class="cpp-keyword">if</span>(Debug)
cout << <span class="cpp-literal">"Gene "</span> << i << <span class="cpp-literal">"\tgot "</span> << ChildCount<span style="font-weight:bold;"> << <span class="cpp-literal">"\tchildren.\n"</span>;
<span class="cpp-keyword">delete</span> GenePool<span style="font-weight:bold;">;
}
GenePool.clear();
<span class="cpp-comment">// copy and make it the new generation</span>
GenePool = NewGenePool;
<span class="cpp-comment">//cout << GenePool.size();</span>
<span class="cpp-keyword">if</span>(Debug)
cout << endl;
}
<span class="cpp-comment">// βββββββββββ-</span>
<span class="cpp-comment">// This function manages the training</span>
<span class="cpp-comment">//</span>
<span class="cpp-comment">// Input: A vector with all the training sets, a value of how many training runs</span>
<span class="cpp-comment">//</span>
<span class="cpp-comment">// Output: Returns the best neural net after training is done</span>
<span class="cpp-comment">// βββββββββββ</span>
NeuralNet GANeuralNet::Train(vector<NetInput> NetIn, <span class="cpp-keyword">int</span> Iterations) {
<span class="cpp-keyword">double</span> AverageFitness = <span class="cpp-number">0</span>;
<span class="cpp-keyword">if</span>(NetIn[<span class="cpp-number">0</span>].Input.size() != NetInputs) {
cout << <span class="cpp-literal">"Error: Invalid amounts of net inputs.\nReturning first net.\n"</span>;
<span class="cpp-keyword">return</span> GenePool[<span class="cpp-number">0</span>]->Net;
}
cout << <span class="cpp-literal">"Training will be run for "</span> << Iterations << <span class="cpp-literal">" generations.\n"</span>;
cout << <span class="cpp-literal">"Working with "</span> << NetIn.size() << <span class="cpp-literal">" number of data sets.\n"</span>;
getch();
<span class="cpp-keyword">for</span>(<span class="cpp-keyword">int</span> iter = <span class="cpp-number">0</span>; iter < Iterations; ++iter) {
<span class="cpp-keyword">if</span>(Debug)
cout << <span class="cpp-literal">"ββββ Generation "</span> << iter << <span class="cpp-literal">" βββββββββββββββββ\n"</span>;
<span class="cpp-keyword">for</span>(<span class="cpp-keyword">int</span> in = <span class="cpp-number">0</span>; in < NetIn.size(); ++in) { <span class="cpp-comment">// once for every kind of input, add up the fitnesses for every kind of input</span>
CalculateFitness(NetIn[in].Input, NetIn[in].TargetOutput);
<span class="cpp-keyword">if</span>(Debug)
cout << endl;
}
<span class="cpp-keyword">for</span>(<span class="cpp-keyword">int</span> i = <span class="cpp-number">0</span>; i < GenePool.size(); ++i) <span class="cpp-comment">// reset fitness</span>
AverageFitness += GenePool<span style="font-weight:bold;">->Fitness;
AverageFitness /= GenePool.size();
cout << <span class="cpp-literal">"AverageFitness is "</span> << AverageFitness << endl;
<span class="cpp-comment">// then replace the current generation with better genomes</span>
NextGeneration();
<span class="cpp-keyword">if</span>(Debug)
getch();
}
<span class="cpp-comment">// training done! return best performing genome</span>
<span class="cpp-keyword">return</span> BestGene();
}
NeuralNet GANeuralNet::BestGene() {
<span class="cpp-keyword">float</span> HighestFitnessSoFar = <span class="cpp-number">0</span>;
<span class="cpp-keyword">int</span> BestGeneID = <span class="cpp-number">0</span>;
<span class="cpp-keyword">for</span>(<span class="cpp-keyword">int</span> i = <span class="cpp-number">0</span>; i < GenePool.size(); ++i) {
<span class="cpp-keyword">if</span>(GenePool<span style="font-weight:bold;">->Fitness > HighestFitnessSoFar) { <span class="cpp-comment">// this gene is better</span>
HighestFitnessSoFar = GenePool<span style="font-weight:bold;">->Fitness;
BestGeneID = i;
}
}
<span class="cpp-keyword">return</span> GenePool[BestGeneID]->Net;
}
</pre></div><!βENDSCRIPTβ>
If you want to see even more code, the full source and executable can be downloaded <a href="http://web.telia.com/~u85427399/GANet.rar">here.</a>
I really want to solve this one, please help a fellow coder out.
[edit] something went wrong with source tags
<!βEDITβ><span class=editedby><!β/EDITβ>[Edited by - Mizipzor on February 12, 2006 10:09:58 AM]<!βEDITβ></span><!β/EDITβ>
[solved] Stuck with trying to write a genetic algorithm routine
I tried to make a neural net with back propagation, something wasnt working and I gave up. Tried to train the net through genetic algorithms instead but Im stuck here to. :P This is for a school assignment to, and Im running out of time. Would really appreciate any kind of help here.
When writing the class that handles the evolution of the neural nets, I followed the tutorial at ai-junkie. Had to do some adaptions since it had to work with the neural net class I wrote, but most of the structure I took from there.
I try to learn it AND, two binary inputs and one output. The problem is, when I train it, the best genome returned always outputs the same (or very similiar) value on all possible combinations of inputs.
The most logical place for the error to lie is within the Breed function, take two genomes and create an offspring, add mutation. Or within the CalculateFitness function. But Im unable to find anything in there.
In a generation, I start by calculating the fitness which is:
1/(NetOutput - TargetOutput)
I have four combinations of training sets, 0 0, 1 0, 0 1, 1 1, I calculate, and add up, the fitness for all four combinations.
Then, I do the so called "roulette wheel" to select two random genes that should mate. I randomize a crossover value, and loop through the nets weight. As long as the weight id is below the crossover value, I take the weight from the mother, if it is above, I take it from the father.
Then I loop through the new set of weights and see if anything should be mutated, if it should be, I multiply the weight with a random number.
Now, the new population is generated and I start all over. Any obvious errors in my routine or is it the code thats wrong?
Heres the code for the genetic algorithm class, Ive attached all of it if anyone wants to see anything besides the Breed and Calculatefitness functions.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement