La programación genética es una técnica de inteligencia artificial en la que se utilizan algoritmos evolutivos para generar automáticamente programas informáticos. Basándose en los principios de selección natural y genética, una población de programas se somete a operaciones como el cruce, la mutación y la selección para evolucionar y mejorar con el tiempo. Esta técnica es parte de la “computación evolutiva”, una rama de la inteligencia artificial que utiliza algoritmos basados en la evolución natural para resolver una amplia gama de problemas. Su importancia radica en su capacidad para resolver problemas de alta complejidad y para explorar soluciones que podrían no ser inmediatamente evidentes para los humanos.
- ¿Cómo Funciona La Programación Genética?
- Aplicaciones Destacables De La Programación Genética
- Ventajas & Desventajas De La Programación Genética
- El Futuro De La Programación Genética
- Conclusión
El objetivo final de la programación genética es generar automáticamente programas que puedan resolver un problema en concreto. Esto se logra evaluando un conjunto de programas generados previamente para determinar cuál o cuáles son los más adecuados para la resolución del problema. La selección se utiliza para crear la próxima generación de programas, que también se evaluarán y seleccionarán si es pertinente, repitiendo el proceso tantas veces como sea necesario. La programación genética se puede utilizar para generar programas basados en lenguajes como Java o Python, y la complejidad de estos dependerá de la complejidad del problema y de las restricciones presentes en el espacio de producción.
¿Cómo Funciona La Programación Genética?
La programación genética implica el uso de un conjunto de operaciones genéticas para hacer evolucionar una población de programas a lo largo del tiempo. Los principales pasos en el proceso son:
1. Inicialización
Se genera e inicializa -de manera aleatoria- un conjunto (o población) de programas denominados “cromosomas” – término tomado de la biología que aquí se refiere a una entidad que porta información para resolver el problema. Estos cromosomas se representan de diversas formas como estructuras de árbol, cadenas carbonadas o redes.
2. Evaluación
Cada programa que compone la población se evalúa, con el fin de determinar si es idóneo para la resolución de la tarea o problema planteado. Esta evaluación se realiza de múltiples formas; si por ejemplo el objetivo es optimizar el rendimiento de un algoritmo de aprendizaje automático, se evaluará en base a la precisión de las predicciones expuestas por dicho algoritmo.
3. Selección
Los programas más válidos para la función requerida se seleccionan según el puntuaje obtenido en la fase de evaluación. Hay una variedad de formas de optimizar este proceso:
- Seleccionando aleatoriamente los programas y eligiendo el más apto.
- Estableciendo la probabilidad de que sea seleccionado mediante la puntuación obtenida en la evaluación.
4. Gestión & Manipulación
Los programas seleccionados se someten a procesos de cruce, mutación y reproducción para crear una nueva generación de programas. Los procesos consisten en lo siguiente:
- En el cruce, se toman dos programas y se combinan para crear uno nuevo.
- En la mutación se realizan cambios en el código del programa, generalmente mediante la alteración de valores.
- En la reproducción, se utilizan los programas más aptos para generar los programas de la nueva generación.
Estas operaciones genéticas permiten la introducción de nuevas ideas y soluciones en la población, manteniendo al mismo tiempo un cierto grado de diversidad.
Además, para que la población no se vuelva excesivamente homogénea -es decir, que sea diversa- se hace uso de las siguientes técnicas:
- Elitismo: donde solamente se preservan los programas más aptos de la población.
- Preservación de nichos: donde se mantiene -de manera forzada y no siempre conveniente- la diversidad entre el conjunto de programas.
5. Repetición
El proceso se repite continuamente hasta encontrar una solución satisfactoria o alcanzar el número de iteraciones deseado. A través de la repetición, la población evolucionará y mejorará progresivamente.
Ejemplo Reproducible Con Python
El siguiente código configura y ejecuta una evolución genética, intentando encontrar una ecuación que se evalúe al número objetivo. Utiliza operaciones matemáticas básicas y constantes aleatorias, evolucionando la población durante 40 generaciones con una población inicial de 300 individuos.
Cada individuo es una ecuación, y se seleccionan y combinan según su aptitud, que se mide por la diferencia entre el valor evaluado de la ecuación y el número objetivo. Las ecuaciones que estén más cerca del objetivo serán más aptas.
# Instalamos DEAP
# https://deap.readthedocs.io/en/master/
# En caso de no usar notebooks, eliminar '!'
!pip install deap
import operator
import math
import random
import numpy
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
from deap import gp
def protectedDiv(left, right):
"""
Función que realiza una división segura.
Inputs:
- left: Dividendo (float).
- right: Divisor (float).
Output:
- Resultado de la división, o 1 si el divisor es 0 (float).
"""
try:
return left / right
except ZeroDivisionError:
return 1
# Definimos un conjunto primitivo de operaciones que se pueden usar para construir la ecuación.
pset = gp.PrimitiveSet("MAIN", 1)
pset.addPrimitive(operator.add, 2)
pset.addPrimitive(operator.sub, 2)
pset.addPrimitive(operator.mul, 2)
pset.addPrimitive(protectedDiv, 2)
pset.addPrimitive(operator.neg, 1)
pset.addPrimitive(math.cos, 1)
pset.addPrimitive(math.sin, 1)
pset.addEphemeralConstant("rand101", lambda: random.randint(-1,1))
pset.renameArguments(ARG0='x')
# Definimos el tipo de individuo y la función de fitness.
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)
toolbox = base.Toolbox()
toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.expr)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("compile", gp.compile, pset=pset)
def evalSymbReg(individual, points):
"""
Función que evalúa el error cuadrado medio entre la ecuación y el objetivo.
Inputs:
- individual: árbol de operaciones y terminales que representa la ecuación (deap.gp.PrimitiveTree).
- points:lLista de puntos en los que evaluar la ecuación (list).
Output:
- Error cuadrado medio entre la ecuación y el objetivo (tuple).
"""
func = toolbox.compile(expr=individual)
sqerrors = ((func(x) - target)**2 for x in points)
return math.fsum(sqerrors) / len(points),
toolbox.register("evaluate", evalSymbReg, points=[x/10. for x in range(-10,10)])
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("mate", gp.cxOnePoint)
toolbox.register("expr_mut", gp.genFull, min_=0, max_=2)
toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
toolbox.decorate("mate", gp.staticLimit(key=operator.attrgetter("height"), max_value=17))
toolbox.decorate("mutate", gp.staticLimit(key=operator.attrgetter("height"), max_value=17))
def main():
"""
Función principal que configura y ejecuta la evolución genética.
Inputs: Ninguno.
Outputs:
- Población final después de la evolución (list).
- Logbook con estadísticas de la evolución (deap.tools.Logbook).
- Mejor individuo encontrado en la evolución (deap.tools.HallOfFame).
"""
random.seed(318)
pop = toolbox.population(n=300)
hof = tools.HallOfFame(1)
stats_fit = tools.Statistics(lambda ind: ind.fitness.values)
stats_size = tools.Statistics(len)
mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)
mstats.register("avg", numpy.mean)
mstats.register("std", numpy.std)
mstats.register("min", numpy.min)
mstats.register("max", numpy.max)
pop, log = algorithms.eaSimple(pop, toolbox, 0.5, 0.1, 40, stats=mstats,
halloffame=hof, verbose=True)
print("El mejor individuo es: ", hof[0], hof[0].fitness.values)
return pop, log, hof
target = 42
if __name__ == "__main__":
main()
# Obtendremos una tabla similar a la siguiente:
----------------------------------------------------------------------------------------------------------------------------------------------
fitness size
----------------------------------------------- -----------------------------------------------
gen nevals avg gen max min nevals std avg gen max min nevals std
0 300 1751.25 0 1936 1594.46 300 56.7527 3.54667 0 7 2 300 1.49482
1 151 1724.43 1 2679.15 1596.34 151 74.9067 3.72 1 12 1 151 1.74974
2 155 1700.09 2 1923.09 1600 155 48.1738 4.07333 2 14 1 155 1.91519
3 170 1697.96 3 1936 1600 170 56.0824 4.67 3 14 1 170 2.17894
4 159 1684.61 4 2256 1600 159 63.7234 5.40667 4 14 1 159 2.46061
5 159 1671.12 5 1923.09 1521 159 65.2714 6.05 5 17 1 159 2.57698
6 159 1657.31 6 1936 1521 159 69.2923 5.98 6 16 1 159 2.59736
7 182 1645.17 7 1927.27 1521 182 72.2271 6.30333 7 16 1 182 2.73581
8 156 1626.26 8 1936 1521 156 67.0837 6.77667 8 17 3 156 2.9978
9 158 1612.29 9 1849 1444 158 77.0652 7.31333 9 19 1 158 3.40125
10 168 1602.53 10 1849 1444 168 83.9299 7.93333 10 23 1 168 3.52073
11 147 1577.26 11 1849 1444 147 85.3056 8.89667 11 24 1 147 3.63492
12 179 1561.35 12 2025 1444 179 83.6129 9.93667 12 29 1 179 4.20943
13 158 1547.25 13 2007.8 1369 158 92.5802 10.8 13 25 1 158 4.10934
14 162 1531.19 14 1919.74 1296 162 93.8127 12.0567 14 31 1 162 4.80695
15 171 1522.36 15 1911.74 1296 171 103.859 13.3067 15 31 1 171 5.21657
16 168 1495.77 16 1995.05 1240.1 168 106.414 14.2267 16 31 3 168 5.19891
17 172 1476.41 17 1936 1225 172 124.85 14.7133 17 34 3 172 5.81875
18 173 1465.61 18 8116.24 1202.5 173 403.261 16.3233 18 38 3 173 6.19452
19 158 1417.19 19 1936 1152.05 158 136.734 18.4533 19 38 1 158 6.37347
20 151 1375.1 20 2414.4 1089 151 144.448 20.79 20 37 3 151 6.06404
21 144 1322.16 21 1849 979.271 144 130.712 23.2833 21 41 1 144 5.78069
22 180 1321.08 22 5576.86 964.228 180 291.249 25.0067 22 58 7 180 6.38905
23 153 1276.54 23 5019.51 964.228 153 266.514 25.4933 23 60 1 153 7.44782
24 185 1256.48 24 2291.18 741.791 185 198.644 26.4433 24 56 5 185 8.14863
25 158 1225.99 25 8389.61 625.957 158 528.746 29.3233 25 60 2 158 9.00401
26 170 1144.14 26 7325.71 625.957 170 506.571 32.86 26 64 7 170 9.39044
27 166 196593 27 5.86625e+07 625.957 166 3.38117e+06 38.4 27 66 7 166 11.2425
28 180 969.483 28 2728.31 532.604 180 268 44.38 28 88 5 180 13.726
29 195 1021.01 29 21436.6 488.879 195 1267.11 50.6367 29 99 1 195 15.3175
30 159 882.293 30 9324.81 462.66 159 689.491 56.22 30 106 1 159 13.9921
31 157 9247.49 31 2.52795e+06 197.926 157 145666 61.4233 31 121 5 157 12.9344
32 163 110714 32 3.29977e+07 181.099 163 1.9019e+06 64.2967 32 124 2 163 13.2122
33 159 110666 33 3.29959e+07 181.099 159 1.9018e+06 68.0733 33 115 25 159 13.8661
34 175 441028 34 1.32133e+08 100.515 175 7.61593e+06 73.2533 34 121 7 175 15.8769
35 164 556.172 35 15133.2 100.515 164 872.803 80.6867 35 166 7 164 19.1778
36 165 828.686 36 52762 67.4395 165 3548.68 87.83 36 166 10 165 21.0679
37 172 659.891 37 31101.7 1.11459 172 2240.55 93.5233 37 171 7 172 24.7109
38 160 442.732 38 15133.2 1.11459 160 1373.84 102.887 38 184 12 160 22.455
39 153 427.455 39 58297.3 0.345872 153 3356.41 104.037 39 198 2 153 25.0747
40 185 482.955 40 47731.2 0.345872 185 3732.89 107.507 40 198 8 185 25.9003
En la tabla generada, se observa en la columna ‘fitness’ que tanto el promedio como los valores mínimos y máximos de fitness disminuyen con el tiempo, lo cual es una señal positiva. Esto significa que el algoritmo está encontrando soluciones que están cada vez más cerca del objetivo (en este caso, una función que evalúa a 42). En particular, el valor mínimo de fitness es la métrica más importante en este caso, ya que representa la mejor solución (es decir, la más cercana a 42) que el algoritmo ha encontrado hasta ahora. En la generación 40, vemos que el valor mínimo de fitness es extremadamente cercano a cero, lo que indica que el algoritmo encontró una solución muy precisa.
En la columna ‘size’, vemos que el tamaño medio de los individuos (que en este contexto representa la complejidad de las soluciones) aumenta constantemente a lo largo de las generaciones. Esto da a entender que, aunque el algoritmo está encontrando soluciones cada vez mejores, estas soluciones se están volviendo más complejas. En la generación 40, el tamaño promedio es de alrededor de 107, con un tamaño máximo de 198.
Este aumento en la complejidad podría ser un problema si estuviéramos buscando soluciones simples y ‘elegantes’ al problema. Aún así, se ha de tener en cuenta que es una característica común de los algoritmos genéticos que pueden generar soluciones cada vez más complejas a medida que se esfuerzan por encontrar la mejor solución posible.
Por último, obtenemos la siguiente combinación de operaciones matemáticas y constantes que se seleccionaron y organizaron a través de la programación genética:
El mejor individuo es: add(sub(sub(sub(sub(protectedDiv(1, add(sub(-1, sub(neg(-1), sub(-1, 1))), add(sub(-1, 1), -1))), add(-1, -1)), sub(-1, 1)), -1), sub(-1, sub(sub(sub(sub(sub(sub(sub(sub(sub(sub(protectedDiv(1, protectedDiv(0, 0)), add(-1, -1)), sub(-1, 1)), add(-1, -1)), add(-1, -1)), sub(-1, 1)), add(-1, -1)), sub(-1, sub(sub(protectedDiv(cos(-1), 0), add(-1, -1)), sub(-1, mul(x, 1))))), sub(-1, 1)), add(-1, 1)), sub(-1, sub(protectedDiv(sub(1, sub(-1, sub(neg(-1), sub(-1, 1)))), sub(1, cos(sin(1)))), sin(neg(add(-1, -1)))))))), protectedDiv(neg(neg(x)), x)) (0.3458723538739451,)
La última parte (0.3458723538739451,) es el valor de fitness de ese mejor individuo, el cual no produce exactamente el número 42, pero tiene el menor error cuadrado medio entre todos los individuos de la población. Esto podría mejorarse aumentando el tamaño de la población, ejecutando el algoritmo durante más generaciones, y/o ajustando las tasas de cruce y mutación.
Aplicaciones Destacables De La Programación Genética
- Aprendizaje automatico y el reconocimiento de patrones: la programación genética puede ser utilizada para mejorar los algoritmos de aprendizaje automático, permitiendo a los sistemas aprender y adaptarse de manera más eficiente.
- Finanzas, cadenas de montaje y optimización de estrategias comerciales: la programación genética se ha utilizado para desarrollar algoritmos de trading que pueden adaptarse y evolucionar con las condiciones cambiantes del mercado. Un caso notable es el desarrollo de sistemas de trading de alta frecuencia que se ajustan en tiempo real a las fluctuaciones del mercado.
- Biología y el descubrimiento de nuevos medicamentos: la programación genética permite la simulación de procesos biológicos y puede ayudar a descubrir nuevos medicamentos al permitir una búsqueda más efectiva en el espacio de posibles soluciones. Un ejemplo de esto es la optimización de la estructura de nuevos fármacos para maximizar su eficacia y minimizar los efectos secundarios.
- Diseños de motores aeronaúticos: algunas empresas han utilizado la programación genética para mejorar el diseño y la eficiencia de los motores aeronáuticos.
Ventajas & Desventajas De La Programación Genética
La programación genética tiene numerosas ventajas, que incluyen:
- Posibilidad de dar soluciones a problemas complicados -o incluso imposibles- de resolver por humanos, gracias a la exploración de un amplio número de posibles soluciones las cuales un individuo cualquiera podría no haber considerado.
- Flexibilidad y posibilidad de aplicación en un amplio rango de problemas, además de permitir su combinación con técnicas de machine learning y algoritmos de optimización para mejorar la precisión de los resultados.
- Es un proceso altamente automatizado que rara vez requiere intervención manual.
Respecto a las desventajas, debemos tener en cuenta las siguientes dos:
- El proceso puede requerir de mucho tiempo, además de consumir recursos computacionales significativos.
- Posible dificultad de comprensión e interpretación de las soluciones propuestas por la programación genética. Esto puede crear problemas, especialmente en términos de explicabilidad y transparencia.
El Futuro De La Programación Genética
La mejora continua de las capacidades de hardware y software, junto con las investigaciones emergentes en inteligencia artificial, están abriendo nuevas vías para el desarrollo y la aplicación de la programación genética:
- Avances Tecnológicos: los avances en la capacidad de procesamiento de los ordenadores permitirán la ejecución de algoritmos genéticos a una velocidad y escala nunca antes vistas. La posibilidad de llevar a cabo evaluaciones más rápidas y de manipular poblaciones de programas mucho más grandes, permitirá a la programación genética abordar problemas de una complejidad y tamaño en constante crecimiento.
- Nuevas Utilidades: veremos cómo la programación genética se aplica en nuevas áreas como la robótica, donde los robots podrán evolucionar sus propios programas para adaptarse a su entorno y aprender nuevas tareas. En el campo de la biología sintética, la programación genética podría ser utilizada para diseñar organismos genéticamente modificados con características específicas. Es probable que también haya una mayor integración de la programación genética con otras técnicas de inteligencia artificial, como las redes neuronales y el aprendizaje profundo. Esta sinergia podría dar lugar a sistemas de inteligencia artificial más potentes y versátiles.
- Ética & Regulación: la transparencia y la interpretación de las soluciones propuestas por la programación genética seguirá siendo un tema importante, especialmente en contextos donde la toma de decisiones basada en IA debe ser justificada o explicada. Además, el uso de la programación genética en ciertas aplicaciones, como la biología sintética o la robótica, plantea cuestiones éticas y regulativas que deberán ser abordadas.
Conclusión
La programación genética es una excelente técnica en machine learning que utiliza algoritmos evolutivos para generar programas automáticamente. Siendo útil para la resolución de problemas y también descubrir nuevas soluciones y conocimientos en una gran variedad de campos. Aún así, es importante asegurarse de que la programación genética sea útil para la problemática que deseemos resolver, ya que puede haber métodos más apropiados para ello. Actualmente se continúa desarrollando y refinando las técnicas y enfoques utilizados en la programación genética, resultado un campo muy prometedor de cara al futuro.