16

GAN: el falsificador y el detective

¿Cómo aprende un generador compitiendo contra un crítico?

11 min

GAN: el falsificador y el detective

Con voz. Puedes pausar y saltar a cualquier paso.

Lección completa

Subtítulos

Un falsificador pinta billetes falsos y un detective intenta descubrirlos. Cada vez que el detective lo atrapa, el falsificador aprende a pintar mejor.

Voz

Sin voz: la lección avanza sola con subtítulos.

La voz de esta lección está en preparación: por ahora avanza sola con subtítulos.

0:00 / 0:00

Qué lograrás

  • Explicar qué hace el generador (convierte un número sorteado en un dato falso) y qué hace el discriminador (estima qué tan probable es que un dato sea real).
  • Describir el entrenamiento adversario como un juego por turnos y reconocer su final ideal: el discriminador responde 0.5 porque ya no distingue.
  • Reconocer el colapso de modo y la inestabilidad en un entrenamiento real, y explicar por qué la difusión tomó la delantera en imágenes.

Ideas clave

  1. Una GAN son dos redes que compiten: el generador convierte un número sorteado en un dato falso; el discriminador estima qué tan probable es que un dato sea real.
  2. El generador nunca ve los datos reales: aprende solo a través de la respuesta del discriminador, moviendo sus falsos hacia donde este dice «más real».
  3. El final ideal es un empate: el discriminador responde 0.5 a todo porque ya no distingue. En la GAN de la lección se llega cerca, aunque no del todo.
  4. El colapso de modo (producir solo una parte de la variedad) y la inestabilidad son sus problemas típicos: con un solo ajuste distinto, cuatro de veinte partidas colapsaron.
  5. La difusión tomó la delantera en imágenes (Dhariwal y Nichol, 2021) porque se entrena de forma estable y cubre mejor la variedad; las GAN siguen siendo más rápidas al generar.

Lo que mucha gente cree

Creencia común El generador de una GAN aprende copiando los datos reales que le muestran.

Lo correcto El generador nunca ve un dato real. Solo recibe números al azar y la respuesta del discriminador, y ajusta sus pesos para que esa respuesta sea «real». Los datos reales solo los ve el discriminador.

Compruébalo

Cuatro preguntas. Sin nota ni cuenta: sirven para ver si la idea quedó.

ConceptoDurante el entrenamiento de una GAN, ¿qué recibe el generador?
ConceptoEn el final ideal de una GAN, ¿qué responde el discriminador a cualquier dato?
CálculoEn la partida que colapsó, en la montaña izquierda hay dos falsos por cada dato real. Si el detective solo puede guiarse por el lugar donde cae un número, ¿qué es lo mejor que puede responder ahí?
AplicaciónUna fábrica quiere usar una GAN para generar fotos de defectos raros y así entrenar su sistema de inspección. Las fotos generadas se ven muy realistas. ¿Qué conviene revisar antes de usarlas?

Por dentroPara quien quiere las fórmulas y el código. No hace falta para seguir el curso.

El juego en una fórmula

El generador GG recibe ruido z∼pzz \sim p_z (en la lección, la campana estándar) y produce G(z)G(z). El discriminador DD devuelve D(x)∈(0,1)D(x) \in (0, 1), la probabilidad que asigna a que xx venga de los datos. Goodfellow et al. (2014) plantean un juego de dos jugadores de tipo minimax:

min⁡Gmax⁡D  V(D,G)=Ex∼pdatos[log⁡D(x)]+Ez∼pz[log⁡(1−D(G(z)))]\min_G \max_D \; V(D, G) = \mathbb{E}_{x \sim p_{\text{datos}}}\big[\log D(x)\big] + \mathbb{E}_{z \sim p_z}\big[\log\big(1 - D(G(z))\big)\big]

DD sube VV diciendo «1» a los reales y «0» a los falsos (es la entropía cruzada de un clasificador binario, con signo cambiado); GG la baja haciendo que D(G(z))D(G(z)) se acerque a 1. Lo que gana uno lo pierde el otro: es un juego de suma cero.

El mejor detective posible y el empate

Con GG fijo, el discriminador óptimo es

