Saltar al contenido
coden

Estrategias evolutivas y programación genética: de la regla de un quinto a la ley de Kepler

La (1+1)-ES y la regla de un quinto, población y autoadaptación en la función de Rastrigin, CMA-ES, programas como árboles, regresión simbólica que encuentra la tercera ley de Kepler en datos de los planetas, y la hinchazón de los programas.

14 minutos Matěj Žalmánek 2026-10-15

Serie Fundamentos de IA Parte 28 de 52
  1. Mapa de la inteligencia artificial: qué es cada cosa y cómo encaja todo
  2. Breve historia de la inteligencia artificial: de Turing a los modelos de lenguaje
  3. Datos, modelo, entrenamiento, inferencia: los conceptos básicos en un solo ejemplo
  4. Cómo leer la notación matemática: símbolos, subíndices, sumatorios y cuantificadores
  5. Conjuntos y relaciones: las piezas con las que se construyen las definiciones en IA
  6. Funciones y aplicaciones: por qué todo modelo de IA es una función
  7. Lógica y demostraciones: las reglas con las que se razona en IA
  8. Grafos y árboles: la estructura sobre la que se apoyan la búsqueda y las redes neuronales
  9. Álgebra lineal para aprendizaje automático: vectores, matrices y por qué la IA sobre todo multiplica
  10. Derivadas, gradiente y regla de la cadena: cómo sabe un modelo en qué dirección aprender
  11. Descenso de gradiente: cómo aprende un modelo
  12. Qué es la probabilidad: del lanzamiento de un dado a los axiomas de Kolmogórov
  13. Probabilidad condicionada y teorema de Bayes: cómo cambiar una estimación cuando te enteras de algo nuevo
  14. Variable aleatoria y función de distribución: una sola función que lo dice todo sobre la distribución
  15. Función de densidad y función de probabilidad: por qué una variable continua tiene probabilidad cero en cada punto
  16. Esperanza, varianza y covarianza: dónde está una distribución, qué anchura tiene y qué se relaciona con qué
  17. Resumen de las distribuciones de probabilidad: Bernoulli, binomial, Poisson, uniforme, normal y exponencial
  18. Ley de los grandes números y teorema central del límite: por qué las medias se comportan de forma tan predecible
  19. Estadística y estimación de parámetros: máxima verosimilitud, MAP e intervalos de confianza
  20. Teoría de la información: entropía, entropía cruzada y divergencia KL, el origen de las funciones de pérdida
  21. Búsqueda en el espacio de estados: BFS, DFS, UCS y A* con animaciones
  22. Juegos y adversarios: minimax, poda alfa-beta y Monte Carlo tree search
  23. Satisfacción de restricciones: backtracking, propagación de restricciones y AC-3 con un sudoku y un mapa de regiones checas
  24. Lógica y sistemas basados en conocimiento: encadenamiento hacia delante y hacia atrás, resolución y sistemas expertos
  25. Planificación: STRIPS, el mundo de bloques, heurísticas a partir de la relajación y la planificación en los agentes de IA
  26. Búsqueda local y recocido simulado: un viajante por las capitales regionales checas
  27. Algoritmos genéticos: población, cruce, mutación y selección en una mochila para la excursión
  28. Estrategias evolutivas y programación genética: de la regla de un quinto a la ley de Kepler
  29. Optimización por colonia de hormigas: cómo un rastro de feromona encuentra un camino corto
  30. Optimización por enjambre de partículas: inercia, experiencia propia y experiencia del enjambre
  31. Neuroevolución: evolución de los pesos y la forma de las redes neuronales, y el algoritmo NEAT
  32. Conjuntos difusos y funciones de pertenencia: cómo escribir «algo cálido»
  33. Lógica difusa y probabilidad: por qué «algo cálido» no significa «probablemente cálido»
  34. Lógica difusa: t-normas, implicaciones y reglas SI–ENTONCES
  35. Control difuso: Mamdani y Sugeno, del ventilador al péndulo invertido sobre un carro
  36. Agrupamiento difuso: el algoritmo fuzzy c-means y los puntos entre clusters
  37. Sistemas neuro-difusos (ANFIS): reglas difusas que aprenden de los datos
  38. Cadenas de Markov: matriz de transición, distribución estacionaria y PageRank
  39. Modelos ocultos de Márkov: el algoritmo de Viterbi y el casino deshonesto
  40. Redes bayesianas: por qué está mojada la hierba y qué significa eso para el aspersor
  41. El filtro de Kalman: cómo estimar a partir de mediciones imprecisas dónde está un coche y a qué velocidad va
  42. Tipos de aprendizaje automático: supervisado, no supervisado, semisupervisado y más
  43. Prompts, contexto y RAG: cómo darle a un modelo la información correcta
  44. Autocodificadores variacionales (VAE): imágenes nuevas a partir de un espacio latente continuo
  45. Redes generativas adversarias (GAN): el falsificador contra el detective
  46. Modelos de difusión: cómo el ruido puro se convierte en una imagen
  47. Procesos de decisión de Markov: estados, acciones, recompensas y ecuaciones de Bellman
  48. Q-learning y DQN: aprender el valor de las acciones, de la tabla a la red neuronal
  49. Gradiente de la política y PPO: aprender directamente la política
  50. Cómo funciona un agente de IA: un modelo de lenguaje en un bucle con herramientas
  51. Programación con agentes de IA: encargo, pruebas y revisión
  52. Evaluación y seguridad de los sistemas de IA: cómo medir y cómo vigilar

