Phase 08 - Lesson 03
GANs — Generador vs Discriminador
El truco de Goodfellow en 2014 fue omitir la densidad por completo. Dos redes. Una crea falsificaciones. Otra las atrapa. Luchan hasta que las falsificaciones son indistinguibles de las reales. No debería funcionar. A menudo no funciona. Cuando lo hace, las muestras siguen siendo las más nítidas de la literatura para dominios estrechos.
Tipo: Build Lenguajes: Python Requisitos previos: Fase 3 · 02 (Backprop), Fase 3 · 08 (Optimizers), Fase 8 · 02 (VAE) Tiempo: ~75 minutos
El Problema
Los VAE producen muestras borrosas porque la pérdida del decodificador MSE es óptima según Bayes para la imagen promedio, y el promedio de muchos dígitos plausibles es un dígito difuso. Quieres una pérdida que recompense la plausibilidad, no la proximidad píxel por píxel a un objetivo específico. No existe una forma cerrada para la plausibilidad. Tienes que aprenderla.
La idea de Goodfellow: entrenar un clasificador D(x) para distinguir imágenes reales de falsas. Entrenar un generador G(z) para engañar a D. La señal de pérdida para G es lo que D crea actualmente que hace que algo parezca real. Esta señal se actualiza a medida que G mejora, persiguiendo un objetivo en movimiento. Si ambas redes convergen, G habrá aprendido la distribución de datos sin necesidad de escribir log p(x).
Esto es entrenamiento adversarial. La matemática es un juego minimax:
min_G max_D E_real[log D(x)] + E_fake[log(1 - D(G(z)))]
En 2026, las GAN ya no son el generador SOTA (la difusión y el flow matching se quedaron con esa corona). Pero StyleGAN 2/3 siguen siendo los modelos de rostros más nítidos jamás creados, los discriminadores de GAN se utilizan como pérdidas perceptuales en el entrenamiento de difusión, y el entrenamiento adversarial impulsa las destilaciones rápidas de 1 paso (SDXL-Turbo, SD3-Turbo, LCM) que te permiten implementar difusión en tiempo real.
El Concepto
Generador G(z). Mapea un vector de ruido z ~ N(0, I) a una muestra x̂. Una red con forma de decodificador (densa o convolucional transpuesta).
Discriminador D(x). Mapea una muestra a una probabilidad escalar (o puntuación/score). Real → 1, falsa → 0.
Pérdida. Dos actualizaciones alternas:
- Entrenar
D:loss_D = -[ log D(x) + log(1 - D(G(z))) ]. Entropía cruzada binaria con real=1, falso=0. - Entrenar
G:loss_G = -log D(G(z)). Esta es la forma no saturada (non-saturating) que usó Goodfellow (la originallog(1 - D(G(z)))se satura y elimina los gradientes cuandoDtiene confianza).
Bucle de entrenamiento. Un paso de D, un paso de G. Repetir.
Por qué funciona. Si G coincide perfectamente con p_data, entonces D no puede hacerlo mejor que el azar y produce 0.5 en todas partes; G no recibe más gradiente. Equilibrio.
Por qué falla. Colapso de modo (mode collapse — G encuentra un modo que D no puede clasificar y lo produce para siempre), gradiente desvanecido (D aprende demasiado rápido y log D se satura), inestabilidad en el entrenamiento (tasas de aprendizaje, tamaños de lote, lo que sea).
Variantes que hicieron que las GAN funcionaran
| Año | Innovación | Solución |
|---|---|---|
| 2015 | DCGAN | Conv/deconv, batch norm, LeakyReLU — la primera arquitectura estable. |
| 2017 | WGAN, WGAN-GP | Reemplaza BCE con la distancia de Wasserstein + penalización de gradiente (gradient penalty). Corrige el gradiente desvanecido. |
| 2017 | Normalización espectral | Limita al discriminador por Lipschitz. Aún se usa en discriminadores de 2026. |
| 2018 | Progressive GAN | Entrena primero en baja resolución, añade capas. Primeros resultados en megapíxeles. |
| 2019 | StyleGAN / StyleGAN2 | Red de mapeo (mapping network) + normalización de instancia adaptativa (adaptive instance norm). Estado del arte para fotorrealismo de dominio fijo. |
| 2021 | StyleGAN3 | Libre de aliasing (alias-free), equivariante a traslación — sigue siendo el estándar de oro para rostros en 2026. |
| 2022 | StyleGAN-XL | Condicional, consciente de la clase (class-aware), escala mayor. |
| 2024 | R3GAN | Rebranding con regularización más fuerte; funciona en 1024² sin trucos. |
Build It
code/main.py entrena una pequeña GAN con datos 1-D: una mezcla de dos gaussianas. El generador y el discriminador son MLP de una sola capa oculta. Implementamos las pasadas hacia adelante (forward), hacia atrás (backward) y el bucle minimax a mano. El objetivo es ver los dos modos de falla clave (colapso de modo + gradiente desvanecido) a medida que ocurren.
Paso 1: pérdida no saturada
La pérdida clásica de Goodfellow log(1 - D(G(z))) tiende a 0 cuando D clasifica la muestra falsa de G como falsa con alta confianza. En ese punto, el gradiente para G es básicamente cero; G no puede mejorar. La forma no saturada -log D(G(z)) tiene la asíntota opuesta: explota cuando D tiene confianza, dando a G una señal fuerte.
def g_loss(d_fake):
# maximize log D(G(z)) <=> minimize -log D(G(z))
return -sum(math.log(max(p, 1e-8)) for p in d_fake) / len(d_fake)
Paso 2: un paso de discriminador por paso de generador
for step in range(steps):
# train D
real_batch = sample_real(batch_size)
fake_batch = [G(z) for z in sample_noise(batch_size)]
update_D(real_batch, fake_batch)
# train G
fake_batch = [G(z) for z in sample_noise(batch_size)] # fresh fakes
update_G(fake_batch)
Muestras falsas frescas para G, de lo contrario los gradientes quedan obsoletos.
Paso 3: vigilar el colapso de modo
if step % 200 == 0:
samples = [G(z) for z in sample_noise(500)]
mode_a = sum(1 for s in samples if s < 0)
mode_b = 500 - mode_a
if min(mode_a, mode_b) < 50:
print(" [!] mode collapse: one mode is starved")
El síntoma canónico: uno de los dos modos reales deja de generarse. El discriminador deja de corregirlo porque nunca se ve como una falsificación.
Trampas
- Discriminador demasiado fuerte. Reduce la tasa de aprendizaje de D a 2-5x, o añade ruido de instancia/capa. Si D alcanza una precisión >95%, G está muerto.
- El generador memoriza un modo. Añade ruido a las entradas de D, usa una capa de discriminador de minilotes (minibatch-discriminator), o cambia a WGAN-GP.
- Fuga de estadísticas en Batch Norm. El flujo del lote real + lote falso a través de la misma capa de BN mezcla sus estadísticas. En su lugar, usa normalización de instancia (instance norm) o normalización espectral (spectral norm).
- Manipulación de Inception Score. FID e IS son ruidosos con recuentos de muestras bajos. Usa ≥10k muestras en la evaluación.
- El muestreo de un solo paso es una mentira para tareas condicionales. Aún necesitas escalas CFG, trucos de truncamiento y remuestreo para obtener salidas útiles.
Use It
La pila de GAN en 2026:
| Situación | Elección |
|---|---|
| Rostos humanos fotorrealistas, pose fija | StyleGAN3 (más nítido, más pequeño) |
| Rostros de anime / estilizados | StyleGAN-XL o Stable Diffusion LoRA |
| Traducción de imagen a imagen | Pix2Pix / CycleGAN (Fase 8 · 04) o ControlNet (Fase 8 · 08) |
| Texto a imagen rápido de 1 paso | Destilación adversarial de difusión (SDXL-Turbo, SD3-Turbo) |
| Pérdida perceptual dentro de un entrenador de difusión | Discriminador GAN pequeño en recortes de imagen (crops) |
| Cualquier cosa multimodal o abierta | No lo uses — usa difusión o flow matching |
Las GAN son nítidas pero estrechas. Una vez que tu dominio se expande (fotos, prompts de texto arbitrarios, video), cambia a la difusión. El truque adversarial sobrevive como un componente (pérdidas perceptuales, destilación), no como un generador independiente.
Ship It
Guarda outputs/skill-gan-debugger.md. La Skill toma una ejecución fallida de GAN (curvas de pérdida, cuadrícula de muestras, tamaño del conjunto de datos) y produce una lista clasificada de causas probables, soluciones de una sola línea y un protocolo de reejecución.
Ejercicios
- Fácil. Ejecuta
code/main.pycon la configuración predeterminada. Luego estableceD_LR = 5 * G_LRy vuelve a ejecutar. ¿Qué tan rápido se colapsa la pérdida de G a una constante? - Medio. Reemplaza la pérdida clásica BCE de Goodfellow con la pérdida de WGAN:
loss_D = E[D(fake)] - E[D(real)],loss_G = -E[D(fake)], y recorta los pesos de D a[-0.01, 0.01]. ¿Es el entrenamiento más estable? Compara la convergencia en tiempo real. - Difícil. Extiende el ejemplo 1-D a datos 2-D (mezcla de 8 gaussianas en un anillo). Realiza un seguimiento de cuántos de los 8 modos captura el generador en los pasos 1k, 5k y 10k. Implementa la discriminación por minilotes (minibatch discrimination) y vuelve a medir.
Términos Clave
| Término | Lo que la gente dice | Lo que realmente significa |
|---|---|---|
| Generador | "G" | Red de ruido a muestra, G: z → x̂. |
| Discriminador | "D" | Clasificador D: x → [0, 1], real vs falsa. |
| Minimax | "El juego" | min_G max_D of a joint objective. |
| Pérdida no saturada | "La solución" | Usar -log D(G(z)) para G en lugar de log(1 - D(G(z))). |
| Colapso de modo | "G memorizó una cosa" | El generador o produce pocas salidas distintas a pesar de la diversidad de datos. |
| WGAN | "Wasserstein" | Reemplaza BCE con la distancia Earth-Mover + penalización de gradiente; gradiente más suave. |
| Normalización espectral | "Truco de Lipschitz" | Limita las normas de los pesos de D para acotar su pendiente; estabiliza el entrenamiento. |
| StyleGAN | "El que funciona" | Red de mapeo + AdaIN; el mejor de su clase para rostros, aún en 2026. |
Nota de producción: la inferencia de un solo paso es la ventaja duradera de las GAN
Las GAN ya no ganan en calidad de muestra para la generación de dominio abierto, pero siguen ganando en costo de inferencia. En el vocabulario de la literatura de inferencia en producción, una GAN tiene:
- Sin etapas de prefill ni decode. Una sola pasada hacia adelante de
G(z). TTFT ≈ latência total. - Sin presión de KV-cache. El único estado son los pesos. El tamaño del lote está limitado por la memoria de activación, no por la caché.
- Procesamiento por lotes continuo trivial. Dado que cada solicitud requiere la misma cantidad de FLOP fijos, un lote estático en la ocupación objetivo del servidor suele ser óptimo. No se necesita un programador dinámico (in-flight scheduler).
Por esto la destilación de GAN (SDXL-Turbo, SD3-Turbo, ADD, LCM) es la técnica dominante para texto a imagen rápido en 2026: colapsa un pipeline de difusión de 20-50 pasos en 1-4 pasadas hacia adelante de estilo GAN, mientras mantiene la distribución de una base de difusión. La pérdida adversarial sobrevive como un control en el tiempo de entrenamiento para convertir generadores lentos en rápidos.
Lecturas Adicionales
- Goodfellow et al. (2014). Generative Adversarial Nets — el artículo original de GAN.
- Radford et al. (2015). Unsupervised Representation Learning with DCGAN — la primera arquitectura estable.
- Arjovsky, Chintala, Bottou (2017). Wasserstein GAN — WGAN.
- Miyato et al. (2018). Spectral Normalization for GANs — SN.
- Karras et al. (2020). Analyzing and Improving the Image Quality of StyleGAN — StyleGAN2.
- Karras et al. (2021). Alias-Free Generative Adversarial Networks — StyleGAN3.
- Sauer et al. (2023). Adversarial Diffusion Distillation — SDXL-Turbo.