⏱️ Lectura: 15 min
Un microcontrolador de 6 dólares acaba de ejecutar, en tiempo real, un modelo de mundo comprimido que inventa videojuegos cuadro por cuadro. El proyecto tinyworldformer corre dentro de 520 KB de RAM en una Raspberry Pi Pico 2W, sin GPU, sin nube y sin conexión a internet.
📑 En este artículo
- TL;DR
- ¿Qué es un modelo de mundo comprimido?
- Por qué importa
- Cómo funciona tinyworldformer por dentro
- Ejemplos prácticos y cómo empezar
- Casos de uso reales
- Errores comunes y buenas prácticas
- Comparativa con alternativas para generar contenido con IA
- Profundizando: por qué el bucle ahorra memoria y dónde se nota el límite
- Preguntas frecuentes
- ¿Qué microcontrolador corre el modelo de mundo comprimido de tinyworldformer?
- ¿Cuántos parámetros tiene el transformer en bucle de cada juego?
- ¿Hace falta GPU para entrenar tinyworldformer antes de cuantizarlo?
- ¿Qué diferencia a la red actora de la IA embebida en microcontroladores que genera los frames?
- ¿Se puede correr tinyworldformer en un microcontrolador distinto al Pico 2W?
- Referencias
Su autor lo logró separando el trabajo en dos redes neuronales por fotograma. Una genera el siguiente cuadro del juego, la otra decide qué botón presionar, y ambas corren a la vez sobre el mismo chip.
TL;DR
- Int8 y pesos compartidos reducen el generador de frames a entre 26.000 y 131.000 parámetros según el juego.
- Un bloque transformer aplicado de 1 a 4 veces sustituye a una pila de capas distintas y ahorra memoria.
- La red actora, separada del generador, pesa solo entre 9.000 y 16.000 parámetros en float32.
- El RP2350 reparte el trabajo: el núcleo 1 corre ambas redes, el núcleo 0 atiende USB y pantalla.
- El repositorio SheepSeb/tinyworldformer deja jugar los cinco juegos reales desde la terminal con uv.
¿Qué es un modelo de mundo comprimido?
Un modelo de mundo comprimido es una red neuronal generativa reducida en tamaño y precisión para correr sin GPU: tinyworldformer la implementa como un transformer en bucle cuantizado a 8 bits que predice el siguiente fotograma de un videojuego y cabe en los 520 KB de RAM de un microcontrolador de 6 dólares.
La idea de partida de su autor fue simple. Los transformers en bucle reutilizan el mismo bloque de pesos varias veces en lugar de apilar capas distintas, así que ocupan mucha menos memoria por diseño. Si ese ahorro era suficiente, un modelo generativo completo podía caber en un chip de bolsillo.
Por qué importa
La mayoría de los modelos generativos de video o de juegos dependen de una GPU con varios gigabytes de memoria. Tinyworldformer invierte esa ecuación: corre en un chip pensado para termostatos y drones de juguete. Eso importa para el campo conocido como edge computing, donde el objetivo es mover la inferencia al dispositivo en lugar de depender de un servidor remoto.
Un modelo que corre en el borde no necesita conexión a internet, no paga latencia de red y no expone datos a un servidor externo. Acá el beneficio es además una prueba de concepto. Si una red generativa cabe en 520 KB, la pregunta deja de ser si el edge AI puede generar contenido y pasa a ser cuánto contenido puede generar con ese presupuesto de memoria.
El ángulo de costo pesa tanto como el técnico. Una Raspberry Pi Pico 2W cuesta una fracción de lo que cuesta cualquier GPU capaz de correr un transformer completo, y consume órdenes de magnitud menos energía porque no tiene memoria de video ni refrigeración activa. Para proyectos de hobby, educación o investigación en TinyML, esa diferencia de costo decide si un experimento se hace o no.
Cómo funciona tinyworldformer por dentro
Tinyworldformer separa el problema en dos redes con trabajos distintos. La primera, el generador de frames, recibe el cuadro actual y la acción del jugador y predice el cuadro siguiente junto con la recompensa. La segunda, la red actora, solo mira el cuadro y decide qué acción tomar. Ninguna de las dos ve jamás el juego real una vez que arranca la sesión: el generador solo recibe acciones de la actora, y la actora solo ve cuadros inventados por el generador.
sequenceDiagram
participant Actor as Red actora
participant Generador as Generador de frames
Actor->>Generador: envia accion
Generador-->>Actor: entrega frame generado y recompensa
Note over Actor,Generador: el generador nunca ve el juego real
Tokenización en parches
Antes de entrar al transformer, cada cuadro se recorta en parches cuadrados: 2×2 celdas en Breakout, 4×4 en Doom-lite, Flappy Bird y Pong, 6×6 en Pac-Man lite. Eso da entre 36 y 80 tokens por juego según el repositorio. Cada token suma un vector aprendido por celda, con una tabla distinta según la posición dentro del parche para no perder el orden, más un vector de posición y un vector de la acción tomada.
Un ejemplo simplificado en Python muestra la misma idea con un cuadro diminuto de 4×4 dividido en parches de 2×2:
import numpy as np
frame = np.array([
[1, 1, 2, 2],
[1, 1, 2, 2],
[3, 3, 4, 4],
[3, 3, 4, 4],
])
def a_parches(frame, parche=2):
h, w = frame.shape
tokens = []
for i in range(0, h, parche):
for j in range(0, w, parche):
tokens.append(frame[i:i+parche, j:j+parche].flatten())
return np.array(tokens)
print(a_parches(frame))
La salida literal es una matriz de 4 tokens, uno por cada región homogénea del cuadro:
[[1 1 1 1]
[2 2 2 2]
[3 3 3 3]
[4 4 4 4]]
El transformer en bucle
En lugar de apilar, por ejemplo, ocho bloques transformer distintos, tinyworldformer usa un único bloque (atención más una red feedforward con ReLU) y lo aplica entre 1 y 4 veces seguidas, sumando la entrada original antes de cada pasada. El resultado de cada pasada se entrena contra el cuadro siguiente real, así que el número de pasadas en tiempo de ejecución es un control directo entre velocidad y precisión. En el Pico 2W, para mantener una tasa de cuadros razonable, el proyecto usa dos pasadas.
flowchart TD
A["Frame t + accion"] --> B["Tokenizar en parches"]
B --> C["Bloque transformer (pesos compartidos)"]
C --> D{"Repetir de 1 a 4 veces"}
D -->|"otra pasada"| C
D -->|"pasada final"| E["Frame t+1 y recompensa"]
Cuantización y reparto entre núcleos
El generador de frames se cuantiza a int8, mientras que la red actora se queda en float32 porque ya es pequeña de por sí, entre 9.000 y 16.000 parámetros, y la precisión ahí importa más para decidir bien la acción. El RP2350 del Pico 2W tiene dos núcleos: el núcleo 1 corre el generador y la actora, el núcleo 0 se encarga del USB y de la pantalla, según documenta el repositorio.
Esa separación entre generador y actor permite optimizar cada red por separado. El generador, la parte pesada, recibe toda la cuantización, mientras que la actora, ya liviana, se queda intacta en float32.
flowchart TD
A["RP2350 dual-core"] --> B["Nucleo 0: USB y pantalla"]
A --> C["Nucleo 1: inferencia"]
subgraph Inferencia["Nucleo de inferencia"]
C --> D["Generador de frames int8"]
C --> E["Red actora float32"]
end
Ejemplos prácticos y cómo empezar
El repositorio incluye cinco juegos reales escritos en unas pocas líneas de numpy, lo que le permite a su autor comparar cada frame generado contra la física real del juego. La siguiente tabla resume el costo de memoria y cómputo de cada uno, con datos publicados en el repositorio de tinyworldformer:
| Juego | Tamaño de frame | Parámetros | MACs por frame | Frames exactos |
|---|---|---|---|---|
| Breakout | 20×16 | 26k | 4.3M | 99.99% |
| Doom-lite | 32×32 | 55k | 4.2M | 83.6% |
| Flappy Bird | 24×24 | 46k | 2.0M | 100% |
| Pong | 20×32 | 46k | 2.3M | 99.45% |
| Pac-Man lite | 48×48 | 131k | 8.2M | 86.4% |
La columna de frames exactos mide el porcentaje de transiciones donde cada celda del cuadro predicho coincide con el cuadro real. Doom-lite tiene el número más bajo, 83.6%, pero no porque juegue mal: su vista en primera persona tiene 768 celdas, y un solo píxel de sombra mal pintado ya marca todo el cuadro como incorrecto en esta métrica.
Cómo probarlo en tu máquina
El proyecto usa uv como gestor de paquetes de Python, así que no hace falta instalar dependencias a mano una por una. En Linux o macOS:
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/SheepSeb/tinyworldformer
cd tinyworldformer
uv run python -m tinyworldformer.games.play --game pacman
En Windows, el único paso que cambia es la instalación de uv, desde PowerShell: powershell -c "irm https://astral.sh/uv/install.ps1 | iex", y luego los mismos comandos de git clone y uv run.
El comando play acepta también breakout, doomlite, flappy o pong en lugar de pacman. No hay salida de texto que mostrar: uv descarga las dependencias declaradas en el pyproject.toml y abre una ventana gráfica con el juego real corriendo en numpy, el mismo que el generador de frames aprendió a imitar.
💡 Tip: correr primero el juego real con
playantes de buscar los pesos entrenados ayuda a entender qué está tratando de imitar el generador cuantizado, celda por celda.
Casos de uso reales
Tinyworldformer no apunta a reemplazar un motor de videojuegos. Su valor está en lo que demuestra: un modelo generativo entrenado sobre partidas grabadas puede, cuantizado y comprimido, correr en el mismo chip que hoy maneja sensores de temperatura o drones de juguete baratos. El caso de uso inmediato es educativo y de investigación en TinyML, pero la receta (tokenizar en parches, compartir pesos entre pasadas, cuantizar solo la parte más pesada) es trasladable a otros generadores pequeños, como un simulador de sensor para probar un controlador embebido sin hardware real conectado.
El repositorio también suma un agente de refuerzo por streaming que aprende a jugar dentro del sueño generado por el propio modelo, en lugar de solo repetir una política ya entrenada. Cualquier botón del dispositivo le devuelve el control al usuario humano, así que la misma placa sirve para ver a la IA jugar sola o para jugar uno mismo dentro del mundo que ella inventa.
Errores comunes y buenas prácticas
- Confundir precisión de píxel con jugabilidad. Un 83.6% de frames exactos suena mal hasta que se entiende la métrica: un solo píxel de sombra distinto ya cuenta como fallo total del cuadro, aunque el estado del juego (posición del enemigo, vida, puntaje) siga siendo correcto.
- Cuantizar todo por igual. Aplicar int8 a la red actora, que ya tiene menos de 16.000 parámetros, aportaría poco ahorro de memoria y arriesgaría la precisión de la política. La cuantización rinde donde el modelo es grande, no donde ya es pequeño.
- Elegir mal el número de pasadas del bucle. Una sola pasada del transformer es más rápida pero deja artefactos visibles, un enemigo que no debería estar ahí, por ejemplo. Cuatro pasadas corrigen casi todo el error pero cuestan más tiempo de cómputo por cuadro. Dos pasadas fue el punto que eligió el proyecto para el Pico 2W.
- Verificar contra la física real, no solo mirar el video generado. Tener la versión real del juego en numpy, como hace este repositorio, es lo que permite medir el error cuadro por cuadro en lugar de confiar en que se vea bien.
Comparativa con alternativas para generar contenido con IA
| Enfoque | Cuándo usarlo | Ventaja | Limitación |
|---|---|---|---|
| Transformer completo en GPU o nube | Cuando la latencia de red no importa y hay presupuesto de cómputo | Máxima fidelidad del frame generado | Depende de conexión e infraestructura externa |
| Modelo de mundo comprimido en microcontrolador (tinyworldformer) | Dispositivos aislados, sin GPU, con RAM y flash muy limitados | Corre offline, sin latencia de red, consumo mínimo | Solo viable en juegos o dominios muy pequeños |
| Distillation genérica a modelos pequeños (TinyML clásico) | Tareas de clasificación o detección simples en sensores | Herramientas y runtimes maduros | No pensado para generar secuencias completas de frames |
La fila intermedia describe a tinyworldformer. No busca competir en fidelidad visual con un generador de video entrenado en GPU; busca que el juego siga siendo jugable dentro de un presupuesto de memoria minúsculo.
Profundizando: por qué el bucle ahorra memoria y dónde se nota el límite
La razón de fondo por la que un transformer en bucle pesa menos es aritmética simple: un transformer tradicional de N capas guarda N juegos de pesos distintos, mientras que el bucle guarda un solo juego de pesos y lo reusa N veces. El costo que se paga a cambio es tiempo de cómputo, cada pasada adicional cuesta los mismos multiplica-acumula que la anterior, en lugar de memoria, y en un microcontrolador la memoria suele ser el recurso más escaso de los dos.
La cuantización int8 agrega un segundo ahorro, independiente del anterior: cada peso pasa de ocupar 4 bytes en float32 a ocupar 1 byte, a costa de redondear cada valor a uno de 256 niveles posibles. El siguiente ejemplo en Python muestra ese redondeo con cuatro pesos cualquiera:
import numpy as np
pesos = np.array([0.42, -0.87, 1.35, -1.90], dtype=np.float32)
escala = np.max(np.abs(pesos)) / 127
cuantizados = np.round(pesos / escala).astype(np.int8)
reconstruidos = cuantizados.astype(np.float32) * escala
print(cuantizados)
print(np.round(reconstruidos, 4))
La salida literal es:
[ 28 -58 90 -127]
[ 0.4189 -0.8678 1.3465 -1.9 ]
El peso original de 1.90 se reconstruye exacto porque definió la escala; los otros tres cargan un pequeño error de redondeo. Ese error, multiplicado por miles de pesos y varias pasadas del bucle, ayuda a explicar por qué Pac-Man lite, el juego más complejo con 131.000 parámetros, tiene un porcentaje de frames exactos menor que Flappy Bird. No hay una forma directa de verificar desde afuera, sin acceso al checkpoint, cuánto de ese error viene de la cuantización y cuánto del tamaño del juego; lo más cercano que ofrece el propio repositorio es comparar el porcentaje de frames exactos por juego de la tabla anterior.
El FPU del RP2350 importa en este punto. Sin una unidad de punto flotante en hardware, la dequantización, volver de int8 a valores reales antes de cada operación de atención, sería mucho más lenta, porque el chip tendría que emular esas operaciones en software.
💭 Clave: el ahorro de memoria del bucle y el ahorro de la cuantización son independientes entre sí; tinyworldformer los combina, pero cada uno podría usarse por separado en otro proyecto.
📖 Resumen en Telegram: Ver resumen
Tu próximo paso: cloná el repositorio, corré uv run python -m tinyworldformer.games.play --game breakout y leé el archivo de ese juego dentro de src/tinyworldformer/games/ para ver cuántas líneas de numpy hacen falta para una física jugable completa.
Preguntas frecuentes
¿Qué microcontrolador corre el modelo de mundo comprimido de tinyworldformer?
Una Raspberry Pi Pico 2W, construida sobre el chip RP2350, que tiene dos núcleos y una unidad de punto flotante. El núcleo 1 ejecuta el generador de frames y la red actora; el núcleo 0 atiende USB y la pantalla.
¿Cuántos parámetros tiene el transformer en bucle de cada juego?
Entre 26.000 y 131.000, según la complejidad del juego: Breakout es el más liviano y Pac-Man lite el más pesado, de acuerdo con la tabla publicada en el repositorio.
¿Hace falta GPU para entrenar tinyworldformer antes de cuantizarlo?
El repositorio no detalla el hardware exacto de entrenamiento, pero el generador de frames se entrena sobre partidas grabadas del juego real, un proceso que normalmente se hace fuera del microcontrolador y antes de cuantizar los pesos para desplegarlos en el Pico 2W.
¿Qué diferencia a la red actora de la IA embebida en microcontroladores que genera los frames?
La red actora es una MLP mucho más chica, entre 9.000 y 16.000 parámetros, que se queda en float32 porque su tarea, decidir una acción a partir de un frame, tolera peor el error de cuantización que generar el frame en sí.
¿Se puede correr tinyworldformer en un microcontrolador distinto al Pico 2W?
El repositorio está construido específicamente para el RP2350 del Pico 2W, incluyendo una carpeta en Rust para el firmware del dispositivo. Portarlo a otro chip exigiría al menos una FPU y memoria suficiente para los buffers de atención, pero el propio proyecto no documenta otros objetivos probados.
Referencias
- GitHub: SheepSeb/tinyworldformer: repositorio original con el código, los juegos en numpy y los datos de tamaño y precisión de cada modelo.
- Raspberry Pi: fabricante oficial de la Raspberry Pi Pico 2W y el chip RP2350 usado en el proyecto.
- Wikipedia: Edge computing: contexto sobre mover la inferencia de IA al dispositivo en lugar de a un servidor remoto.
- Wikipedia: Quantization: fundamento matemático de la cuantización usada para pasar los pesos de float32 a int8.
📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.
Imagen destacada: Foto de Random Thinking en Unsplash
¿Te sirvió? ¿Te dio otro error? Contalo abajo: las preguntas se responden y le sirven al siguiente que llegue.
Dejar un comentario
0 Comentarios