Temario completo de la serie 52 de 83 temas publicados

Qué te llevarás
  • Cómo las estrategias evolutivas ajustan números reales y cómo fijan por sí solas su tamaño de paso.
  • Por qué en un terreno accidentado una población gana a un solo individuo.
  • Cómo la programación genética cultiva fórmulas enteras, y por qué tienden a hincharse.

Conviene conocer antes: Algoritmos genéticos: población, cruce, mutación y selección en una mochila para la excursión

En los años sesenta, dos estudiantes de la Universidad Técnica de Berlín, Ingo Rechenberg y Hans-Paul Schwefel, estaban en un túnel de viento frente a una placa articulada. Querían encontrar la forma con la menor resistencia, pero para calcularla les faltaban tanto las ecuaciones como los ordenadores. Así que echaron a suertes: giraban las articulaciones al azar, medían la resistencia y, si era menor, se quedaban con la nueva forma. De aquel experimento nació la estrategia evolutiva: una evolución que, en lugar de unos y ceros, ajusta números. Un cuarto de siglo después, John Koza mostró que con la evolución también se pueden cultivar programas enteros.

Continúa el artículo Algoritmos genéticos. Allí un individuo era una cadena de bits; aquí será un vector de números reales y un árbol de programa.

1. Evolución de números: la (1+1)-ES

Algoritmo — (1+1)-ES

Minimizamos f : ℝn → ℝ. Un individuo es un punto x ∈ ℝn, σ > 0 es el tamaño de paso. En cada generación:

  1. crea un descendiente y = x + σ · z, donde z ~ N(0, I) es un vector de números independientes con distribución normal;
  2. si f(y) ≤ f(x), sustituye x ← y.
Dicho de forma sencilla

Un progenitor, un hijo. El hijo es el progenitor con un poco de ruido aleatorio en cada número; si es mejor, ocupa su lugar. El tamaño de paso σ dice lo grande que es ese ruido: si hace pequeños retoques o grandes saltos.

y = x + σ · z,   z ~ N(0, I)

y
descendiente — el nuevo candidato que vamos a evaluar
x
progenitor — el mejor punto encontrado hasta ahora; el descendiente nace en su entorno, porque cerca de un buen punto suele haber otros buenos puntos
σ
tamaño de paso — un único número que determina lo lejos del progenitor que se busca; el ruido z tiene varianza 1, y solo σ le da la escala del problema
z ~ N(0, I)
dirección y longitud del intento — la media 0 significa que ninguna dirección está favorecida de antemano; la matriz identidad I, que todas las coordenadas cambian de forma independiente e igual de fuerte
Mutación en una estrategia evolutiva: el progenitor más un paso aleatorio cuya longitud controla σ.
Definición — distribución normal N(0, 1) y N(0, I)

Una variable aleatoria Z tiene distribución normal estándar N(0, 1) si su densidad es φ(z) = e−z²/2 / √(2π); su media es 0 y su varianza 1. Aproximadamente el 68 % de los valores está en el intervalo [−1, 1] y el 95 % en [−1,96; 1,96].

Un vector z ~ N(0, I) en ℝn tiene n componentes independientes con distribución N(0, 1). Su longitud suele rondar √n (para n = 10, unos 3,2), porque E‖z‖² = n.