D∗(x)=pdatos(x)pdatos(x)+pg(x)D^*(x) = \frac{p_{\text{datos}}(x)}{p_{\text{datos}}(x) + p_g(x)}

donde pgp_g es la distribución de los falsos. Si en una zona hay dos falsos por cada real, lo mejor que puede responder ahí es 1/31/3. Es lo que se mide en la partida que colapsa: en la montaña izquierda (todos los falsos y la mitad de los reales) el detective responde entre 0.32 y 0.35; en la derecha, donde no hay falsos, 1.00. El artículo demuestra que el juego tiene su óptimo global en pg=pdatosp_g = p_{\text{datos}}, con D=12D = \tfrac12 en todas partes. Ahí la pérdida del detective vale 2ln⁡2≈1.3862\ln 2 \approx 1.386 y la del generador (versión no saturante, abajo) ln⁡2≈0.693\ln 2 \approx 0.693; en los últimos doscientos pasos de la partida sana de la lección promedian 1.384 y 0.702.

El truco no saturante y los kk pasos

Al principio, DD rechaza los falsos con mucha seguridad y log⁡(1−D(G(z)))\log(1 - D(G(z))) se satura: casi no da gradiente. Por eso el mismo artículo propone que GG maximice log⁡D(G(z))\log D(G(z)), que tiene el mismo punto fijo y gradientes más fuertes al inicio. Es lo que usa la lección. El gradiente que corrige a GG pasa por DD (retropropagación a través de las dos redes, lección 8): para cada falso, ∂log⁡D/∂x\partial \log D / \partial x dice hacia dónde moverlo. Las flechas de la escena son esa derivada, calculada con la curva real del detective.

El Algoritmo 1 alterna kk pasos de DD con uno de GG; los autores usaron k=1k = 1. En la lección, kk es el único hiperparámetro que cambia: con k=1k = 1, las 20 semillas cubren las dos montañas; con k=5k = 5, 15 las cubren, 4 colapsan (una montaña recibe menos de 5 % de los falsos) y una queda a medias (48 % y 6 %). El artículo advierte del desequilibrio contrario, entrenar demasiado a GG sin actualizar a DD, que llama «el escenario Helvetica»: GG manda demasiados valores de zz al mismo xx. En los dos sentidos, el equilibrio entre los jugadores importa.

La GAN de la lección

  • Datos: mezcla 50/50 de N(−1.5, 0.42)\mathcal N(-1.5,\ 0.4^2) y N(1.5, 0.42)\mathcal N(1.5,\ 0.4^2). Los 800 reales de la escena son cuantiles de cada campana, y los 800 sorteos del generador, cuantiles de N(0,1)\mathcal N(0, 1): así cada histograma es reproducible.
  • Redes: GG y DD son iguales, 1→10 tanh⁡→10 tanh⁡→11 \to 10\ \tanh \to 10\ \tanh \to 1 (141 parámetros cada una). Adam con tasa 5×10−45 \times 10^{-4} y β1=0.5\beta_1 = 0.5, lotes de 256, 2000 pasos del generador. Los pesos se exportaron cuantizados a 16 bits y el navegador ejecuta las redes (solo la pasada hacia delante); los scripts train_gan.py y train_detective.py están en la carpeta de la lección.
  • El detective solo: para presentarlo aparte, se entrenó el detective inicial de la partida sana durante 1000 pasos contra el falsificador inicial congelado (todo cerca de 0). Responde 0.95 a 1.6 y 0.05 a 0.07.
  • «Cerca de una montaña»: a menos de 0.8 de su centro. Los reales: 47.75 % en cada una. Los falsos al final de la partida sana: 46.4 % y 48.1 %.
  • Lo que no salió perfecto: las montañas falsas son más angostas (la más alta junta 337 de 800 falsos en un tramo de 0.25; la real, 98), y queda un puente de falsos en el valle. El puente es inevitable con este diseño: GG es una función continua de un solo número, así que los zz cercanos a 0 caen entre las dos montañas. Ahí D(0)≈0.30D(0) \approx 0.30. El detective, pequeño y entrenado al mismo ritmo, no detecta lo angosto.

En código

import torch, torch.nn as nn
torch.manual_seed(0)

