Un modelo que pinta acuarelas escribiendo JavaScript
El 23 de agosto, Surya Narreddi publicó un video que se volvió viral —más de 1,5 millones de vistas al momento de escribir esta nota— en el que un modelo de lenguaje pinta acuarelas. El truco no está en generar píxeles: el modelo escribe alrededor de 150 líneas de JavaScript usando la librería p5.brush (de Alejandro Campos Uribe), que simula un medio —pigmento que se corre, papel con textura, trazos con masa— en lugar de dibujar formas geométricas. La pieza original, la idea y el método son de Narreddi; el artículo que nos ocupa es la reproducción abierta del lado ingenieril, publicada en el blog de Hugging Face.
Lo que el autor del blog aporta es infraestructura: el pool de referencia, el entorno de RL, los scripts de entrenamiento y los modelos entrenados, todo abierto y ejecutándose en Hugging Face de extremo a extremo (Jobs, Spaces, Inference Providers y Hub).
Cómo se entrena el «gusto» de un modelo
La mayoría del trabajo reciente de RL sobre modelos de lenguaje se apoya en recompensas verificables: problemas de matemáticas con respuesta conocida, código que pasa tests, correctores binarios baratos. Este proyecto cae en la excepción clásica: RLHF, donde el modelo aprende de preferencias humanas. La diferencia clave es que aquí la recompensa es estética, y no hay una respuesta correcta.
🤖 La IA no es solo para leer sobre ella
En la comunidad la aplicamos: automatización, agentes IA y herramientas reales para emprender, no solo para informarte.
👥 Aplicarla en la comunidadLa función de recompensa implementada tiene cuatro términos, con los pesos que Narreddi convergió:
- gate (0,05): la composición compila, pinta algo y no hace trampa.
- length (0,05): un empujón suave hacia snippets de código más largos.
- pairwise judge (0,60): estilo, comparado contra referencias tomadas del pool.
- HPSv3 (0,30): preferencia estética sobre el render final.
HPSv3 es un modelo de preferencias abierto de 7B entrenado sobre un gran conjunto de elecciones humanas entre pares de imágenes; devuelve un puntaje promedio de «cuánto preferiría una persona esa imagen». El pairwise judge es Qwen3-VL-30B-A3B-Instruct, un modelo de visión general invocado vía HF Inference Providers, que ve la pintura candidata junto a cuatro referencias extraídas al azar del pool, recibe una guía escrita (sangrados, lavados translúcidos, bordes suaves) y se evalúa en ambos órdenes de presentación. Su estándar es el pool: su puntaje es el gusto del autor, codificado en esas calificaciones.
El pool es la verdadera materia prima
El pool tiene 178 pinturas organizadas en dos niveles (love y okay) según preferencia personal. Todas fueron generadas por modelos —cuatro familias open-weight llamadas vía Inference Providers escribieron sketches de p5.brush, cada una trabajando sobre una foto de hibisco con licencia abierta de iNaturalist, con un modelo de visión dando feedback escrito durante tres rondas de refinamiento. Los 178 renders finales fueron calificados uno a uno, a mano.
Cuando el pairwise judge toma cuatro referencias, la mitad salen de love y la mitad de okay, de modo que la política siempre tenga rivales a los que a veces pueda ganar. Este es uno de los pocos cambios deliberados respecto a la receta original de Narreddi, que comparaba solo contra el tier superior.
El artículo reconoce una limitación real: no hay pintura humana en el pool. p5.brush es una librería nicho, y el trabajo humano accesible con código es apenas un puñado de piezas, lejos de un corpus de entrenamiento.
Lo que aprendieron (y lo que rompieron)
Entrenaron tres runs con la misma recompensa, variando solo cómo se reparte el peso entre los dos jueces:
| run | pairwise judge | HPSv3 | pasos | Δ group reward (primer vs último tercio) |
|---|---|---|---|---|
| hps-only | 0,00 | 0,90 | 60 | +0,13 |
| judge-led | 0,60 | 0,30 | 110 | +0,27 |
| hps-led | 0,30 | 0,60 | 110 | +0,24 |
Cuanto más peso lleva el pairwise judge, más bajo empieza el run y más ruidosa es la subida. judge-led pasó sus primeros 30 pasos casi plano antes de moverse. Las tres curvas terminaron aprendiendo, y el pairwise judge subió de forma independiente en los dos runs que lo usaron: el modelo gana más comparaciones contra el pool a medida que avanza el entrenamiento, exactamente la afirmación que hps-only no puede sostener.
Un hallazgo contraintuitivo: en hps-only, tres cuartos de la subida viene de que las pinturas malas se vuelven raras. El learning se ve en el medio de la distribución, no en el mejor cuadro. El pairwise judge cambia el techo: en los runs con juez, el mejor cuadro sube, y la cobertura de pintura se duplica (0,11 → 0,23 en judge-led; 0,13 → 0,30 en hps-led), mientras que en hps-only apenas se mueve.
El modelo también ignora una instrucción explícita del prompt —quince a treinta formas rellenas— y tiene razón en ignorarla: la política no es recompensada por obedecerla, y la correlación entre n_shapes y reward es esencialmente cero.
Bugs y bloqueos en el camino
Antes de que algo aprendiera hubo una larga meseta de curvas planas. Los autores describen cuatro cambios que destrabaron el primer run exitoso:
- Learning rate de 2e-5 a 5e-5 (el techo que LoRA Without Regret usa para GRPO).
- Scheduler de linear a constantwithwarmup — la decaída lineal había gastado la mayoría del LR a mitad del run.
- scale_rewards de group a none — un rechazo del gate estaba encogiendo todas las ventajas del grupo.
- target_modules para LoRA: pasar a all-linear, porque Qwen/Qwen3.5-35B-A3B es una mezcla de expertos con nombres de proyecciones distintos; la lista habitual entrenaba 10 capas de 40.
También encontraron un bug en OpenEnv: el cliente mantiene un websocket persistente, y un socket cerrado por el extremo remoto quedaba cacheado, así que cada llamada posterior fallaba aunque el entorno estuviera sano. Costó dos runs medio terminados. El fix fue enviado upstream y los runs con la versión parcheada corrieron limpios.
Una métrica práctica: los fallos de infraestructura entraban a la recompensa como ceros. Con la corrección, esos paths devuelven None y el rollout se excluye del grupo, en vez de entrenar al modelo con ruido.
¿Qué significa esto para tu startup?
Este proyecto es, en esencia, una receta abierta para enseñarle a un modelo tu propio gusto, no el de la media estadística. Si tu producto necesita que un LLM genere outputs con un estilo específico —el tono de tu marca, la estética de tu UI, una voz muy concreta— la lección concreta es que el pool curado a mano pesa más que el algoritmo. La recompensa de las matemáticas es automática; la recompensa del arte, no.
Tres cosas accionables que te llevas:
- Empieza con un HPS-only o un juez general barato para validar que tu pipeline aprende, y solo después enciende el juez caro. Esta misma estrategia —reducir el problema hasta que algo aprende, y luego agregar las partes difíciles una a una— está detrás de cada run exitoso del proyecto.
- Trata el pool como un dataset de producto, no como un detalle. Si tu pool está sesgado hacia un solo sujeto (hibiscos, en este caso), tu modelo aprenderá a pintar ese sujeto. La diversidad de output depende de la diversidad del pool.
- Antes de GRPO, considera SFT sobre las fuentes del pool (la lista de ideas pendientes lo menciona). Es uno de los siguientes pasos más obvios para cualquiera que replique la receta.
Para founders que ya trabajan con Hugging Face, todo el pipeline corre sobre su infraestructura estándar: Jobs para entrenamiento, Spaces para el entorno y el scorer, Inference Providers para el juez. Eso baja la barrera de entrada a un experimento que, hasta hace poco, requería orquestar infraestructura propia.
Limitaciones que la nota misma señala
El artículo es honesto sobre lo que no resuelve:
- No hay feedback visual durante el entrenamiento. El modelo pinta «con los ojos cerrados»: ningún render entra como entrada, solo un número. La evidencia de que un loop de retroalimentación funciona está en cómo se hizo el pool —los modelos iteraron tres rondas bajo un crítico visual— pero la política entrenada nunca ve ese loop.
- No está claro cuánto más se podría escalar hps-only. Si cada rollout igualara a sus mejores casos, la media se quedaría en 0,771; si más pasos romperían ese techo es una pregunta abierta.
- El pool define lo que el modelo considera bello, y el pool es una decisión humana, no algorítmica. Es el cuello de botella del pipeline, y no tiene respuesta de principio.
Fuentes
- Training a coding model to paint watercolours with TRL and OpenEnv (fuente original)
- Hugging Face — Wikipedia
🤖 La IA no es solo para leer sobre ella
En la comunidad la aplicamos: automatización, agentes IA y herramientas reales para emprender, no solo para informarte.
👥 Aplicarla en la comunidad