Nuestro programa genera números normales con el método de Box-Muller a partir de dos números uniformes u1, u2 ∈ (0, 1): z = √(−2 ln u1) · cos(2πu2).

Probémoslo con la función más sencilla posible, la esfera f(x) = x12 + … + x102 en diez dimensiones. El mínimo está evidentemente en cero; empezamos en el punto (3, …, 3), donde f = 90. ¿Cómo elegir σ? Un valor grande baja la pendiente rápido, pero cerca del mínimo se pasa una y otra vez. Uno pequeño es preciso, pero tarda una eternidad en llegar al fondo.

2. La regla de un quinto

Definición — regla de un quinto (Rechenberg, 1973)

Si la proporción de mutaciones con éxito (descendiente mejor que el progenitor) es mayor que 1/5, aumenta σ; si es menor, redúcelo. Una de las formas posibles: tras un éxito σ ← σ · e1/3, tras un fracaso σ ← σ · e−1/12. El tamaño de paso no cambia en promedio exactamente cuando tiene éxito una quinta parte de las mutaciones: (1/5) · (1/3) = (4/5) · (1/12).

Dicho de forma sencilla

Si casi todos los intentos salen bien, estás siendo prudente sin necesidad: aumenta. Si casi ninguno sale bien, saltas demasiado lejos: reduce. Rechenberg dedujo que se avanza más rápido cuando sale bien aproximadamente uno de cada cinco intentos. Así, el algoritmo ajusta solo su tamaño de paso.

Las dos ramas de la regla se pueden escribir con una sola fórmula; s = 1 tras un éxito y 0 tras un fracaso:

σ ← σ · exp( 5/12 · ( s − 1/5 ) )

σ
tamaño de paso — cambia multiplicando, no sumando: sigue siendo positivo y el paso puede reducirse en muchos órdenes de magnitud (de 1 hasta milésimas), como exige la cercanía del mínimo
5/12
velocidad de adaptación — tras un éxito exp(5/12 · 4/5) = e1/3 ≈ 1,40, tras un fracaso exp(−5/12 · 1/5) = e−1/12 ≈ 0,92; un número mayor reaccionaría más rápido, pero σ oscilaría más
s
resultado del último intento (1 = el descendiente no fue peor) — la única información que necesita la regla
1/5
tasa de éxito objetivo — cuando sale bien exactamente una quinta parte de los intentos, la media de s − 1/5 es cero y σ no cambia en promedio
Tras N intentos con S éxitos, el paso queda multiplicado por exp(5/12 · (S − N/5)): de diez intentos, dos éxitos no cambian el paso, cuatro lo multiplican por 2,30 y uno lo reduce a 0,66.
10210010-210-410-6 02505007501000 número de evaluaciones de la función (número) valor f(x) (escala log.)
(1+1)-ES en la esfera en diez dimensiones fijo σ = 1σ = 0,1σ = 0,01regla de 1/5

La regla de un quinto baja de 10−6 en 587 evaluaciones, y la línea del gráfico desciende de forma uniforme, porque el paso se reduce tan rápido como se acerca el mínimo. Con un σ = 1 fijo, el algoritmo pronto choca con un «suelo» en torno a uno, por debajo del cual no se puede llegar con saltos tan grandes. Con σ = 0,01 baja, pero despacio: ni siquiera tras 5 000 evaluaciones está por debajo de 10−4.

import math, random

def es_1_plus_1(f, x, sigma=1.0, kroku=1000):
    fx = f(x)
    for _ in range(kroku):
        y = [xi + sigma * random.gauss(0, 1) for xi in x]
        fy = f(y)
        if fy <= fx:
            x, fx = y, fy
            sigma *= math.exp(1 / 3)     # éxito: aumenta
        else:
            sigma *= math.exp(-1 / 12)   # fracaso: reduce
    return x, fx
Ejemplo — los primeros doce pasos de la ejecución del gráfico

Inicio x = (3, …, 3), f(x) = 90, σ = 1. La columna σ es el tamaño de paso después de la actualización:

pasof(y)¿éxito?f(x)σ
1115,31no90,000,920
295,78no90,000,846
3123,94no90,000,779
4107,86no90,000,717
5101,54no90,000,659
6113,11no90,000,607
794,03no90,000,558
895,09no90,000,513
986,27sí86,270,717
10114,52no86,270,659
1197,72no86,270,607
12102,44no86,270,558