def red():   # 1 → 10 → 10 → 1, con tanh: 141 parámetros
    return nn.Sequential(nn.Linear(1, 10), nn.Tanh(), nn.Linear(10, 10), nn.Tanh(), nn.Linear(10, 1))

def reales(n):   # mezcla 50/50 de N(−1.5, 0.4²) y N(+1.5, 0.4²)
    signo = torch.randint(0, 2, (n, 1)) * 2 - 1
    return 1.5 * signo + 0.4 * torch.randn(n, 1)

G, D = red(), red()               # falsificador y detective
opt_G = torch.optim.Adam(G.parameters(), lr=5e-4, betas=(0.5, 0.999))
opt_D = torch.optim.Adam(D.parameters(), lr=5e-4, betas=(0.5, 0.999))
bce = nn.BCEWithLogitsLoss()      # aplica la sigmoide y calcula −log
uno, cero = torch.ones(256, 1), torch.zeros(256, 1)

for paso in range(2000):
    # turno del detective: a los reales debe decir 1; a los falsos, 0
    falsos = G(torch.randn(256, 1)).detach()
    perdida_D = bce(D(reales(256)), uno) + bce(D(falsos), cero)
    opt_D.zero_grad(); perdida_D.backward(); opt_D.step()
    # turno del falsificador: que el detective diga «real» a sus falsos
    perdida_G = bce(D(G(torch.randn(256, 1))), uno)
    opt_G.zero_grad(); perdida_G.backward(); opt_G.step()

with torch.no_grad():
    x = G(torch.randn(10000, 1))
    izq = ((x + 1.5).abs() < 0.8).float().mean().item()
    der = ((x - 1.5).abs() < 0.8).float().mean().item()
    print(f'falsos cerca de cada montaña: {izq:.2f} y {der:.2f}')
    dr, df = torch.sigmoid(D(reales(10000))).mean().item(), torch.sigmoid(D(x)).mean().item()
    print(f'detective, en promedio: reales {dr:.2f}, falsos {df:.2f}')
# falsos cerca de cada montaña: 0.47 y 0.48
# detective, en promedio: reales 0.50, falsos 0.50

(PyTorch 2.11 en CPU; otra versión puede dar cifras algo distintas.)

Lo que viene

Las GAN dominaron la generación de imágenes de alta calidad hasta que Dhariwal y Nichol (2021) mostraron que los modelos de difusión las superaban. Ellos mismos señalan que las GAN capturan menos variedad y son difíciles de entrenar, «colapsando sin hiperparámetros y regularizadores cuidadosamente elegidos», mientras que la difusión ofrece cobertura de la distribución, un objetivo de entrenamiento estacionario (no depende de un rival que cambia) y facilidad para escalar; su desventaja es que genera más lento, en muchos pasos de quitar ruido. Eso es la lección 17.

Lente de decisión

Qué significa esta idea si tu trabajo es decidir, no programar.

  • El ojo humano ya no es un detector confiable: en el estudio de Nightingale y Farid (2022), 315 personas acertaron 48.2 % al separar rostros reales de rostros hechos con StyleGAN2, y con entrenamiento y retroalimentación llegaron solo a 59.0 %. Verificar autenticidad exige procedencia y herramientas, no intuición.
  • Si un proveedor genera datos sintéticos (para entrenar o para probar), pregunta cómo midió la variedad: el colapso de modo puede borrar justo los casos raros que más importan, sin que las muestras «se vean mal».
  • Entrenar una GAN es frágil: pide resultados de varias corridas y semillas, no una sola demostración. En la lección, cambiar un ajuste hizo colapsar cuatro de veinte partidas.
  • Elige por el uso: la difusión da más calidad y variedad pero genera en muchos pasos; una GAN genera en una sola pasada, útil cuando la velocidad manda (tiempo real, dispositivos pequeños).

Términos de la lección

Transcripción completa
ComentariosConversación pública de esta lección: dudas, datos por revisar, ideas.

Los comentarios viven en GitHub Discussions, en el repositorio público del curso; para escribir se necesita una cuenta de GitHub. Se cargan desde giscus.app al abrir esta sección. Las personas que revisan el curso los leen, y si un cambio se acepta, se aplica en los dos idiomas. Abrir el hilo en GitHub

Fuentes