{"id":4450,"date":"2021-03-02T18:00:34","date_gmt":"2021-03-02T18:00:34","guid":{"rendered":"https:\/\/www.aiproblog.com\/index.php\/2021\/03\/02\/simple-genetic-algorithm-from-scratch-in-python\/"},"modified":"2021-03-02T18:00:34","modified_gmt":"2021-03-02T18:00:34","slug":"simple-genetic-algorithm-from-scratch-in-python","status":"publish","type":"post","link":"https:\/\/www.aiproblog.com\/index.php\/2021\/03\/02\/simple-genetic-algorithm-from-scratch-in-python\/","title":{"rendered":"Simple Genetic Algorithm From Scratch in Python"},"content":{"rendered":"<p>Author: Jason Brownlee<\/p>\n<div>\n<p>The <strong>genetic algorithm<\/strong> is a stochastic global optimization algorithm.<\/p>\n<p>It may be one of the most popular and widely known biologically inspired algorithms, along with artificial neural networks.<\/p>\n<p>The algorithm is a type of evolutionary algorithm and performs an optimization procedure inspired by the biological theory of evolution by means of natural selection with a binary representation and simple operators based on genetic recombination and genetic mutations.<\/p>\n<p>In this tutorial, you will discover the genetic algorithm optimization algorithm.<\/p>\n<p>After completing this tutorial, you will know:<\/p>\n<ul>\n<li>Genetic algorithm is a stochastic optimization algorithm inspired by evolution.<\/li>\n<li>How to implement the genetic algorithm from scratch in Python.<\/li>\n<li>How to apply the genetic algorithm to a continuous objective function.<\/li>\n<\/ul>\n<p>Let\u2019s get started.<\/p>\n<div id=\"attachment_11855\" style=\"width: 810px\" class=\"wp-caption aligncenter\">\n<img decoding=\"async\" aria-describedby=\"caption-attachment-11855\" loading=\"lazy\" class=\"size-full wp-image-11855\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2021\/03\/Simple-Genetic-Algorithm-From-Scratch-in-Python.jpg\" alt=\"Simple Genetic Algorithm From Scratch in Python\" width=\"800\" height=\"547\" srcset=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2021\/03\/Simple-Genetic-Algorithm-From-Scratch-in-Python.jpg 800w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2021\/03\/Simple-Genetic-Algorithm-From-Scratch-in-Python-300x205.jpg 300w, https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2021\/03\/Simple-Genetic-Algorithm-From-Scratch-in-Python-768x525.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\"><\/p>\n<p id=\"caption-attachment-11855\" class=\"wp-caption-text\">Simple Genetic Algorithm From Scratch in Python<br \/>Photo by <a href=\"https:\/\/www.flickr.com\/photos\/magharebia\/5323128313\/\">Magharebia<\/a>, some rights reserved.<\/p>\n<\/div>\n<h2>Tutorial Overview<\/h2>\n<p>This tutorial is divided into four parts; they are:<\/p>\n<ol>\n<li>Genetic Algorithm<\/li>\n<li>Genetic Algorithm From Scratch<\/li>\n<li>Genetic Algorithm for OneMax<\/li>\n<li>Genetic Algorithm for Continuous Function Optimization<\/li>\n<\/ol>\n<h2>Genetic Algorithm<\/h2>\n<p>The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Genetic_algorithm\">Genetic Algorithm<\/a> is a stochastic global search optimization algorithm.<\/p>\n<p>It is inspired by the biological theory of evolution by means of natural selection. Specifically, the new synthesis that combines an understanding of genetics with the theory.<\/p>\n<blockquote>\n<p>Genetic algorithms (algorithm 9.4) borrow inspiration from biological evolution, where fitter individuals are more likely to pass on their genes to the next generation.<\/p>\n<\/blockquote>\n<p>\u2014 Page 148, <a href=\"https:\/\/amzn.to\/2Traqek\">Algorithms for Optimization<\/a>, 2019.<\/p>\n<p>The algorithm uses analogs of a genetic representation (bitstrings), fitness (function evaluations), genetic recombination (crossover of bitstrings), and mutation (flipping bits).<\/p>\n<p>The algorithm works by first creating a population of a fixed size of random bitstrings. The main loop of the algorithm is repeated for a fixed number of iterations or until no further improvement is seen in the best solution over a given number of iterations.<\/p>\n<p>One iteration of the algorithm is like an evolutionary generation.<\/p>\n<p>First, the population of bitstrings (candidate solutions) are evaluated using the objective function. The objective function evaluation for each candidate solution is taken as the fitness of the solution, which may be minimized or maximized.<\/p>\n<p>Then, parents are selected based on their fitness. A given candidate solution may be used as parent zero or more times. A simple and effective approach to selection involves drawing <em>k<\/em> candidates from the population randomly and selecting the member from the group with the best fitness. This is called tournament selection where <em>k<\/em> is a hyperparameter and set to a value such as 3. This simple approach simulates a more costly fitness-proportionate selection scheme.<\/p>\n<blockquote>\n<p>In tournament selection, each parent is the fittest out of k randomly chosen chromosomes of the population<\/p>\n<\/blockquote>\n<p>\u2014 Page 151, <a href=\"https:\/\/amzn.to\/2Traqek\">Algorithms for Optimization<\/a>, 2019.<\/p>\n<p>Parents are used as the basis for generating the next generation of candidate points and one parent for each position in the population is required.<\/p>\n<p>Parents are then taken in pairs and used to create two children. Recombination is performed using a crossover operator. This involves selecting a random split point on the bit string, then creating a child with the bits up to the split point from the first parent and from the split point to the end of the string from the second parent. This process is then inverted for the second child.<\/p>\n<p>For example the two parents:<\/p>\n<ul>\n<li>parent1 = 00000<\/li>\n<li>parent2 = 11111<\/li>\n<\/ul>\n<p>May result in two cross-over children:<\/p>\n<ul>\n<li>child1 = 00011<\/li>\n<li>child2 = 11100<\/li>\n<\/ul>\n<p>This is called one point crossover, and there are many other variations of the operator.<\/p>\n<p>Crossover is applied probabilistically for each pair of parents, meaning that in some cases, copies of the parents are taken as the children instead of the recombination operator. Crossover is controlled by a hyperparameter set to a large value, such as 80 percent or 90 percent.<\/p>\n<blockquote>\n<p>Crossover is the Genetic Algorithm\u2019s distinguishing feature. It involves mixing and matching parts of two parents to form children. How you do that mixing and matching depends on the representation of the individuals.<\/p>\n<\/blockquote>\n<p>\u2014 Page 36, <a href=\"https:\/\/amzn.to\/2HxZVn4\">Essentials of Metaheuristics<\/a>, 2011.<\/p>\n<p>Mutation involves flipping bits in created children candidate solutions. Typically, the mutation rate is set to <em>1\/L<\/em>, where <em>L<\/em> is the length of the bitstring.<\/p>\n<blockquote>\n<p>Each bit in a binary-valued chromosome typically has a small probability of being flipped. For a chromosome with m bits, this mutation rate is typically set to 1\/m, yielding an average of one mutation per child chromosome.<\/p>\n<\/blockquote>\n<p>\u2014 Page 155, <a href=\"https:\/\/amzn.to\/2Traqek\">Algorithms for Optimization<\/a>, 2019.<\/p>\n<p>For example, if a problem used a bitstring with 20 bits, then a good default mutation rate would be (1\/20) = 0.05 or a probability of 5 percent.<\/p>\n<p>This defines the simple genetic algorithm procedure. It is a large field of study, and there are many extensions to the algorithm.<\/p>\n<p>Now that we are familiar with the simple genetic algorithm procedure, let\u2019s look at how we might implement it from scratch.<\/p>\n<h2>Genetic Algorithm From Scratch<\/h2>\n<p>In this section, we will develop an implementation of the genetic algorithm.<\/p>\n<p>The first step is to create a population of random bitstrings. We could use boolean values <em>True<\/em> and <em>False<\/em>, string values \u20180\u2019 and \u20181\u2019, or integer values 0 and 1. In this case, we will use integer values.<\/p>\n<p>We can generate an array of integer values in a range using the <a href=\"https:\/\/numpy.org\/doc\/stable\/reference\/random\/generated\/numpy.random.randint.html\">randint() function<\/a>, and we can specify the range as values starting at 0 and less than 2, e.g. 0 or 1. We will also represent a candidate solution as a list instead of a NumPy array to keep things simple.<\/p>\n<p>An initial population of random bitstring can be created as follows, where \u201c<em>n_pop<\/em>\u201d is a hyperparameter that controls the population size and \u201c<em>n_bits<\/em>\u201d is a hyperparameter that defines the number of bits in a single candidate solution:<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# initial population of random bitstring\r\npop = [randint(0, 2, n_bits).tolist() for _ in range(n_pop)]<\/pre>\n<p>Next, we can enumerate over a fixed number of algorithm iterations, in this case, controlled by a hyperparameter named \u201c<em>n_iter<\/em>\u201c.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# enumerate generations\r\n\tfor gen in range(n_iter):\r\n\t\t...<\/pre>\n<p>The first step in the algorithm iteration is to evaluate all candidate solutions.<\/p>\n<p>We will use a function named <em>objective()<\/em> as a generic objective function and call it to get a fitness score, which we will minimize.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# evaluate all candidates in the population\r\nscores = [objective(c) for c in pop]<\/pre>\n<p>We can then select parents that will be used to create children.<\/p>\n<p>The tournament selection procedure can be implemented as a function that takes the population and returns one selected parent. The <em>k<\/em> value is fixed at 3 with a default argument, but you can experiment with different values if you like.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># tournament selection\r\ndef selection(pop, scores, k=3):\r\n\t# first random selection\r\n\tselection_ix = randint(len(pop))\r\n\tfor ix in randint(0, len(pop), k-1):\r\n\t\t# check if better (e.g. perform a tournament)\r\n\t\tif scores[ix] &lt; scores[selection_ix]:\r\n\t\t\tselection_ix = ix\r\n\treturn pop[selection_ix]<\/pre>\n<p>We can then call this function one time for each position in the population to create a list of parents.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# select parents\r\nselected = [selection(pop, scores) for _ in range(n_pop)]<\/pre>\n<p>We can then create the next generation.<\/p>\n<p>This first requires a function to perform crossover. This function will take two parents and the crossover rate. The crossover rate is a hyperparameter that determines whether crossover is performed or not, and if not, the parents are copied into the next generation. It is a probability and typically has a large value close to 1.0.<\/p>\n<p>The <em>crossover()<\/em> function below implements crossover using a draw of a random number in the range [0,1] to determine if crossover is performed, then selecting a valid split point if crossover is to be performed.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># crossover two parents to create two children\r\ndef crossover(p1, p2, r_cross):\r\n\t# children are copies of parents by default\r\n\tc1, c2 = p1.copy(), p2.copy()\r\n\t# check for recombination\r\n\tif rand() &lt; r_cross:\r\n\t\t# select crossover point that is not on the end of the string\r\n\t\tpt = randint(1, len(p1)-2)\r\n\t\t# perform crossover\r\n\t\tc1 = p1[:pt] + p2[pt:]\r\n\t\tc2 = p2[:pt] + p1[pt:]\r\n\treturn [c1, c2]<\/pre>\n<p>We also need a function to perform mutation.<\/p>\n<p>This procedure simply flips bits with a low probability controlled by the \u201c<em>r_mut<\/em>\u201d hyperparameter.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># mutation operator\r\ndef mutation(bitstring, r_mut):\r\n\tfor i in range(len(bitstring)):\r\n\t\t# check for a mutation\r\n\t\tif rand() &lt; r_mut:\r\n\t\t\t# flip the bit\r\n\t\t\tbitstring[i] = 1 - bitstring[i]<\/pre>\n<p>We can then loop over the list of parents and create a list of children to be used as the next generation, calling the crossover and mutation functions as needed.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# create the next generation\r\nchildren = list()\r\nfor i in range(0, n_pop, 2):\r\n\t# get selected parents in pairs\r\n\tp1, p2 = selected[i], selected[i+1]\r\n\t# crossover and mutation\r\n\tfor c in crossover(p1, p2, r_cross):\r\n\t\t# mutation\r\n\t\tmutation(c, r_mut)\r\n\t\t# store for next generation\r\n\t\tchildren.append(c)<\/pre>\n<p>We can tie all of this together into a function named <em>genetic_algorithm()<\/em> that takes the name of the objective function and the hyperparameters of the search, and returns the best solution found during the search.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># genetic algorithm\r\ndef genetic_algorithm(objective, n_bits, n_iter, n_pop, r_cross, r_mut):\r\n\t# initial population of random bitstring\r\n\tpop = [randint(0, 2, n_bits).tolist() for _ in range(n_pop)]\r\n\t# keep track of best solution\r\n\tbest, best_eval = 0, objective(pop[0])\r\n\t# enumerate generations\r\n\tfor gen in range(n_iter):\r\n\t\t# evaluate all candidates in the population\r\n\t\tscores = [objective(c) for c in pop]\r\n\t\t# check for new best solution\r\n\t\tfor i in range(n_pop):\r\n\t\t\tif scores[i] &lt; best_eval:\r\n\t\t\t\tbest, best_eval = pop[i], scores[i]\r\n\t\t\t\tprint(\"&gt;%d, new best f(%s) = %.3f\" % (gen,  pop[i], scores[i]))\r\n\t\t# select parents\r\n\t\tselected = [selection(pop, scores) for _ in range(n_pop)]\r\n\t\t# create the next generation\r\n\t\tchildren = list()\r\n\t\tfor i in range(0, n_pop, 2):\r\n\t\t\t# get selected parents in pairs\r\n\t\t\tp1, p2 = selected[i], selected[i+1]\r\n\t\t\t# crossover and mutation\r\n\t\t\tfor c in crossover(p1, p2, r_cross):\r\n\t\t\t\t# mutation\r\n\t\t\t\tmutation(c, r_mut)\r\n\t\t\t\t# store for next generation\r\n\t\t\t\tchildren.append(c)\r\n\t\t# replace population\r\n\t\tpop = children\r\n\treturn [best, best_eval]<\/pre>\n<p>Now that we have developed an implementation of the genetic algorithm, let\u2019s explore how we might apply it to an objective function.<\/p>\n<h2>Genetic Algorithm for OneMax<\/h2>\n<p>In this section, we will apply the genetic algorithm to a binary string-based optimization problem.<\/p>\n<p>The problem is called OneMax and evaluates a binary string based on the number of 1s in the string. For example, a bitstring with a length of 20 bits will have a score of 20 for a string of all 1s.<\/p>\n<p>Given we have implemented the genetic algorithm to minimize the objective function, we can add a negative sign to this evaluation so that large positive values become large negative values.<\/p>\n<p>The <em>onemax()<\/em> function below implements this and takes a bitstring of integer values as input and returns the negative sum of the values.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># objective function\r\ndef onemax(x):\r\n\treturn -sum(x)<\/pre>\n<p>Next, we can configure the search.<\/p>\n<p>The search will run for 100 iterations and we will use 20 bits in our candidate solutions, meaning the optimal fitness will be -20.0.<\/p>\n<p>The population size will be 100, and we will use a crossover rate of 90 percent and a mutation rate of 5 percent. This configuration was chosen after a little trial and error.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# define the total iterations\r\nn_iter = 100\r\n# bits\r\nn_bits = 20\r\n# define the population size\r\nn_pop = 100\r\n# crossover rate\r\nr_cross = 0.9\r\n# mutation rate\r\nr_mut = 1.0 \/ float(n_bits)<\/pre>\n<p>The search can then be called and the best result reported.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# perform the genetic algorithm search\r\nbest, score = genetic_algorithm(onemax, n_bits, n_iter, n_pop, r_cross, r_mut)\r\nprint('Done!')\r\nprint('f(%s) = %f' % (best, score))<\/pre>\n<p>Tying this together, the complete example of applying the genetic algorithm to the OneMax objective function is listed below.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># genetic algorithm search of the one max optimization problem\r\nfrom numpy.random import randint\r\nfrom numpy.random import rand\r\n\r\n# objective function\r\ndef onemax(x):\r\n\treturn -sum(x)\r\n\r\n# tournament selection\r\ndef selection(pop, scores, k=3):\r\n\t# first random selection\r\n\tselection_ix = randint(len(pop))\r\n\tfor ix in randint(0, len(pop), k-1):\r\n\t\t# check if better (e.g. perform a tournament)\r\n\t\tif scores[ix] &lt; scores[selection_ix]:\r\n\t\t\tselection_ix = ix\r\n\treturn pop[selection_ix]\r\n\r\n# crossover two parents to create two children\r\ndef crossover(p1, p2, r_cross):\r\n\t# children are copies of parents by default\r\n\tc1, c2 = p1.copy(), p2.copy()\r\n\t# check for recombination\r\n\tif rand() &lt; r_cross:\r\n\t\t# select crossover point that is not on the end of the string\r\n\t\tpt = randint(1, len(p1)-2)\r\n\t\t# perform crossover\r\n\t\tc1 = p1[:pt] + p2[pt:]\r\n\t\tc2 = p2[:pt] + p1[pt:]\r\n\treturn [c1, c2]\r\n\r\n# mutation operator\r\ndef mutation(bitstring, r_mut):\r\n\tfor i in range(len(bitstring)):\r\n\t\t# check for a mutation\r\n\t\tif rand() &lt; r_mut:\r\n\t\t\t# flip the bit\r\n\t\t\tbitstring[i] = 1 - bitstring[i]\r\n\r\n# genetic algorithm\r\ndef genetic_algorithm(objective, n_bits, n_iter, n_pop, r_cross, r_mut):\r\n\t# initial population of random bitstring\r\n\tpop = [randint(0, 2, n_bits).tolist() for _ in range(n_pop)]\r\n\t# keep track of best solution\r\n\tbest, best_eval = 0, objective(pop[0])\r\n\t# enumerate generations\r\n\tfor gen in range(n_iter):\r\n\t\t# evaluate all candidates in the population\r\n\t\tscores = [objective(c) for c in pop]\r\n\t\t# check for new best solution\r\n\t\tfor i in range(n_pop):\r\n\t\t\tif scores[i] &lt; best_eval:\r\n\t\t\t\tbest, best_eval = pop[i], scores[i]\r\n\t\t\t\tprint(\"&gt;%d, new best f(%s) = %.3f\" % (gen,  pop[i], scores[i]))\r\n\t\t# select parents\r\n\t\tselected = [selection(pop, scores) for _ in range(n_pop)]\r\n\t\t# create the next generation\r\n\t\tchildren = list()\r\n\t\tfor i in range(0, n_pop, 2):\r\n\t\t\t# get selected parents in pairs\r\n\t\t\tp1, p2 = selected[i], selected[i+1]\r\n\t\t\t# crossover and mutation\r\n\t\t\tfor c in crossover(p1, p2, r_cross):\r\n\t\t\t\t# mutation\r\n\t\t\t\tmutation(c, r_mut)\r\n\t\t\t\t# store for next generation\r\n\t\t\t\tchildren.append(c)\r\n\t\t# replace population\r\n\t\tpop = children\r\n\treturn [best, best_eval]\r\n\r\n# define the total iterations\r\nn_iter = 100\r\n# bits\r\nn_bits = 20\r\n# define the population size\r\nn_pop = 100\r\n# crossover rate\r\nr_cross = 0.9\r\n# mutation rate\r\nr_mut = 1.0 \/ float(n_bits)\r\n# perform the genetic algorithm search\r\nbest, score = genetic_algorithm(onemax, n_bits, n_iter, n_pop, r_cross, r_mut)\r\nprint('Done!')\r\nprint('f(%s) = %f' % (best, score))<\/pre>\n<p>Running the example will report the best result as it is found along the way, then the final best solution at the end of the search, which we would expect to be the optimal solution.<\/p>\n<p><strong>Note<\/strong>: Your <a href=\"https:\/\/machinelearningmastery.com\/different-results-each-time-in-machine-learning\/\">results may vary<\/a> given the stochastic nature of the algorithm or evaluation procedure, or differences in numerical precision. Consider running the example a few times and compare the average outcome.<\/p>\n<p>In this case, we can see that the search found the optimal solution after about eight generations.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">&gt;0, new best f([1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1]) = -14.000\r\n&gt;0, new best f([1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0]) = -15.000\r\n&gt;1, new best f([1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1]) = -16.000\r\n&gt;2, new best f([0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1]) = -17.000\r\n&gt;2, new best f([1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) = -19.000\r\n&gt;8, new best f([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) = -20.000\r\nDone!\r\nf([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) = -20.000000<\/pre>\n<\/p>\n<h2>Genetic Algorithm for Continuous Function Optimization<\/h2>\n<p>Optimizing the OneMax function is not very interesting; we are more likely to want to optimize a continuous function.<\/p>\n<p>For example, we can define the x^2 minimization function that takes input variables and has an optima at\u00a0 f(0, 0) = 0.0.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># objective function\r\ndef objective(x):\r\n\treturn x[0]**2.0 + x[1]**2.0<\/pre>\n<p>We can minimize this function with a genetic algorithm.<\/p>\n<p>First, we must define the bounds of each input variable.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# define range for input\r\nbounds = [[-5.0, 5.0], [-5.0, 5.0]]<\/pre>\n<p>We will take the \u201c<em>n_bits<\/em>\u201d hyperparameter as a number of bits per input variable to the objective function and set it to 16 bits.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# bits per variable\r\nn_bits = 16<\/pre>\n<p>This means our actual bit string will have (16 * 2) = 32 bits, given the two input variables.<\/p>\n<p>We must update our mutation rate accordingly.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# mutation rate\r\nr_mut = 1.0 \/ (float(n_bits) * len(bounds))<\/pre>\n<p>Next, we need to ensure that the initial population creates random bitstrings that are large enough.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# initial population of random bitstring\r\npop = [randint(0, 2, n_bits*len(bounds)).tolist() for _ in range(n_pop)]<\/pre>\n<p>Finally, we need to decode the bitstrings to numbers prior to evaluating each with the objective function.<\/p>\n<p>We can achieve this by first decoding each substring to an integer, then scaling the integer to the desired range. This will give a vector of values in the range that can then be provided to the objective function for evaluation.<\/p>\n<p>The <em>decode()<\/em> function below implements this, taking the bounds of the function, the number of bits per variable, and a bitstring as input and returns a list of decoded real values.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># decode bitstring to numbers\r\ndef decode(bounds, n_bits, bitstring):\r\n\tdecoded = list()\r\n\tlargest = 2**n_bits\r\n\tfor i in range(len(bounds)):\r\n\t\t# extract the substring\r\n\t\tstart, end = i * n_bits, (i * n_bits)+n_bits\r\n\t\tsubstring = bitstring[start:end]\r\n\t\t# convert bitstring to a string of chars\r\n\t\tchars = ''.join([str(s) for s in substring])\r\n\t\t# convert string to integer\r\n\t\tinteger = int(chars, 2)\r\n\t\t# scale integer to desired range\r\n\t\tvalue = bounds[i][0] + (integer\/largest) * (bounds[i][1] - bounds[i][0])\r\n\t\t# store\r\n\t\tdecoded.append(value)\r\n\treturn decoded<\/pre>\n<p>We can then call this at the beginning of the algorithm loop to decode the population, then evaluate the decoded version of the population.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">...\r\n# decode population\r\ndecoded = [decode(bounds, n_bits, p) for p in pop]\r\n# evaluate all candidates in the population\r\nscores = [objective(d) for d in decoded]<\/pre>\n<p>Tying this together, the complete example of the genetic algorithm for continuous function optimization is listed below.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\"># genetic algorithm search for continuous function optimization\r\nfrom numpy.random import randint\r\nfrom numpy.random import rand\r\n\r\n# objective function\r\ndef objective(x):\r\n\treturn x[0]**2.0 + x[1]**2.0\r\n\r\n# decode bitstring to numbers\r\ndef decode(bounds, n_bits, bitstring):\r\n\tdecoded = list()\r\n\tlargest = 2**n_bits\r\n\tfor i in range(len(bounds)):\r\n\t\t# extract the substring\r\n\t\tstart, end = i * n_bits, (i * n_bits)+n_bits\r\n\t\tsubstring = bitstring[start:end]\r\n\t\t# convert bitstring to a string of chars\r\n\t\tchars = ''.join([str(s) for s in substring])\r\n\t\t# convert string to integer\r\n\t\tinteger = int(chars, 2)\r\n\t\t# scale integer to desired range\r\n\t\tvalue = bounds[i][0] + (integer\/largest) * (bounds[i][1] - bounds[i][0])\r\n\t\t# store\r\n\t\tdecoded.append(value)\r\n\treturn decoded\r\n\r\n# tournament selection\r\ndef selection(pop, scores, k=3):\r\n\t# first random selection\r\n\tselection_ix = randint(len(pop))\r\n\tfor ix in randint(0, len(pop), k-1):\r\n\t\t# check if better (e.g. perform a tournament)\r\n\t\tif scores[ix] &lt; scores[selection_ix]:\r\n\t\t\tselection_ix = ix\r\n\treturn pop[selection_ix]\r\n\r\n# crossover two parents to create two children\r\ndef crossover(p1, p2, r_cross):\r\n\t# children are copies of parents by default\r\n\tc1, c2 = p1.copy(), p2.copy()\r\n\t# check for recombination\r\n\tif rand() &lt; r_cross:\r\n\t\t# select crossover point that is not on the end of the string\r\n\t\tpt = randint(1, len(p1)-2)\r\n\t\t# perform crossover\r\n\t\tc1 = p1[:pt] + p2[pt:]\r\n\t\tc2 = p2[:pt] + p1[pt:]\r\n\treturn [c1, c2]\r\n\r\n# mutation operator\r\ndef mutation(bitstring, r_mut):\r\n\tfor i in range(len(bitstring)):\r\n\t\t# check for a mutation\r\n\t\tif rand() &lt; r_mut:\r\n\t\t\t# flip the bit\r\n\t\t\tbitstring[i] = 1 - bitstring[i]\r\n\r\n# genetic algorithm\r\ndef genetic_algorithm(objective, bounds, n_bits, n_iter, n_pop, r_cross, r_mut):\r\n\t# initial population of random bitstring\r\n\tpop = [randint(0, 2, n_bits*len(bounds)).tolist() for _ in range(n_pop)]\r\n\t# keep track of best solution\r\n\tbest, best_eval = 0, objective(pop[0])\r\n\t# enumerate generations\r\n\tfor gen in range(n_iter):\r\n\t\t# decode population\r\n\t\tdecoded = [decode(bounds, n_bits, p) for p in pop]\r\n\t\t# evaluate all candidates in the population\r\n\t\tscores = [objective(d) for d in decoded]\r\n\t\t# check for new best solution\r\n\t\tfor i in range(n_pop):\r\n\t\t\tif scores[i] &lt; best_eval:\r\n\t\t\t\tbest, best_eval = pop[i], scores[i]\r\n\t\t\t\tprint(\"&gt;%d, new best f(%s) = %f\" % (gen,  decoded[i], scores[i]))\r\n\t\t# select parents\r\n\t\tselected = [selection(pop, scores) for _ in range(n_pop)]\r\n\t\t# create the next generation\r\n\t\tchildren = list()\r\n\t\tfor i in range(0, n_pop, 2):\r\n\t\t\t# get selected parents in pairs\r\n\t\t\tp1, p2 = selected[i], selected[i+1]\r\n\t\t\t# crossover and mutation\r\n\t\t\tfor c in crossover(p1, p2, r_cross):\r\n\t\t\t\t# mutation\r\n\t\t\t\tmutation(c, r_mut)\r\n\t\t\t\t# store for next generation\r\n\t\t\t\tchildren.append(c)\r\n\t\t# replace population\r\n\t\tpop = children\r\n\treturn [best, best_eval]\r\n\r\n# define range for input\r\nbounds = [[-5.0, 5.0], [-5.0, 5.0]]\r\n# define the total iterations\r\nn_iter = 100\r\n# bits per variable\r\nn_bits = 16\r\n# define the population size\r\nn_pop = 100\r\n# crossover rate\r\nr_cross = 0.9\r\n# mutation rate\r\nr_mut = 1.0 \/ (float(n_bits) * len(bounds))\r\n# perform the genetic algorithm search\r\nbest, score = genetic_algorithm(objective, bounds, n_bits, n_iter, n_pop, r_cross, r_mut)\r\nprint('Done!')\r\ndecoded = decode(bounds, n_bits, best)\r\nprint('f(%s) = %f' % (decoded, score))<\/pre>\n<p>Running the example reports the best decoded results along the way and the best decoded solution at the end of the run.<\/p>\n<p><strong>Note<\/strong>: Your <a href=\"https:\/\/machinelearningmastery.com\/different-results-each-time-in-machine-learning\/\">results may vary<\/a> given the stochastic nature of the algorithm or evaluation procedure, or differences in numerical precision. Consider running the example a few times and compare the average outcome.<\/p>\n<p>In this case, we can see that the algorithm discovers an input very close to f(0.0, 0.0) = 0.0.<\/p>\n<pre class=\"urvanov-syntax-highlighter-plain-tag\">&gt;0, new best f([-0.785064697265625, -0.807647705078125]) = 1.268621\r\n&gt;0, new best f([0.385894775390625, 0.342864990234375]) = 0.266471\r\n&gt;1, new best f([-0.342559814453125, -0.1068115234375]) = 0.128756\r\n&gt;2, new best f([-0.038909912109375, 0.30242919921875]) = 0.092977\r\n&gt;2, new best f([0.145721435546875, 0.1849365234375]) = 0.055436\r\n&gt;3, new best f([0.14404296875, -0.029754638671875]) = 0.021634\r\n&gt;5, new best f([0.066680908203125, 0.096435546875]) = 0.013746\r\n&gt;5, new best f([-0.036468505859375, -0.10711669921875]) = 0.012804\r\n&gt;6, new best f([-0.038909912109375, -0.099639892578125]) = 0.011442\r\n&gt;7, new best f([-0.033111572265625, 0.09674072265625]) = 0.010455\r\n&gt;7, new best f([-0.036468505859375, 0.05584716796875]) = 0.004449\r\n&gt;10, new best f([0.058746337890625, 0.008087158203125]) = 0.003517\r\n&gt;10, new best f([-0.031585693359375, 0.008087158203125]) = 0.001063\r\n&gt;12, new best f([0.022125244140625, 0.008087158203125]) = 0.000555\r\n&gt;13, new best f([0.022125244140625, 0.00701904296875]) = 0.000539\r\n&gt;13, new best f([-0.013885498046875, 0.008087158203125]) = 0.000258\r\n&gt;16, new best f([-0.011444091796875, 0.00518798828125]) = 0.000158\r\n&gt;17, new best f([-0.0115966796875, 0.00091552734375]) = 0.000135\r\n&gt;17, new best f([-0.004730224609375, 0.00335693359375]) = 0.000034\r\n&gt;20, new best f([-0.004425048828125, 0.00274658203125]) = 0.000027\r\n&gt;21, new best f([-0.002288818359375, 0.00091552734375]) = 0.000006\r\n&gt;22, new best f([-0.001983642578125, 0.00091552734375]) = 0.000005\r\n&gt;22, new best f([-0.001983642578125, 0.0006103515625]) = 0.000004\r\n&gt;24, new best f([-0.001373291015625, 0.001068115234375]) = 0.000003\r\n&gt;25, new best f([-0.001373291015625, 0.00091552734375]) = 0.000003\r\n&gt;26, new best f([-0.001373291015625, 0.0006103515625]) = 0.000002\r\n&gt;27, new best f([-0.001068115234375, 0.0006103515625]) = 0.000002\r\n&gt;29, new best f([-0.000152587890625, 0.00091552734375]) = 0.000001\r\n&gt;33, new best f([-0.0006103515625, 0.0]) = 0.000000\r\n&gt;34, new best f([-0.000152587890625, 0.00030517578125]) = 0.000000\r\n&gt;43, new best f([-0.00030517578125, 0.0]) = 0.000000\r\n&gt;60, new best f([-0.000152587890625, 0.000152587890625]) = 0.000000\r\n&gt;65, new best f([-0.000152587890625, 0.0]) = 0.000000\r\nDone!\r\nf([-0.000152587890625, 0.0]) = 0.000000<\/pre>\n<\/p>\n<h2>Further Reading<\/h2>\n<p>This section provides more resources on the topic if you are looking to go deeper.<\/p>\n<h3>Books<\/h3>\n<ul>\n<li>\n<a href=\"https:\/\/amzn.to\/3jADHgZ\">Genetic Algorithms in Search, Optimization, and Machine Learning<\/a>, 1989.<\/li>\n<li>\n<a href=\"https:\/\/amzn.to\/3kK8Osd\">An Introduction to Genetic Algorithms<\/a>, 1998.<\/li>\n<li>\n<a href=\"https:\/\/amzn.to\/2Traqek\">Algorithms for Optimization<\/a>, 2019.<\/li>\n<li>\n<a href=\"https:\/\/amzn.to\/2HxZVn4\">Essentials of Metaheuristics<\/a>, 2011.<\/li>\n<li>\n<a href=\"https:\/\/amzn.to\/2HzjbjV\">Computational Intelligence: An Introduction<\/a>, 2007.<\/li>\n<\/ul>\n<h3>API<\/h3>\n<ul>\n<li>\n<a href=\"https:\/\/numpy.org\/doc\/stable\/reference\/random\/generated\/numpy.random.randint.html\">numpy.random.randint API<\/a>.<\/li>\n<\/ul>\n<h3>Articles<\/h3>\n<ul>\n<li>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Genetic_algorithm\">Genetic algorithm, Wikipedia<\/a>.<\/li>\n<li>\n<a href=\"http:\/\/www.scholarpedia.org\/article\/Genetic_algorithms\">Genetic algorithms, Scholarpedia<\/a>.<\/li>\n<\/ul>\n<h2>Summary<\/h2>\n<p>In this tutorial, you discovered the genetic algorithm optimization.<\/p>\n<p>Specifically, you learned:<\/p>\n<ul>\n<li>Genetic algorithm is a stochastic optimization algorithm inspired by evolution.<\/li>\n<li>How to implement the genetic algorithm from scratch in Python.<\/li>\n<li>How to apply the genetic algorithm to a continuous objective function.<\/li>\n<\/ul>\n<p><strong>Do you have any questions?<\/strong><br \/>\nAsk your questions in the comments below and I will do my best to answer.<\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/simple-genetic-algorithm-from-scratch-in-python\/\">Simple Genetic Algorithm From Scratch in Python<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/\">Machine Learning Mastery<\/a>.<\/p>\n<\/div>\n<p><a href=\"https:\/\/machinelearningmastery.com\/simple-genetic-algorithm-from-scratch-in-python\/\">Go to Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Author: Jason Brownlee The genetic algorithm is a stochastic global optimization algorithm. It may be one of the most popular and widely known biologically inspired [&hellip;] <span class=\"read-more-link\"><a class=\"read-more\" href=\"https:\/\/www.aiproblog.com\/index.php\/2021\/03\/02\/simple-genetic-algorithm-from-scratch-in-python\/\">Read More<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":4451,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[24],"tags":[],"_links":{"self":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/4450"}],"collection":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/comments?post=4450"}],"version-history":[{"count":0,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/4450\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media\/4451"}],"wp:attachment":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media?parent=4450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/categories?post=4450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/tags?post=4450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}