tras 8 fracasos: σ = e−8/12 ≈ 0,513

el paso 9 tiene éxito (86,27 < 90): σ = 0,513 · e1/3 ≈ 0,513 · 1,396 ≈ 0,717

Con σ = 1, un paso en diez dimensiones mide unos √10 ≈ 3,2 y los ocho primeros descendientes son peores que el inicio, así que la regla encoge el paso. Después los éxitos llegan con más frecuencia y σ se estabiliza donde sale bien aproximadamente uno de cada cinco intentos: en toda la ejecución tuvieron éxito 99 de 587 intentos, es decir, el 17 %. A medida que el punto se acerca al mínimo, el paso se reduce con él: al final de la ejecución, tras 580 evaluaciones, σ ≈ 0,00055.

3. Población y autoadaptación

La esfera tiene un único mínimo. Los problemas reales suelen tener montones, y ahí un solo individuo no basta. Una función de prueba típica es la función de Rastrigin: f(x) = 10n + ∑ (xi2 − 10 cos 2πxi). Es una esfera en la que alguien ha excavado una cuadrícula regular de hoyos; el mínimo global 0 está en el origen y a su alrededor hay cientos de mínimos locales.

f(x) = 10n + ∑i=1n ( xi2 − 10 cos 2πxi )

10n
desplazamiento hacia arriba — en el origen el coseno resta como mucho 10 por coordenada, y la constante lo devuelve para que el mínimo global valga exactamente 0
xi2
esfera — la pendiente general hacia el origen; sin ella todos los hoyos serían igual de profundos y ningún mínimo sería global
10 cos 2πxi
ondas de periodo 1 y amplitud 10 — excavan un hoyo en cada número entero; la amplitud es mucho mayor que la pendiente de la esfera entre hoyos vecinos, así que una búsqueda local se queda atascada en el hoyo más cercano
La función de Rastrigin: una esfera lisa con la superficie ondulada de forma regular.
Ejemplo — el inicio y los hoyos vecinos (n = 2)

inicio (4,2; −3,8): x12 = 17,64,   10 cos(2π · 4,2) = 10 cos(0,4π) ≈ 3,09

x22 = 14,44,   10 cos(2π · (−3,8)) = 10 cos(0,4π) ≈ 3,09

f = 20 + (17,64 − 3,09) + (14,44 − 3,09) ≈ 45,90

fondo de los hoyos junto al origen, en (±1; 0) y (0; ±1): f ≈ 0,99 (en el punto 0,995, no exactamente 1: la esfera lo atrae hacia el origen)

fondo de los hoyos diagonales en (±1; ±1): f ≈ 1,99

El valor 1,49 en la generación 8 (véase más abajo) significa, por tanto, una población en un hoyo vecino, todavía no en su fondo.

Definición — (μ, λ)-ES y (μ + λ)-ES con autoadaptación

La población tiene μ progenitores; cada generación crea λ > μ descendientes. En (μ, λ) sobreviven los μ mejores descendientes y los progenitores mueren; en (μ + λ) se elige entre progenitores y descendientes juntos.

Autoadaptación: cada individuo lleva su propio tamaño de paso σ. El descendiente primero lo muta, σ′ = σ · eτ · N(0, 1) con τ ≈ 1/√(2n), y solo entonces desplaza con él la posición: y = x + σ′ · N(0, I).

Dicho de forma sencilla

El tamaño de paso se hereda como cualquier otro rasgo. Quien tiene el paso bien ajustado tiene mejores hijos, y así se impone también su paso. Nadie tiene que contar la tasa de éxito: la evolución se ajusta a sí misma. La variante (μ, λ), en la que los progenitores no sobreviven, además no se deja bloquear por un solo individuo que tuvo suerte: cada generación empieza de nuevo.

σ′ = σ · eτ · N(0, 1),   τ = 1/√(2n)

