los algoritmo genético es un algoritmo de optimización global estocástico.
Puede ser uno de los algoritmos de inspiración biológica más populares y más conocidos, junto con las redes neuronales artificiales.
El algoritmo es un tipo de algoritmo evolutivo y realiza un procedimiento de optimización inspirado en la teoría biológica de la evolución mediante selección natural con representación binaria y operadores simples basados en recombinación genética y mutaciones genéticas.
En este tutorial, descubrirá el algoritmo de optimización del algoritmo genético.
Después de completar este tutorial, sabrá:
- El algoritmo genético es un algoritmo de optimización estocástico inspirado en la evolución.
- Cómo implementar el algoritmo genético desde cero en Python.
- Cómo aplicar el algoritmo genético a una función objetiva continua.
Empecemos.
Descripción general del tutorial
Este tutorial se divide en cuatro partes; son:
- Algoritmo genético
- Algoritmo genético desde cero
- Algoritmo genético para OneMax
- Algoritmo genético para la optimización continua de funciones
Algoritmo genético
El algoritmo genético es un algoritmo estocástico de optimización de búsqueda global.
Está inspirado en la teoría biológica de la evolución por medio de la selección natural. Específicamente, la nueva síntesis que combina la comprensión de la genética con la teoría.
Los algoritmos genéticos (algoritmo 9.4) se inspiran en la evolución biológica, donde los individuos más aptos tienen más probabilidades de transmitir sus genes a la siguiente generación.
– Página 148, Algoritmos de optimización, 2019.
El algoritmo utiliza análogos de una representación genética (cadenas de bits), aptitud (evaluaciones de funciones), recombinación genética (cruce de cadenas de bits) y mutación (voltear bits).
El algoritmo funciona creando primero una población de un tamaño fijo de cadenas de bits aleatorias. El bucle principal del algoritmo se repite durante un número fijo de iteraciones o hasta que no se observe ninguna mejora adicional en la mejor solución en un número determinado de iteraciones.
Una iteración del algoritmo es como una generación evolutiva.
Primero, la población de cadenas de bits (soluciones candidatas) se evalúa utilizando la función objetivo. La evaluación de la función objetivo para cada solución candidata se toma como la adecuación de la solución, que puede minimizarse o maximizarse.
Luego, los padres se seleccionan en función de su estado físico. Una solución candidata dada se puede utilizar como padre cero o más veces. Un enfoque simple y efectivo para la selección implica dibujar k candidatos de la población de forma aleatoria y seleccionando al miembro del grupo con la mejor aptitud. A esto se le llama selección de torneo donde k es un hiperparámetro y se establece en un valor como 3. Este enfoque simple simula un esquema de selección proporcional a la aptitud más costoso.
En la selección del torneo, cada padre es el más apto de los k cromosomas elegidos al azar de la población.
– Página 151, Algoritmos de optimización, 2019.
Los padres se utilizan como base para generar la próxima generación de puntos candidatos y se requiere un padre para cada puesto en la población.
Luego, los padres se toman en parejas y se utilizan para crear dos hijos. La recombinación se realiza mediante un operador de cruce. Esto implica seleccionar un punto de división aleatorio en la cadena de bits, luego crear un hijo con los bits hasta el punto de división del primer padre y desde el punto de división hasta el final de la cadena del segundo padre. Este proceso luego se invierte para el segundo hijo.
Por ejemplo los dos padres:
- padre1 = 00000
- parent2 = 11111
Puede resultar en dos hijos cruzados:
- niño1 = 00011
- niño2 = 11100
Esto se llama cruce de un punto y hay muchas otras variaciones del operador.
El cruce se aplica de manera probabilística para cada par de padres, lo que significa que, en algunos casos, las copias de los padres se toman como hijos en lugar del operador de recombinación. El cruce se controla mediante un hiperparámetro establecido en un valor grande, como 80 por ciento o 90 por ciento.
Crossover es la característica distintiva del algoritmo genético. Implica mezclar y combinar partes de dos padres para formar hijos. Cómo se hace esa mezcla y combinación depende de la representación de los individuos.
– Página 36, Fundamentos de metaheurística, 2011.
La mutación implica invertir bits en soluciones candidatas para niños creados. Normalmente, la tasa de mutación se establece en 1 / L, dónde L es la longitud de la cadena de bits.
Cada bit de un cromosoma con valor binario normalmente tiene una pequeña probabilidad de ser invertido. Para un cromosoma con m bits, esta tasa de mutación generalmente se establece en 1 / m, lo que produce un promedio de una mutación por cromosoma infantil.
– Página 155, Algoritmos de optimización, 2019.
Por ejemplo, si un problema utiliza una cadena de bits con 20 bits, entonces una buena tasa de mutación predeterminada sería (1/20) = 0.05 o una probabilidad del 5 por ciento.
Esto define el procedimiento de algoritmo genético simple. Es un campo de estudio extenso y existen muchas extensiones para el algoritmo.
Ahora que estamos familiarizados con el procedimiento de algoritmo genético simple, veamos cómo podríamos implementarlo desde cero.
Algoritmo genético desde cero
En esta sección, desarrollaremos una implementación del algoritmo genético.
El primer paso es crear una población de cadenas de bits aleatorias. Podríamos usar valores booleanos Cierto y Falso, valores de cadena «0» y «1», o valores enteros 0 y 1. En este caso, usaremos valores enteros.
Podemos generar una matriz de valores enteros en un rango usando la función randint (), y podemos especificar el rango como valores que comienzan en 0 y menos de 2, p. Ej. 0 o 1. También representaremos una solución candidata como una lista en lugar de una matriz NumPy para simplificar las cosas.
Se puede crear una población inicial de cadena de bits aleatoria de la siguiente manera, donde «n_pop«Es un hiperparámetro que controla el tamaño de la población y»n_bits”Es un hiperparámetro que define la cantidad de bits en una única solución candidata:
... # población inicial de cadena de bits aleatoria música pop = [[randint(0, 2, n_bits).Listar() por _ en abarcar(n_pop)] |
A continuación, podemos enumerar un número fijo de iteraciones del algoritmo, en este caso, controladas por un hiperparámetro llamado «nitro“.
... # enumerar generaciones por gen en abarcar(nitro): ... |
El primer paso en la iteración del algoritmo es evaluar todas las soluciones candidatas.
Usaremos una función llamada objetivo() como función objetivo genérica y la llamaremos para obtener una puntuación de aptitud, que minimizaremos.
... # evaluar a todos los candidatos de la población puntuaciones = [[objetivo(C) por C en música pop] |
A continuación, podemos seleccionar los padres que se utilizarán para crear hijos.
El procedimiento de selección del torneo se puede implementar como una función que toma la población y devuelve un padre seleccionado. los k El valor se fija en 3 con un argumento predeterminado, pero puede experimentar con diferentes valores si lo desea.
# selección de torneos def selección(música pop, puntuaciones, k=3): # primera selección aleatoria selection_ix = randint(len(música pop)) por ix en randint(0, len(música pop), k–1): # comprobar si es mejor (por ejemplo, realizar un torneo) si puntuaciones[[ix] < puntuaciones[[selection_ix]: selection_ix = ix regreso música pop[[selection_ix] |
Entonces podemos llamar a esta función una vez para cada posición en la población para crear una lista de padres.
... # padres seleccionados seleccionado = [[selección(música pop, puntuaciones) por _ en abarcar(n_pop)] |
Entonces podemos crear la próxima generación.
Esto primero requiere una función para realizar el cruce. Esta función tomará dos padres y la tasa de cruce. La tasa de cruce es un hiperparámetro que determina si el cruce se realiza o no, y si no, los padres se copian en la siguiente generación. Es una probabilidad y normalmente tiene un valor grande cercano a 1.0.
los Transversal() la función a continuación implementa el cruce usando un sorteo de un número aleatorio en el rango [0,1] para determinar si se realiza el cruce, luego seleccione un punto de división válido si se va a realizar el cruce.
# cruce de dos padres para crear dos hijos def Transversal(p1, p2, r_cross): # los niños son copias de los padres de forma predeterminada c1, c2 = p1.Copiar(), p2.Copiar() # comprobar la recombinación si rand() < r_cross: # seleccione un punto de cruce que no esté al final de la cadena pt = randint(1, len(p1)–2) # realizar crossover c1 = p1[[:pt] + p2[[pt:] c2 = p2[[:pt] + p1[[pt:] regreso [[c1, c2] |
También necesitamos una función para realizar la mutación.
Este procedimiento simplemente invierte bits con una probabilidad baja controlada por el «r_mut”Hiperparámetro.
# operador de mutación def mutación(cadena de bits, r_mut): por I en abarcar(len(cadena de bits)): # comprobar si hay una mutación si rand() < r_mut: # voltea el bit cadena de bits[[I] = 1 – cadena de bits[[I] |
A continuación, podemos recorrer la lista de padres y crear una lista de hijos que se utilizarán como la próxima generación, llamando a las funciones de cruce y mutación según sea necesario.
... # crea la próxima generación niños = lista() por I en abarcar(0, n_pop, 2): # conseguir padres seleccionados en parejas p1, p2 = seleccionado[[I], seleccionado[[I+1] # crossover y mutación por C en Transversal(p1, p2, r_cross): # mutación mutación(C, r_mut) # tienda para la próxima generación niños.adjuntar(C) |
Podemos unir todo esto en una función llamada algoritmo genético() que toma el nombre de la función objetivo y los hiperparámetros de la búsqueda, y devuelve la mejor solución encontrada durante la búsqueda.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dieciséis 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# algoritmo genético def algoritmo genético(objetivo, n_bits, nitro, n_pop, r_cross, r_mut): # población inicial de cadena de bits aleatoria música pop = [[randint(0, 2, n_bits).Listar() por _ en abarcar(n_pop)] # realizar un seguimiento de la mejor solución mejor, best_eval = 0, objetivo(música pop[[0]) # enumerar generaciones por gen en abarcar(nitro): # evaluar a todos los candidatos de la población puntuaciones = [[objetivo(C) por C en música pop] # busque la mejor solución nueva por I en abarcar(n_pop): si puntuaciones[[I] < best_eval: mejor, best_eval = música pop[[I], puntuaciones[[I] imprimir(«>% d, nuevo mejor f (% s) =% .3f» % (gen, música pop[[I], puntuaciones[[I])) # padres seleccionados seleccionado = [[selección(música pop, puntuaciones) por _ en abarcar(n_pop)] # crea la próxima generación niños = lista() por I en abarcar(0, n_pop, 2): # conseguir padres seleccionados en parejas p1, p2 = seleccionado[[I], seleccionado[[I+1] # crossover y mutación por C en Transversal(p1, p2, r_cross): # mutación mutación(C, r_mut) # tienda para la próxima generación niños.adjuntar(C) # reemplazar población música pop = niños regreso [[mejor, best_eval] |
Ahora que hemos desarrollado una implementación del algoritmo genético, exploremos cómo podríamos aplicarlo a una función objetivo.
Algoritmo genético para OneMax
En esta sección, aplicaremos el algoritmo genético a un problema de optimización basado en cadenas binarias.
El problema se llama OneMax y evalúa una cadena binaria en función del número de unos en la cadena. Por ejemplo, una cadena de bits con una longitud de 20 bits tendrá una puntuación de 20 para una cadena de todos unos.
Dado que hemos implementado el algoritmo genético para minimizar la función objetivo, podemos agregar un signo negativo a esta evaluación para que los valores positivos grandes se conviertan en valores negativos grandes.
los onemax () La función siguiente implementa esto y toma una cadena de bits de valores enteros como entrada y devuelve la suma negativa de los valores.
# función objetiva def onemax(X): regreso –suma(X) |
A continuación, podemos configurar la búsqueda.
La búsqueda se ejecutará durante 100 iteraciones y usaremos 20 bits en nuestras soluciones candidatas, lo que significa que la aptitud óptima será -20.0.
El tamaño de la población será 100 y usaremos una tasa de cruce del 90 por ciento y una tasa de mutación del 5 por ciento. Esta configuración se eligió después de un poco de prueba y error.
... # definir las iteraciones totales nitro = 100 # bits n_bits = 20 # definir el tamaño de la población n_pop = 100 # tasa de cruce r_cross = 0,9 # tasa de mutación r_mut = 1.0 / flotador(n_bits) |
A continuación, se puede llamar a la búsqueda y notificar el mejor resultado.
... # realizar la búsqueda del algoritmo genético mejor, puntaje = algoritmo genético(onemax, n_bits, nitro, n_pop, r_cross, r_mut) imprimir(‘¡Hecho!’) imprimir(‘f (% s) =% f’ % (mejor, puntaje)) |
Al unir esto, el ejemplo completo de la aplicación del algoritmo genético a la función objetivo de OneMax se enumera a continuación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dieciséis 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 sesenta y cinco 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# búsqueda de algoritmo genético del único problema de optimización máxima desde numpy.aleatorio importar randint desde numpy.aleatorio importar rand # función objetiva def onemax(X): regreso –suma(X) # selección de torneos def selección(música pop, puntuaciones, k=3): # primera selección aleatoria selection_ix = randint(len(música pop)) por ix en randint(0, len(música pop), k–1): # comprobar si es mejor (por ejemplo, realizar un torneo) si puntuaciones[[ix] < puntuaciones[[selection_ix]: selection_ix = ix regreso música pop[[selection_ix] # cruce de dos padres para crear dos hijos def Transversal(p1, p2, r_cross): # los niños son copias de los padres de forma predeterminada c1, c2 = p1.Copiar(), p2.Copiar() # comprobar la recombinación si rand() < r_cross: # seleccione un punto de cruce que no esté al final de la cadena pt = randint(1, len(p1)–2) # realizar crossover c1 = p1[[:pt] + p2[[pt:] c2 = p2[[:pt] + p1[[pt:] regreso [[c1, c2] # operador de mutación def mutación(cadena de bits, r_mut): por I en abarcar(len(cadena de bits)): # comprobar si hay una mutación si rand() < r_mut: # voltea el bit cadena de bits[[I] = 1 – cadena de bits[[I] # algoritmo genético def algoritmo genético(objetivo, n_bits, nitro, n_pop, r_cross, r_mut): # población inicial de cadena de bits aleatoria música pop = [[randint(0, 2, n_bits).Listar() por _ en abarcar(n_pop)] # realizar un seguimiento de la mejor solución mejor, best_eval = 0, objetivo(música pop[[0]) # enumerar generaciones por gen en abarcar(nitro): # evaluar a todos los candidatos de la población puntuaciones = [[objetivo(C) por C en música pop] # busque la mejor solución nueva por I en abarcar(n_pop): si puntuaciones[[I] < best_eval: mejor, best_eval = música pop[[I], puntuaciones[[I] imprimir(«>% d, nuevo mejor f (% s) =% .3f» % (gen, música pop[[I], puntuaciones[[I])) # padres seleccionados seleccionado = [[selección(música pop, puntuaciones) por _ en abarcar(n_pop)] # crea la próxima generación niños = lista() por I en abarcar(0, n_pop, 2): # conseguir padres seleccionados en parejas p1, p2 = seleccionado[[I], seleccionado[[I+1] # crossover y mutación por C en Transversal(p1, p2, r_cross): # mutación mutación(C, r_mut) # tienda para la próxima generación niños.adjuntar(C) # reemplazar población música pop = niños regreso [[mejor, best_eval] # definir las iteraciones totales nitro = 100 # bits n_bits = 20 # definir el tamaño de la población n_pop = 100 # tasa de cruce r_cross = 0,9 # tasa de mutación r_mut = 1.0 / flotador(n_bits) # realizar la búsqueda del algoritmo genético mejor, puntaje = algoritmo genético(onemax, n_bits, nitro, n_pop, r_cross, r_mut) imprimir(‘¡Hecho!’) imprimir(‘f (% s) =% f’ % (mejor, puntaje)) |
Ejecutar el ejemplo informará el mejor resultado a medida que se encuentre en el camino, luego la mejor solución final al final de la búsqueda, que esperaríamos que sea la solución óptima.
Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o las diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y compare el resultado promedio.
En este caso, podemos ver que la búsqueda encontró la solución óptima después de aproximadamente ocho generaciones.
> 0, nuevo mejor f ([1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1]) = -14.000 > 0, nuevo mejor f ([1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0]) = -15.000 > 1, nuevo mejor f ([1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1]) = -16.000 > 2, nuevo mejor f ([0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1]) = -17.000 > 2, nuevo mejor f ([1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) = -19.000 > 8, nuevo mejor f ([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) = -20.000 ¡Hecho! F([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) = -20,000000 |
Algoritmo genético para la optimización continua de funciones
Optimizar la función OneMax no es muy interesante; es más probable que deseemos optimizar una función continua.
Por ejemplo, podemos definir la función de minimización x ^ 2 que toma variables de entrada y tiene un óptimo en f (0, 0) = 0.0.
# función objetiva def objetivo(X): regreso X[[0]**2.0 + X[[1]**2.0 |
Podemos minimizar esta función con un algoritmo genético.
Primero, debemos definir los límites de cada variable de entrada.
... # definir rango para entrada límites = [[[[–5,0, 5,0], [[–5,0, 5,0]] |
Tomaremos el «n_bits” hyperparameter as a number of bits per input variable to the objective function and set it to 16 bits.
... # bits per variable n_bits = dieciséis |
This means our actual bit string will have (16 * 2) = 32 bits, given the two input variables.
We must update our mutation rate accordingly.
... # mutation rate r_mut = 1.0 / (float(n_bits) * len(bounds)) |
Next, we need to ensure that the initial population creates random bitstrings that are large enough.
... # initial population of random bitstring pop = [[randint(0, 2, n_bits*len(bounds)).tolist() for _ in range(n_pop)] |
Finally, we need to decode the bitstrings to numbers prior to evaluating each with the objective function.
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.
los decode() 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dieciséis 17 |
# decode bitstring to numbers def decode(bounds, n_bits, bitstring): decoded = list() largest = 2**n_bits for i in range(len(bounds)): # extract the substring start, end = i * n_bits, (i * n_bits)+n_bits substring = bitstring[[start:end] # convert bitstring to a string of chars chars = ».join([[str(s) for s in substring]) # convert string to integer integer = int(chars, 2) # scale integer to desired range value = bounds[[i][[0] + (integer/largest) * (bounds[[i][[1] – bounds[[i][[0]) # store decoded.append(value) return decoded |
We can then call this at the beginning of the algorithm loop to decode the population, then evaluate the decoded version of the population.
... # decode population decoded = [[decode(bounds, n_bits, p) for p in pop] # evaluate all candidates in the population scores = [[objective(d) for d in decoded] |
Tying this together, the complete example of the genetic algorithm for continuous function optimization is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dieciséis 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 sesenta y cinco 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 |
# genetic algorithm search for continuous function optimization from numpy.random import randint from numpy.random import rand # objective function def objective(X): return X[[0]**2.0 + X[[1]**2.0 # decode bitstring to numbers def decode(bounds, n_bits, bitstring): decoded = list() largest = 2**n_bits for i in range(len(bounds)): # extract the substring start, end = i * n_bits, (i * n_bits)+n_bits substring = bitstring[[start:end] # convert bitstring to a string of chars chars = ».join([[str(s) for s in substring]) # convert string to integer integer = int(chars, 2) # scale integer to desired range value = bounds[[i][[0] + (integer/largest) * (bounds[[i][[1] – bounds[[i][[0]) # store decoded.append(value) return decoded # tournament selection def selection(pop, scores, k=3): # first random selection selection_ix = randint(len(pop)) for ix in randint(0, len(pop), k–1): # check if better (e.g. perform a tournament) if scores[[ix] < scores[[selection_ix]: selection_ix = ix return pop[[selection_ix] # crossover two parents to create two children def crossover(p1, p2, r_cross): # children are copies of parents by default c1, c2 = p1.copy(), p2.copy() # check for recombination if rand() < r_cross: # select crossover point that is not on the end of the string pt = randint(1, len(p1)–2) # perform crossover c1 = p1[[:pt] + p2[[pt:] c2 = p2[[:pt] + p1[[pt:] return [[c1, c2] # mutation operator def mutation(bitstring, r_mut): for i in range(len(bitstring)): # check for a mutation if rand() < r_mut: # flip the bit bitstring[[i] = 1 – bitstring[[i] # genetic algorithm def genetic_algorithm(objective, bounds, n_bits, n_iter, n_pop, r_cross, r_mut): # initial population of random bitstring pop = [[randint(0, 2, n_bits*len(bounds)).tolist() for _ in range(n_pop)] # keep track of best solution best, best_eval = 0, objective(pop[[0]) # enumerate generations for gen in range(n_iter): # decode population decoded = [[decode(bounds, n_bits, p) for p in pop] # evaluate all candidates in the population scores = [[objective(d) for d in decoded] # check for new best solution for i in range(n_pop): if scores[[i] < best_eval: best, best_eval = pop[[i], scores[[i] imprimir(«>%d, new best f(%s) = %f» % (gen, decoded[[i], scores[[i])) # select parents seleccionado = [[selection(pop, scores) for _ in range(n_pop)] # create the next generation niños = list() for i in range(0, n_pop, 2): # get selected parents in pairs p1, p2 = seleccionado[[i], seleccionado[[i+1] # crossover and mutation for c in crossover(p1, p2, r_cross): # mutation mutation(c, r_mut) # store for next generation niños.append(c) # replace population pop = niños return [[best, best_eval] # define range for input bounds = [[[[–5.0, 5.0], [[–5.0, 5.0]] # define the total iterations n_iter = 100 # bits per variable n_bits = dieciséis # define the population size n_pop = 100 # crossover rate r_cross = 0.9 # mutation rate r_mut = 1.0 / (float(n_bits) * len(bounds)) # perform the genetic algorithm search best, score = genetic_algorithm(objective, bounds, n_bits, n_iter, n_pop, r_cross, r_mut) imprimir(‘Done!’) decoded = decode(bounds, n_bits, best) imprimir(‘f(%s) = %f’ % (decoded, score)) |
Running the example reports the best decoded results along the way and the best decoded solution at the end of the run.
Note: Your results may vary 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.
In this case, we can see that the algorithm discovers an input very close to f(0.0, 0.0) = 0.0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dieciséis 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
>0, new best f([-0.785064697265625, -0.807647705078125]) = 1.268621 >0, new best f([0.385894775390625, 0.342864990234375]) = 0.266471 >1, new best f([-0.342559814453125, -0.1068115234375]) = 0.128756 >2, new best f([-0.038909912109375, 0.30242919921875]) = 0.092977 >2, new best f([0.145721435546875, 0.1849365234375]) = 0.055436 >3, new best f([0.14404296875, -0.029754638671875]) = 0.021634 >5, new best f([0.066680908203125, 0.096435546875]) = 0.013746 >5, new best f([-0.036468505859375, -0.10711669921875]) = 0.012804 >6, new best f([-0.038909912109375, -0.099639892578125]) = 0.011442 >7, new best f([-0.033111572265625, 0.09674072265625]) = 0.010455 >7, new best f([-0.036468505859375, 0.05584716796875]) = 0.004449 >10, new best f([0.058746337890625, 0.008087158203125]) = 0.003517 >10, new best f([-0.031585693359375, 0.008087158203125]) = 0.001063 >12, new best f([0.022125244140625, 0.008087158203125]) = 0.000555 >13, new best f([0.022125244140625, 0.00701904296875]) = 0.000539 >13, new best f([-0.013885498046875, 0.008087158203125]) = 0.000258 >16, new best f([-0.011444091796875, 0.00518798828125]) = 0.000158 >17, new best f([-0.0115966796875, 0.00091552734375]) = 0.000135 >17, new best f([-0.004730224609375, 0.00335693359375]) = 0.000034 >20, new best f([-0.004425048828125, 0.00274658203125]) = 0.000027 >21, new best f([-0.002288818359375, 0.00091552734375]) = 0.000006 >22, new best f([-0.001983642578125, 0.00091552734375]) = 0.000005 >22, new best f([-0.001983642578125, 0.0006103515625]) = 0.000004 >24, new best f([-0.001373291015625, 0.001068115234375]) = 0.000003 >25, new best f([-0.001373291015625, 0.00091552734375]) = 0.000003 >26, new best f([-0.001373291015625, 0.0006103515625]) = 0.000002 >27, new best f([-0.001068115234375, 0.0006103515625]) = 0.000002 >29, new best f([-0.000152587890625, 0.00091552734375]) = 0.000001 >33, new best f([-0.0006103515625, 0.0]) = 0.000000 >34, new best f([-0.000152587890625, 0.00030517578125]) = 0.000000 >43, new best f([-0.00030517578125, 0.0]) = 0.000000 >60, new best f([-0.000152587890625, 0.000152587890625]) = 0.000000 >65, new best f([-0.000152587890625, 0.0]) = 0.000000 Done! f([-0.000152587890625, 0.0]) = 0.000000 |
Further Reading
This section provides more resources on the topic if you are looking to go deeper.
Books
API
Artículos
Resumen
In this tutorial, you discovered the genetic algorithm optimization.
Specifically, you learned:
- Genetic algorithm is a stochastic optimization algorithm inspired by evolution.
- How to implement the genetic algorithm from scratch in Python.
- How to apply the genetic algorithm to a continuous objective function.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.