2

I have a few questions about my genetic algorithm and GAs overall.

I have created a GA that when given points to a curve it tries to figure out what function produced this curve.

An example is the following Points

{{-2, 4},{-1, 1},{0, 0},{1, 1},{2, 4}}

Function

x^2

Sometimes I will give it points that will never produce a function, or will sometimes produce a function. It can even depend on how deep the initial trees are.

Some questions:

  • Why does the tree depth matter in trying to evaluate the points and produce a satisfactory function?
  • Why do I sometimes get a premature convergence and the GA never breaks out if the cycle?
  • What can I do to prevent a premature convergence?
  • What about annealing? How can I use it?

Can you take a quick look at my code and tell me if anything is obviously wrong with it? (This is test code, I need to do some code clean up.)

https://github.com/kevkid/GeneticAlgorithmTest

Source: http://www.gp-field-guide.org.uk/

EDIT: Looks like Thomas's suggestions worked well I get very fast results, and less premature convergence. I feel like increasing the Gene pool gives better results, but i am not exactly sure if it is actually getting better over every generation or if the fact that it is random allows it to find a correct solution.

EDIT 2: Following Thomas's suggestions I was able to get it work properly, seems like I had an issue with getting survivors, and expanding my gene pool. Also I recently added constants to my GA test if anyone else wants to look at it.

Kevin
  • 3,077
  • 6
  • 31
  • 77
  • @dcsohl fixed the issue – Kevin Jun 17 '15 at 13:06
  • Uhm correct me if I am wrong, but you can always construct such function by making the following sum - `y0*(x-x1)*(x-x2)*.../(x0-x1)*(x0-x2)*... + ...`. So no need for the GA. – Pavel Horal Jun 17 '15 at 13:31
  • Curious, what algorithm do you use to determine whether a given set of points fits a closed form function or not? For example, if the last 4 in your example is swapped for a 5, would your algorithm fail? – mohsenmadi Jun 17 '15 at 13:36
  • @PavelHoral right, but I am simply using this problem so I can understand GAs more. This is my first attempt at writing a GA. It would fail (or give an unsatisfactory answer). mohsenmadi I check the x values against the tree, which gives me a y value then I check that y against the known value. so if x=-2 and evaluating the tree at -2 gives me 4 this is the correct answer. The more correct answers it gives me the higher the fitness. – Kevin Jun 17 '15 at 13:39
  • 1
    Not all problems are solvable by GA... and this one does seem pretty hard. I can think of a good fitness function, however I am not able to think of any reasonable crossover function which is able to generate a better scoring solution. You should pick a different problem to learn GA. – Pavel Horal Jun 17 '15 at 13:49
  • @PavelHoral just to add: a standard problem for learning GA would be the travelling salesman problem. :) – Thomas Jun 17 '15 at 13:52
  • okay, I will check that out. Whats funny is that the book in my source mentions the problem I am trying to solve, and uses it as an example – Kevin Jun 17 '15 at 13:53
  • So if the book uses it as an example, doesn't it provide some more details? – Thomas Jun 17 '15 at 13:57
  • it provides detail, but its more graphical rather than an implementation. My issue revolves around why my GA sometimes will converge correctly and sometimes not. – Kevin Jun 17 '15 at 14:00
  • Well, a GA relies on random crossover which can result in very different solutions for the same input, depending on the settings (like how radical the mutations are, population size, fitness function, maximum number of generations) and thus it might randomly converge in the wrong place or not. – Thomas Jun 17 '15 at 14:07
  • @Thomas I guess the question is what I am seeing in my program a feature of GA's and not an error in programming? – Kevin Jun 17 '15 at 14:10
  • @Kevin yes, it seems not to be a programming issue but rather an issue with your algorithm itself or the settings you're using. – Thomas Jun 17 '15 at 15:00

2 Answers2

2

In order to avoid premature convergence you can also use multiple-subpopulations. Each sub-population will evolve independently. At the end of each generation you can exchange some individuals between subpopulations.

I did an implementation with multiple-subpopulations for a Genetic Programming variant: http://www.mepx.org/source_code.html

Mihai Oltean
  • 159
  • 1
  • 9
1

I don't have the time to dig into your code but I'll try to answer from what I remember on GAs:

Sometimes I will give it points that will never produce a function, or will sometimes produce a function. It can even depend on how deep the initial trees are.