σ′
paso del descendiente — con él se desplaza enseguida la posición, así que la aptitud del descendiente revela si el paso era bueno
σ
paso del progenitor — se hereda; el descendiente parte de lo que le funcionó al progenitor
τ
tasa de aprendizaje del paso — cuánto puede cambiar el paso entre generaciones; disminuye al crecer el número de dimensiones n, porque entonces un solo valor de aptitud aporta menos información sobre si el paso era bueno
N(0, 1)
cambio aleatorio — simétrico en torno a cero, así que aumentar y reducir son igual de probables; la dirección la decide solo la selección
El paso cambia multiplicándose por una exponencial: sigue siendo positivo y duplicarse es tan probable como reducirse a la mitad.
Ejemplo — mutación del paso para n = 2

τ = 1/√(2 · 2) = 1/2

N(0, 1) = +1:   σ′ = σ · e0,5 ≈ 1,65 σ;   N(0, 1) = −1:   σ′ = σ · e−0,5 ≈ 0,61 σ

Un descendiente típico prueba, por tanto, un paso desde un 40 % menor hasta un 65 % mayor que el del progenitor. En nuestra ejecución, el paso del mejor individuo cayó en 15 generaciones de 2 a 0,011: casi doscientas veces.

import math, random

def es_mi_lambda(f, start, mi=15, lam=100, sigma=2.0, generaci=80):
    n = len(start)
    tau = 1 / math.sqrt(2 * n)
    rodice = [(start, sigma)] * mi
    for _ in range(generaci):
        deti = []
        for _ in range(lam):
            x, s = random.choice(rodice)                    # progenitor aleatorio
            s = s * math.exp(tau * random.gauss(0, 1))      # primero muta el paso
            y = [xi + s * random.gauss(0, 1) for xi in x]   # luego con él la posición
            deti.append((y, s))
        deti.sort(key=lambda d: f(d[0]))
        rodice = deti[:mi]          # (μ, λ): solo sobreviven los mejores descendientes
    return rodice[0]
−5−50055 coordenada x₁ (sin unidad) coordenada x₂ (sin unidad) generación 0 generación 3 generación 8 generación 15 generación 30 generación 80
(15, 100)-ES en la función de Rastrigin; la cruz es el mínimo global

La población arranca en torno al punto (4,2; −3,8), donde f ≈ 45,9. Al principio está repartida por varios hoyos a la vez y se va concentrando en los más profundos. En la generación 8 el mejor individuo sigue en un hoyo vecino (f ≈ 1,49), pero el tamaño de paso ya ha bajado de 2 a 0,27. En la generación 15 la población está en el mínimo global y el paso se ha reducido a una centésima para poder afinarlo.

De cien ejecuciones con semillas distintas, la (15, 100)-ES encontró el mínimo global en 32 casos. Como comparación, la (1+1)-ES con la regla de un quinto y el mismo presupuesto de 8 000 evaluaciones: en uno solo. En las esferas un único individuo es el más rápido; en terreno accidentado gana la población, y aun así a menudo acaba en alguno de los hoyos vecinos.

4. CMA-ES

La autoadaptación solo ajusta la longitud del paso. ¿Y si el mínimo está en un valle estrecho e inclinado, donde conviene dar pasos largos en una dirección y cortos en otra? CMA-ES (Hansen y Ostermeier, 2001) aprende de los pasos con éxito toda la matriz de covarianza de la mutación: la forma de la nube de la que sortea los descendientes. Durante la búsqueda la nube gira y se estira a lo largo del valle. Para optimizar funciones continuas cuyo gradiente no conocemos, CMA-ES es hoy uno de los métodos más fiables.

Definición — mutación con matriz de covarianza

Para una matriz simétrica definida positiva C ∈ ℝn×n, z ~ N(0, C) es un vector con media 0 y covarianzas E[zizj] = Cij. Se obtiene a partir de ruido corriente: z = Au, donde u ~ N(0, I) y AAT = C. El descendiente es y = m + σ · z, donde m es el centro de la población.

Los puntos con la misma densidad están sobre un elipsoide; sus ejes son los vectores propios de C y las longitudes de los ejes son proporcionales a las raíces de los valores propios. Para C = I es una esfera y obtenemos una estrategia evolutiva corriente.

De forma simplificada (la CMA-ES real añade además las llamadas trayectorias evolutivas y su propio control de σ, véase N. Hansen, The CMA Evolution Strategy: A Tutorial, 2016), la matriz se desplaza después de cada generación hacia los pasos con éxito:

C ← (1 − c) C + c ∑i=1μ wi ziziT

