/// <summary> /// Create a single child genome by performing crossover on two or more parents. /// </summary> /// <param name="rnd">A random number generator.</param> /// <param name="parents">The parents.</param> /// <param name="cutLength">The cut length for sequences of genes that crossover together.</param> /// <returns>The new child genome.</returns> public static Genome[] Crossover(Random rnd, Genome[] parents, int cutLength) { int genomeSize = parents[0].Genes.Length; var offspring = new Genome(genomeSize); var result = new Genome[1]; result[0] = offspring; int currentIndex = 0; int cutCount = 0; int parentIndex = 0; Genome currentParent = parents[parentIndex]; while (currentIndex < parents[0].Genes.Length) { offspring.Genes[currentIndex] = currentParent.Genes[currentIndex]; currentIndex++; cutCount++; if (cutCount > cutLength) { parentIndex++; if (parentIndex >= parents.Length) { parentIndex = 0; } currentParent = parents[parentIndex]; cutCount = 0; } } return result; }
public double[] Compute(double[] input, Genome genome) { // first, compute the output values of each of the RBFs. // Add in one additional RBF output for bias (always set to one). var rbfOutput = new double[_rbfCount + 1]; rbfOutput[rbfOutput.Length - 1] = 1; // bias for (int rbfIndex = 0; rbfIndex < _rbfCount; rbfIndex++) { // weight the input var weightedInput = new double[input.Length]; for (int inputIndex = 0; inputIndex < input.Length; inputIndex++) { int memoryIndex = _indexInputWeights + (rbfIndex*_inputCount) + inputIndex; weightedInput[inputIndex] = input[inputIndex]*genome.Genes[memoryIndex]; } // calculate the rbf rbfOutput[rbfIndex] = CalculateGaussian(genome, rbfIndex, weightedInput); } // second, calculate the output, which is the result of the weighted result of the RBF's. var result = new double[_outputCount]; for (int outputIndex = 0; outputIndex < result.Length; outputIndex++) { double sum = 0; for (int rbfIndex = 0; rbfIndex < rbfOutput.Length; rbfIndex++) { int memoryIndex = _indexOutputWeights + (outputIndex*rbfOutput.Length) + rbfIndex; sum += rbfOutput[rbfIndex]*genome.Genes[memoryIndex]; } result[outputIndex] = sum; } return result; }
/// <summary> /// Score the model when the output is regression. /// For regression, the score is mean square error (MSE). /// http://www.heatonresearch.com/wiki/Mean_Square_Error /// </summary> /// <param name="model">The model to score.</param> /// <param name="testSubject">The test subject.</param> /// <param name="input">The input.</param> /// <param name="ideal">The ideal.</param> /// <returns>The score.</returns> private static double ScoreRegression(IGAModel model, Genome testSubject, double[][] input, double[][] ideal) { double error = 0; double elementCount = 0; Parallel.For(0, input.Length, row => { double[] output = model.Compute(input[row], testSubject); for (int col = 0; col < output.Length; col++) { double delta = output[col] - ideal[row][col]; error += delta * delta; elementCount++; } }); return Math.Sqrt(error / elementCount); }
/// <summary> /// Score the model when the output is classification. /// For classification, the score is the percentage correct. /// </summary> /// <param name="model">The model to score.</param> /// <param name="testSubject">The test subject.</param> /// <param name="input">The input.</param> /// <param name="ideal">The ideal.</param> /// <returns>The score.</returns> private static double ScoreClassification(IGAModel model, Genome testSubject, double[][] input, double[][] ideal) { double error = 0; double elementCount = 0; int badCount = 0; Parallel.For(0, input.Length, row => { double[] output = model.Compute(input[row], testSubject); int outputIndex = FindTopIndex(output); int idealIndex = FindTopIndex(ideal[row]); if (outputIndex != idealIndex) { badCount++; } elementCount++; }); return badCount / elementCount; }
/// <summary> /// Score the model for regression or classification. /// For classification, the score is the percentage correct. /// For regression, the score is mean square error (MSE). /// http://www.heatonresearch.com/wiki/Mean_Square_Error /// </summary> /// <param name="model">The model to score.</param> /// <param name="testSubject">The test subject.</param> /// <param name="input">The input.</param> /// <param name="ideal">The ideal.</param> /// <returns>The score.</returns> public static double Score(IGAModel model, Genome testSubject, double[][] input, double[][] ideal) { if (ideal[0].Length > 1) { return ScoreClassification(model, testSubject, input, ideal); } return ScoreRegression(model, testSubject, input, ideal); }
/// <summary> /// Produce a new genome, by randomly changing the parent. However, the parent is /// not actually changed. /// </summary> /// <param name="rnd">Random number generator.</param> /// <param name="parent">The parent genome.</param> /// <param name="minRange">The minimum amount by which a gene will change.</param> /// <param name="maxRange">The maximum amount by which a gene will change.</param> /// <returns>The new child genome.</returns> public static Genome Mutate(Random rnd, Genome parent, double minRange, double maxRange) { bool didMutate = false; var result = new Genome(parent.Genes.Length); for (int i = 0; i < parent.Genes.Length; i++) { if (rnd.NextDouble() < 0.2) { double d = RandomUtil.RangeDouble(rnd, minRange, maxRange); result.Genes[i] += d; didMutate = true; } else { result.Genes[i] = parent.Genes[i]; } } if (!didMutate) { int i = RandomUtil.RangeInt(rnd, 0, parent.Genes.Length); result.Genes[i] += RandomUtil.RangeDouble(rnd, minRange, maxRange); } return result; }
/// <summary> /// Generate a random starting population. /// </summary> public void Generate() { var rnd = new Random(); _genomes.Clear(); _bestGenome = null; for (int i = 0; i < _config.PopulationSize; i++) { Genome genome = _config.Model.GenerateRandomGenome(rnd, _config); _genomes.Add(genome); UpdateBestGenome(genome); } }
public Genome Mutate(Random rnd, Genome parent) { return TypicalGAModel.Mutate(rnd, parent, -0.01, +0.01); }
/// <summary> /// Update the best genome. /// </summary> /// <param name="g">The genome to evaluate.</param> private void UpdateBestGenome(Genome g) { if (_config.MaxGoal) { if (_bestGenome == null || g.Score > _bestGenome.Score) { _bestGenome = g; } } else { if (_bestGenome == null || g.Score < _bestGenome.Score) { _bestGenome = g; } } }
/// <summary> /// Score all genomes. /// </summary> /// <param name="trainingInput">The training input.</param> /// <param name="trainingIdeal">The ideal output.</param> public void ScoreAll(double[][] trainingInput, double[][] trainingIdeal) { _bestGenome = null; foreach (Genome genome in _genomes) { UpdateBestGenome(genome); genome.Score = _config.Model.Score(genome, trainingInput, trainingIdeal); } }
/// <summary> /// Determine if one genome is better than another. /// </summary> /// <param name="a">The first genome.</param> /// <param name="b">The second genome.</param> /// <returns>True, if a is better than b.</returns> public bool IsBetterThan(Genome a, Genome b) { if (_config.MaxGoal) { return a.Score > b.Score; } return a.Score < b.Score; }
private double CalculateGaussian(Genome genome, int rbfIndex, double[] xValues) { int rbfArrayIndex = _indexRBFParams + ((_inputCount + 1)*rbfIndex); double value = 0; double width = genome.Genes[rbfArrayIndex]; for (int i = 0; i < _inputCount; i++) { double center = genome.Genes[rbfArrayIndex + i + 1]; value += Math.Pow(xValues[i] - center, 2)/(2.0*width*width); } return Math.Exp(-value); }
public double Score(Genome testSubject, double[][] input, double[][] ideal) { return TypicalGAModel.Score(this, testSubject, input, ideal); }
public static Genome GenerateRandomGenome(Random rnd, ConfigScript script, int genomeSize) { var result = new Genome(genomeSize); for (int i = 0; i < result.Genes.Length; i++) { result.Genes[i] = rnd.NextDouble(); } return result; }
/// <summary> /// Add a new child genome and replace low-scoring population member. /// This is how unfit genomes are removed. /// </summary> /// <param name="rnd">A random number generator.</param> /// <param name="child">The new child to add.</param> public void AddChildAndReplace(Random rnd, Genome child) { int killIndex = TournamentForWorst(rnd); _genomes[killIndex] = child; UpdateBestGenome(child); }
/// <summary> /// Perform a crossover. /// </summary> /// <param name="rnd">The random number generator.</param> private void PerformCrossover(Random rnd) { // first, find the correct number of optimal parents int parentsNeeded = _population.Config.MaxParents; var parents = new Genome[parentsNeeded]; int parentIndex = 0; int tries = 0; while (parentIndex < parentsNeeded) { // select a potential parent Genome parent = _population.TournamentForBest(rnd); bool goodParent = true; // do we already have this parent? for (int i = 0; i < parentIndex; i++) { if (parents[i] == parent) { goodParent = false; break; } } // add the parent if acceptable if (goodParent) { parents[parentIndex++] = parent; } // might get into an endless loop with a really small population, don't do that! tries++; if (tries > 10000) { throw new InvalidOperationException("Failed to find acceptable parenents for crossover."); } } // we now have a good batch of parents, so call the crossover operation Genome[] children = _population.Config.Model.Crossover(rnd, parents); // integrate any children into the population foreach (Genome child in children) { child.Score = _population.Config.Model.Score(child, _trainingInput, _trainingIdeal); _population.AddChildAndReplace(rnd, child); } }
public Genome[] Crossover(Random rnd, Genome[] parents) { return TypicalGAModel.Crossover(rnd, parents, _cutLength); }