I'm not sure what's the question here but if you need a result you could try and select the function that provides the least distance to the given points (could be sum, mean, number of points etc. depending on your needs).

Why does the tree depth matter in trying to evaluate the points and produce a satisfactory function?

I'm not sure what tree depth you mean but it could affect two things:

  • accuracy: i.e. the higher the depth the more accurate the solution might be or the more possibilities for mutations are given
  • performance: depending on what tree you mean a higher depth might increase performance (allowing for more educated guesses on the function) or decrease it (requiring more solutions to be generated and compared).

Why do I sometimes get a premature convergence and the GA never breaks out if the cycle?

That might be due to too little mutations. If you have a set of solutions that all converge around a local optimimum only slight mutations might not move the resulting solutions far enough from that local optimum in order to break out.

What can I do to prevent a premature convergence?

You could allow for bigger mutations, e.g. when solutions start to converge. Alternatively you could throw entirely new solutions into the mix (think of is as "immigration").

What about annealing? How can I use it?

Annealing could be used to gradually improve your solutions once they start to converge on a certain point/optimum, i.e. you'd improve the solutions in a more controlled way than "random" mutations.

You can also use it to break out of a local optimum depending on how those are distributed. As an example, you could use your GA until solutions start to converge, then use annealing and/or larger mutations and/or completely new solutions (you could generate several sets of solutions with different approaches and compare them at the end), create your new population and if the convergence is broken, start a new iteration with the GA. If the solutions still converge at the same optimum then you could stop since no bigger improvement is to be expected.

Besides all that, heuristic algorithms may still hit a local optimum but that's the tradeoff they provide: performance vs. accuracy.

Thomas
  • 87,414
  • 12
  • 119
  • 157
  • In response to your first point: sometimes I will give it an input such as points to x^4 and it will never give me an answer and will converge to an incorrect solution (not a solution at all). To your second point, the tree depth seems to give me a problem. When trying to find a solution to x^4 it may never find it with a tree depth of 10 (premature convergence to an unsatisfactory solution), but finds it in under 20 generations when the tree depth is 4. – Kevin Jun 17 '15 at 13:19
  • @Kevin Hmm, I'm still not sure what the tree depth signifies (if I understand your code correctly from the quick glance I had it is something like an AST) but if a higher depth promotes premature convergence then a lower depth might just either promote more radical mutations that allow the algorithm to break out of local optima or might make convergence on those more difficult. I'd assume that depending on the input a lower tree depth might actually also prevent the algorithm to converge on the global optimum, i.e. the approximation of the solution might be less accurate. – Thomas Jun 17 '15 at 14:02
  • so should the mutations be more radical? I am doing about 1% mutation rate. It takes a random tree and replaces a random point of that tree and generates a random subtree. I read a little on Premature Convergence, maybe my crossover needs to be more robust (an attempt at uniform crossover), right now it mates 2 parents and produces 2 children who then are passed back into the crossover function the shallowest tree node count times. I also set the population to a high number (10,000 children). – Kevin Jun 17 '15 at 14:09
  • @Kevin a robust crossover would result in children similar to the parents. If those a converge at the same place the children will do so as well. That's why you also introduce mutations, to push the solutions (or at least a couple of solutions) away from the point of convergence. The more radical that mutation (i.e. the harder the push) the easier it is for the mutated solutions to leave the local convergence space. As I said, you could use dynamic mutation rates, i.e. the higher the convergence the higher the mutation rate (or do it in discrete steps). – Thomas Jun 17 '15 at 14:17
  • Yes, that sounds good. An issue I noticed with mutations are their randomness. When the mutation is created it never go to the next generation because it was removed from the top trees. They seem to be low on the fitness scale, but I guess doing a dynamic mutation rate will give it more chance to produce better trees. – Kevin Jun 17 '15 at 14:20
  • @Kevin many implementations allow for less optimal solutions to go into the next generation though the ratio might vary. That's needed to prevent the behavior you're observing: the mutated children might seem less fit at first but might eventually converge at another optimum. For a different problem my next generation consisted of the following (in varying ratios): best parents (survivors), best children with minor or no mutations, best children with heavy mutations, entirely new solutions (immigration, effectively expanding the "gene pool"). – Thomas Jun 17 '15 at 15:06
  • I will keep this in mind while modifying my code. Putting mutations in the next population was something I was playing with, but thought about it and decided against it. – Kevin Jun 17 '15 at 15:08