C
matriz de covarianza de la mutación — la forma de la nube de la que se sortean los descendientes
1 − c
memoria — la matriz antigua solo se acerca un poco a la nueva información, porque una generación es una muestra pequeña y aleatoria
c
tasa de aprendizaje de la matriz, un número entre 0 y 1 — cuánto peso reciben los pasos de la última generación
∑ wi
media ponderada sobre los μ mejores descendientes; los pesos son positivos, suman 1 y un descendiente mejor tiene uno mayor: solo aprendemos de los pasos que valieron la pena
ziziT
el paso con éxito zi = (yi − m)/σ como matriz — estira la nube justo en su dirección
Las direcciones en las que hubo éxito reciben una varianza mayor en la siguiente generación.
Ejemplo — un paso con éxito en diagonal

C = I (nube circular), un único paso con éxito z = (1, 1), c = 0,2:

zzT = [[1, 1], [1, 1]]

C ← 0,8 · [[1, 0], [0, 1]] + 0,2 · [[1, 1], [1, 1]] = [[1; 0,2], [0,2; 1]]

valores propios: 1 + 0,2 = 1,2 en la dirección (1, 1) y 1 − 0,2 = 0,8 en la dirección (1, −1)

La nube se ha estirado a lo largo de la dirección con éxito y se ha estrechado en la transversal: tras unas cuantas generaciones así, queda tendida a lo largo del valle.

5. Programación genética

Definición — un programa como árbol

Se dan un conjunto de funciones F (con sus aridades) y un conjunto de terminales T (variables y constantes). Un programa es un árbol con raíz cuyos nodos internos son funciones de F con tantos hijos como indica su aridad, y cuyas hojas son terminales de T. El valor del programa se calcula desde las hojas hasta la raíz.

Clausura: cada función debe aceptar la salida de cualquier otra. Por eso se usan la división protegida (dividir entre cero devuelve 1) y la raíz del valor absoluto.

Dicho de forma sencilla

La fórmula (a ÷ 5) × √a es un árbol: arriba la multiplicación, debajo la división y la raíz, abajo del todo a y un cinco. La programación genética cruza programas cortándole una rama a uno e injertando en su lugar una rama del otro. La mutación sustituye una rama por otra nueva crecida al azar. Las operaciones protegidas garantizan que ningún programa falle; como mucho puede dar disparates, y la selección los descarta.

John Koza, de la Universidad de Stanford, describió el método en 1992 en el libro Genetic Programming y lo demostró en decenas de problemas, desde el control de un robot hasta la búsqueda de fórmulas.

Ejemplo — evaluación del árbol (a ÷ 5) × √a para la Tierra

De las hojas a la raíz, a = 149,6 (millones de km):

rama izquierda: a ÷ 5 = 149,6 / 5 = 29,92

rama derecha: √a = √149,6 ≈ 12,231

raíz: 29,92 × 12,231 ≈ 365,95 días   (valor real 365,26, desviación +0,19 %)

def gp(populace, generaci, p_krizeni=0.9, max_hloubka=7):
    for _ in range(generaci):
        dalsi = [nejlepsi(populace)]                  # elitismo: el mejor
        while len(dalsi) < len(populace):
            if random.random() < p_krizeni:           # cruce por intercambio de subárboles
                a, b = turnaj(populace), turnaj(populace)
                dite = nahrad(a, nahodny_uzel(a), podstrom(b, nahodny_uzel(b)))
            else:                                     # mutación: rama → nueva aleatoria
                a = turnaj(populace)
                dite = nahrad(a, nahodny_uzel(a), nahodny_strom(hloubka=2))
            if hloubka(dite) <= max_hloubka:         # descartamos los hijos demasiado profundos
                dalsi.append(dite)
        populace = dalsi
    return nejlepsi(populace)        # menor error; en caso de empate, el árbol menor

6. Regresión simbólica: la ley de Kepler

Johannes Kepler buscó durante diez años la relación entre la distancia de un planeta al Sol y su periodo orbital; publicó la tercera ley en 1619. Démosle a la programación genética la misma tarea. Dispone de ocho planetas —el semieje mayor a en millones de kilómetros y el periodo orbital T en días—, de las operaciones +, −, ×, ÷ y √, de la variable a y de los dígitos del 1 al 9. El error de un programa es la desviación relativa media respecto a los periodos orbitales reales. La población tiene 300 programas y 40 generaciones.

Definición — regresión simbólica

