Capítulo 4 · El aprendizaje
Cómo aprende a crecer
La red parte sin saber nada. 4 000 veces seguidas hace crecer lagartijas, mide qué tan mal le salieron y corrige sus 8 320 números. Este capítulo cuenta ese bucle y los tres experimentos del artículo, cada uno arreglando lo que falla en el anterior, con los resultados reales de un entrenamiento hecho en un portátil, sin GPU.
El objetivo
Lo único que la red recibe como "instrucción" es una imagen de 40×40 píxeles con transparencia: el emoji 🦎 dibujado con la fuente Noto Color Emoji, reducido y centrado. Se le agregan 16 píxeles vacíos por lado, para que la lagartija tenga espacio para crecer, y queda una cuadrícula de 72×72.
Igual que el estado del autómata, el objetivo se guarda con el color premultiplicado por alfa. La pérdida compara 72 × 72 × 4 = 20 736 números: color y alfa de cada célula. El fondo transparente también cuenta: una célula viva donde debería haber vacío es un error.
objetivo, 40×40
El bucle de entrenamiento
Un paso de entrenamiento (no confundir con un paso del autómata) hace esto:
Un paso de entrenamiento
# entrenar.py, simplificado
for paso in range(1, 4001):
x = armar_lote() # 1. depende del experimento
for _ in range(random.randint(64, 96)): # 2.
x = modelo(x)
perdida = ((x[:, :4] - objetivo) ** 2).mean() # 3.
optimizador.zero_grad()
perdida.backward() # 4. a través de los 64-96 pasos
for p in modelo.parameters():
p.grad /= p.grad.norm() + 1e-8 # 5.
optimizador.step()
guardar_en_el_pool(x) # 6. solo en persistir y regenerar
Los tres experimentos usan exactamente este bucle. Lo único que cambia es el paso 1, desde dónde parte cada intento, y esa diferencia basta para que aprendan cosas distintas.
Experimento 1: crecer
Desde dónde parte: siempre desde la semilla. Los 8 intentos del lote son 8 semillas.
x = nca.semilla(lado, LOTE)
Es lo más directo: "desde una célula, en 64 a 96 pasos, tienes que verte como la lagartija". Y la red lo aprende. Pero mira lo que pasa si la dejas correr más allá de lo que se entrenó:
Lo que sabe hacer al final del entrenamiento
A los 100 pasos del autómata, la lagartija es casi perfecta: es lo que se le pidió. A los 1 000 pasos se desarmó en fragmentos. Medido en 8 corridas del modelo final, la pérdida pasa de 0,0003 en el paso 96 a 0,007 en el 300, a 0,025 en el 1 000 y a 0,034 en el 3 000. Eso es casi lo mismo que una cuadrícula vacía, que tiene pérdida 0,035: la lagartija se borró sola. Tampoco sabe curarse: con la herida (fotos 3 y 4) todo empeora.
En la historia del entrenamiento se ve que no siempre falló igual. Hasta el paso 1 000 de entrenamiento, el autómata no paraba de crecer y a los 1 000 pasos llenaba la cuadrícula entera de texturas. En el paso 2 000 se esparcía en rayas verdes, y en el modelo final se desintegra. Como nadie le pidió nada sobre el largo plazo, cada versión de la red falla a su manera.
Una red aprende exactamente lo que le pides, no lo que querías decir. Le pediste "verse bien entre los pasos 64 y 96". Nunca le dijiste qué hacer en el paso 97, así que cualquier cosa vale. En aprendizaje automático esto pasa seguido: el modelo optimiza la pérdida que escribiste, no tu intención.
Experimento 2: persistir
El artículo titula esta sección "What persists, exists": lo que persiste, existe. La idea es entrenar no solo desde la semilla sino también desde estados que ya crecieron, para que la red tenga que responder la pregunta "y ahora, ¿qué haces?".
El pool de muestras
indices = torch.randperm(POOL)[:LOTE]
x = pool[indices]
orden = perdida_por_muestra(x, objetivo).argsort(descending=True)
indices, x = indices[orden], x[orden]
x[:1] = nca.semilla(lado) # la peor vuelve a empezar
# ... crecer, pérdida, retropropagar, Adam ...
pool[indices] = x.detach() # los resultados vuelven al pool
Piensa en un estado del pool que ya pasó por el bucle 20 veces: lleva entre 1 300 y 1 900 pasos del autómata desde la semilla, y se le sigue pidiendo que dentro de 64 a 96 pasos más se vea como la lagartija. La única forma de cumplir con todos los estados del pool, jóvenes y viejos, es llegar a la forma y quedarse ahí.
En el lenguaje de los sistemas dinámicos, la lagartija se vuelve un atractor: un estado al que el sistema vuelve solo, como una pelota que siempre termina en el fondo del mismo valle. El parpadeador del Juego de la Vida era un atractor encontrado por casualidad. Este lo diseñó el entrenamiento.
¿Por qué reemplazar la peor por la semilla? Por dos razones: para que la red no olvide cómo crecer desde cero (si el pool solo tuviera lagartijas crecidas, dejaría de practicarlo) y para limpiar el pool de estados que se echaron a perder.
Lo que sabe hacer al final del entrenamiento
El modelo final se queda quieto en la forma. Medido en 8 corridas, la pérdida baja a menos de 0,00005 a partir del paso 200 del autómata y sigue ahí en el paso 3 000, con exactamente 580 células vivas en todas las corridas. Es un atractor de verdad: la lagartija se volvió el estado al que el sistema llega y del que no sale.
Pero mira la herida (fotos 3 y 4): persistir no sabe curarse. Las partes que quedan siguen creciendo cada una por su cuenta y arman una lagartija incompleta. La pérdida sube de 0,021 recién herida a 0,038 trescientos pasos después: la herida empeora. Con un corte por la mitad le va algo mejor (de 0,014 a 0,008), pero tampoco se recupera del todo.
Mantener una forma y repararla son habilidades distintas. El pool solo le mostró lagartijas enteras o semillas, así que nunca vio una lagartija rota y no sabe qué hacer con ella.
Experimento 3: regenerar
Una forma que se mantiene no es lo mismo que una forma que se repara. Para enseñarle a reparar, el pool se llena de heridas: en cada paso de entrenamiento, a las 3 muestras con menor pérdida (las que ya están bien formadas) se les borra un círculo al azar, poniendo en cero sus 16 canales.
if danar:
x[-DANADAS:] *= circulos(DANADAS, lado) # las 3 mejores: un círculo de ceros
Se dañan las mejores porque dañar una lagartija mal formada no enseña nada nuevo: ya tenía problemas. Las heridas tienen radio de entre 0,1 y 0,4 en una cuadrícula que va de −1 a 1, o sea, de 4 a 14 células de radio, y su centro cae en la zona central.
Lo que sabe hacer al final del entrenamiento
Esta sí se cura. Medido en 8 corridas con la misma herida, un círculo de radio 12 borrado al centro en el paso 300, la pérdida baja de 0,021 recién herida a 0,0056 apenas 50 pasos después. Un corte por la mitad nunca lo vio en el entrenamiento, y aun así lo cura mejor: de 0,014 a 0,0019 en 100 pasos. La cicatriz no queda perfecta: después de curarse, la pérdida se queda entre 0,003 y 0,01, contra 0,0001 de una lagartija que nunca se hirió. A simple vista casi no se nota.
Y sin heridas crece y persiste igual de bien que el experimento 2. Su pérdida de entrenamiento es la más alta de las tres (sección 8), pero no porque sea peor: en cada lote tiene 3 heridas frescas que curar en 64 a 96 pasos, y eso nunca sale perfecto.
Los tres, lado a lado
Los tres modelos finales corriendo a la vez, desde la misma semilla. Hiérelos con los botones o arrastrando el mouse sobre cualquiera, y compara.
La historia de cada entrenamiento
En cada punto de control, entrenar.py guardó los pesos y cuatro fotos de lo que la red sabía hacer
en ese momento: el autómata a los 100 pasos, a los 1 000, recién herido (un círculo de radio 12 borrado en el
paso 300) y 300 pasos después de la herida. Cada fila es la misma red, más entrenada.
Lo que se ve al recorrer las filas de regenerar:
- Pasos 25 y 50: una mancha verdosa y difusa, más grande que la lagartija. La red aprendió "crece y ponte verde", y nada más. A los 1 000 pasos la mancha ya llenó la cuadrícula.
- Pasos 100 a 400: aparece una silueta alargada en diagonal, la dirección del cuerpo. Primero se aprende la forma gruesa y después los detalles: patas, cola, ojos.
- Paso 700: la lagartija ya se reconoce y ya persiste a los 1 000 pasos, pero la herida la mata: casi nada sobrevive.
- Pasos 1 000 a 2 000: se cura, pero mal: vuelve a crecer una lagartija deforme, borrosa o estirada.
- Pasos 3 000 y 4 000: se cura del todo.
En crecer, la columna de 1 000 pasos nunca se arregla, porque nada en su entrenamiento lo pide. En persistir, la columna de 1 000 pasos se arregla y la de la herida no.
Las curvas de pérdida
Pérdida por paso de entrenamiento (escala logarítmica, promedio cada 10 pasos)
Cómo leerlas:
- Las tres bajan entre 50 y 300 veces. La escala es logarítmica: cada línea horizontal es 10 veces menos error.
- El escalón en el paso 2 000 es la tasa de aprendizaje dividida por 10: con pasos más finos, la pérdida baja un poco de golpe y la curva se vuelve menos ruidosa.
- Crecer termina con una pérdida baja (0,0004) y aun así está roto. Su pérdida solo mide entre los pasos 64 y 96 desde la semilla, y ahí lo hace muy bien. Lo que pasa después es invisible para la pérdida.
- Persistir termina con la más baja (0,00009), porque buena parte de su lote son lagartijas que ya están perfectas y solo tienen que quedarse quietas.
- Regenerar termina con la más alta (0,0005), por las heridas que siempre tiene que curar.
- Al final siguen bajando, pero lento: del paso 3 000 al 4 000, entre un 20 y un 40 %. Con los 8 000 pasos del artículo saldrían algo más nítidas.
Cuánto costó
Los tres experimentos se entrenaron a la vez, cada uno con 5 hilos de CPU, en un AMD Ryzen 9 8945HS sin GPU. Cada uno ocupó alrededor de 1,5 GB de RAM, sobre todo para guardar los estados intermedios que necesita la retropropagación.
Un paso de persistir y regenerar cuesta más que uno de crecer porque sus estados ya vienen crecidos: hay más células vivas donde calcular la red. Cómo se llegó a estos tiempos está en decisiones técnicas.
Las otras figuras
El laboratorio tiene seis figuras más. Cada una se entrenó desde cero con el experimento regenerar, el mismo código y los mismos hiperparámetros que la lagartija, solo que 3 000 pasos en vez de 4 000. Lo único que cambia es la imagen objetivo. Las fotos son las mismas cuatro de la historia: a los 100 pasos, a los 1 000, recién herida y 300 pasos después.
| Cargando las figuras… |
Para compararlas entre sí, cada pérdida se divide por la de una cuadrícula vacía: el error relativo. 0 % es la figura perfecta y 100 % es lo mismo que no haber dibujado nada. Esto hace falta porque un emoji grande y de muchos colores tiene más error posible que la lagartija. Cada número es el promedio de 8 corridas del modelo final.
| Figura | Mantenerse: a los 1 000 pasos | Herida circular: 100 pasos después | Corte por la mitad: 100 pasos después |
|---|---|---|---|
| Lagartija (4 000 pasos) | 0,3 % | 14 % | 6 % |
| Tripulante | 0,2 % | 5 % | 8 % |
| Invasor 👾 | 0,5 % | 8 % | 9 % |
| This is fine | 3,5 % | 4 % | 17 % |
| Hongo 🍄 | 4 % | 6 % | 23 % |
| Pulpo 🐙 | 6 % | 5 % | 20 % |
| Pepe | 9 % | 6 % | 43 % |
Lo que se aprende comparándolas:
- Las formas compactas y de colores planos salen casi perfectas. El tripulante y el invasor quedan tan bien como la lagartija. Las figuras con detalles finos pierden justo eso: a Pepe y al pulpo se les desdibujan los ojos, y al perro de This is fine, la cara. La silueta y los colores grandes sí los aprenden. Pepe, además, se va desdibujando de a poco: su error sube de 5 % en el paso 96 a 9 % en el 1 000.
- Todas curan la herida circular, que es el tipo de daño que vieron en el entrenamiento: 100 pasos después quedan entre 4 % y 14 %. El invasor tiene una curiosidad: cierra la herida, pero rellena de morado también los huecos que debían quedar vacíos, y 300 pasos después su error sube a 20 %.
- El corte por la mitad, que ninguna vio, las separa. El tripulante y el invasor se recuperan casi tan bien como la lagartija. Pepe y el hongo no: vuelven a crecer un poco, pero no la mitad que les falta. Que una red se las arregle con heridas que nunca vio no está garantizado: depende de lo que haya aprendido, y eso cambia de una figura a otra.
Con más pasos de entrenamiento (el artículo usa 8 000) o más neuronas, las figuras difíciles probablemente ganarían detalle. No se probó aquí: cada figura ya tomó cerca de dos horas.