Para unos datos (xi, yi), la regresión simbólica busca una expresión g de una gramática dada (funciones y terminales) que minimice el error ∑ ℓ(g(xi), yi). A diferencia de la regresión corriente, la forma del modelo no está dada de antemano, solo las piezas de construcción.

Dicho de forma sencilla

La regresión lineal del artículo sobre estadística busca la mejor recta. La regresión simbólica no busca números dentro de una fórmula conocida, sino la propia fórmula, y el resultado lo puede leer una persona.

Con precisión, el error de un programa g es la media de las diferencias absolutas de logaritmos:

E(g) = (1/8) ∑i=18 | ln g(ai) − ln Ti |

(1/8) ∑
media sobre ocho planetas — el error no depende de cuántos datos tengamos y se puede leer como la «desviación típica por planeta»
| … |
valor absoluto — una predicción un 1 % mayor es tan mala como una un 1 % menor, y los errores de distinto signo no se anulan
ln g(ai) − ln Ti
la diferencia de logaritmos es ln(g/T), es decir, la desviación relativa: para desviaciones pequeñas ln(1 + r) ≈ r. Los periodos orbitales difieren casi mil veces (88 días Mercurio, 60 190 Neptuno); con una diferencia corriente, solo Neptuno decidiría el error
La desviación relativa media, en la que Mercurio pesa lo mismo que Neptuno. Un programa con resultado negativo o infinito recibe un error ∞.

El mejor programa de la población inicial, aleatoria, se desvía de los periodos orbitales reales un 32 % de media. En la tercera generación ya tiene una desviación del 8 % y la forma correcta a1,5, solo que con la constante 0,22. En la decimoquinta generación llega esto:

× ÷ a 5 √ a
Mejor programa en la generación 15 ((a ÷ 5) × √(a))

T = a · √a / 5, es decir, T proporcional a a3/2, o T2 proporcional a a3. Es la tercera ley de Kepler. La desviación media es del 0,30 % y la constante 1/5 está muy cerca del valor real 0,1996 para estas unidades. De veinte ejecuciones con semillas distintas, dieciocho encontraron el exponente 1,5.

¿De dónde sale 0,1996? De la ley de la gravitación de Newton se deduce, para una órbita circular (y para una elipse de semieje mayor a, cuando la masa del planeta es despreciable frente a la del Sol), la forma de la ley junto con la constante:

T = 2π / √(GM☉) · a3/2

T
periodo orbital — el tiempo de una vuelta
2π
una vuelta es un ángulo de 2π; la fórmula surge de la velocidad angular ω como T = 2π/ω
√(GM☉)
la fuerza de la gravedad del Sol (G la constante de gravitación, M☉ la masa del Sol) — una atracción más fuerte significa una órbita más rápida, por eso está en el denominador
a3/2
distancia — la gravedad se debilita con a2 y el recorrido es más largo en proporción a a; de la igualdad entre la fuerza centrípeta y la gravitatoria, ω2a = GM☉/a2, sale ω ∝ a−3/2
La tercera ley de Kepler deducida de la gravitación de Newton; la programación genética encontró la forma a3/2 y la constante a partir de solo ocho pares de números.
Ejemplo — la constante en millones de km y días

El parámetro gravitacional del Sol es GM☉ = 1,327 124 · 1020 m3/s2 (valor nominal de la Unión Astronómica Internacional, resolución B3 de 2015). La unidad de longitud es 109 m, así que a3/2 en metros es (109)3/2 = √(1027) veces mayor:

√(1027 / 1,327 124 · 1020) = √(7,535 · 106) ≈ 2 745,0 s

2π · 2 745,0 s ≈ 17 247 s

17 247 s / 86 400 s/día ≈ 0,1996 días

El programa con la constante 1/5 = 0,2 difiere, por tanto, en un 0,19 %: exactamente la desviación que salió antes para la Tierra.

7. Hinchazón

Pero la evolución no se quedó en la bonita fórmula. Hacia la generación 40 «mejoró» la desviación hasta el 0,23 % con esto:

× ÷ + a ÷ ÷ − × 2 8 a 5 a 5 √ a
Mejor programa en la generación 40 (((a + ((((2 × 8) − a) ÷ 5) ÷ a)) ÷ 5) × √(a))

El árbol tiene dieciséis nodos en lugar de seis, y las ramas añadidas no hacen otra cosa que retocar la constante y sortear el redondeo de los datos. Físicamente es una fórmula peor, sobreajustada a ocho planetas. Mientras tanto, el tamaño medio de los programas de la población creció de 9,8 nodos al principio a 23,8 en la vigésima generación.

Definición — hinchazón y presión de parsimonia

La hinchazón (bloat) es el crecimiento del tamaño de los programas de la población que no corresponde a una mejora de su aptitud. La presión de parsimonia (parsimony pressure) penaliza el tamaño: o bien lo suma al error, F′(g) = F(g) + c · |g|, o bien lo usa como segundo criterio cuando los errores empatan.

Dicho de forma sencilla

Los programas se van cubriendo de código que no hace nada o hace una corrección ínfima, igual que en los genomas de los seres vivos se acumulan tramos sin función. Además, a un árbol grande el cruce lo rompe con más dificultad, así que se multiplica con más facilidad. Nuestro algoritmo, a igual error, prefiere el árbol menor, pero cuando un árbol mayor baja el error un pelo, gana. El mismo dilema que con el sobreajuste de un modelo: ¿precisión en los datos o sencillez que generaliza?

F′(g) = F(g) + c · |g|

F′(g)
error ajustado con el que se selecciona — menor es mejor
F(g)
error del programa en los datos — sigue siendo lo principal que queremos reducir
c
precio de un nodo — dice cuánto debe mejorar el error para que compense un nodo más; se elige de modo que solo decida en mejoras despreciables
|g|
tamaño del programa, el número de nodos del árbol — una medida de complejidad que se puede calcular sin entender el programa
Un nodo adicional tiene que pagarse reduciendo el error al menos en c.
Ejemplo — qué c habría salvado la ley de Kepler

Árbol de la generación 15: error 0,2986 %, 6 nodos. Árbol de la generación 40: error 0,2312 %, 16 nodos. El árbol menor gana cuando

0,002 986 + 6c < 0,002 312 + 16c

10c > 0,000 674  ⇒  c > 6,7 · 10−5

Diez nodos más mejoraron el error en menos de una centésima de punto porcentual. Con un precio superior al 0,007 % por nodo, el árbol de la generación 40 habría perdido en la selección frente a la ley legible de la generación 15.

8. Estrategias evolutivas y programas hoy

  • Estrategias evolutivas en lugar de aprendizaje por refuerzo. En 2017 OpenAI mostró que una estrategia evolutiva sencilla puede enseñar a redes neuronales a controlar robots simulados y a jugar a juegos de Atari casi tan bien como el aprendizaje por refuerzo, y que se puede repartir entre miles de procesadores que solo se envían unos pocos números.
  • Descubrir leyes a partir de datos. En 2009 Schmidt y Lipson hicieron que la regresión simbólica dedujera leyes del movimiento y leyes de conservación a partir de mediciones de péndulos. Bibliotecas actuales como PySR se usan en física y astronomía, donde una fórmula legible vale más que una caja negra.
  • Evolución de algoritmos de aprendizaje automático. El proyecto AutoML-Zero (Google, 2020) dejó que la evolución montara algoritmos de aprendizaje a partir de operaciones matemáticas elementales y así redescubrió, por ejemplo, la retropropagación.
  • Programas con modelos de lenguaje. Los sistemas FunSearch y AlphaEvolve del artículo anterior son programación genética en la que la mutación, en vez de una rama aleatoria, la hace un modelo de lenguaje.

9. Resumen

  • Las estrategias evolutivas mutan números reales con ruido normal de tamaño de paso σ.
  • La regla de un quinto y la autoadaptación ajustan σ automáticamente; CMA-ES aprende también la forma de las mutaciones.
  • En las esferas basta un individuo; en terreno accidentado ayuda una población: en la función de Rastrigin, 32 éxitos de 100 frente a uno.
  • La programación genética cultiva programas como árboles; la regresión simbólica encontró en los datos de los planetas la tercera ley de Kepler.
  • Los programas tienden a hincharse y a sobreajustarse; contra ello ayuda la presión de parsimonia.

¿Quieres probarlo tú? Pide a un agente de IA que escriba una regresión simbólica y dale tus propios datos —por ejemplo, el periodo de un péndulo según la longitud del cordel— y mira si encuentra √L.

¿Lo ves de otra manera? Escribe a support@coden-app.com.

Un Agente en tu ordenador, controlado desde el móvil