15

Generar es aprender una distribución

¿Qué significa que una red "genere" algo nuevo?

10 min

Generar es aprender una distribución

Con voz. Puedes pausar y saltar a cualquier paso.

Lección completa

Subtítulos

En la parte uno, las redes aprendieron a decidir: miran un dibujo como este y responden: es un siete.

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 la diferencia entre clasificar (trazar una frontera) y generar (conocer la distribución de los datos y sacar muestras nuevas de ella).
  • Describir, pieza por pieza, cómo un autocodificador resume una imagen en dos números y la reconstruye, y qué es el espacio latente.
  • Reconocer por qué el plano de un autocodificador simple tiene huecos y cómo las nubes de un VAE los rellenan para poder muestrear.

Ideas clave

  1. Clasificar es trazar una frontera entre grupos. Generar pide más: conocer la distribución de los datos, dónde se apiñan y hasta dónde llegan, para sacar muestras nuevas.
  2. Muestrear es sacar un dato nuevo siguiendo la distribución: sale más seguido lo común y pocas veces lo raro.
  3. Los píxeles de una imagen dependen unos de otros: sacarlos por separado da manchas. Por eso se comprime cada imagen en pocos números que sí basten para rehacerla.
  4. Un autocodificador tiene dos piezas: el codificador resume la imagen en un código (aquí, dos números) y el decodificador la reconstruye. El plano de los códigos es el espacio latente.
  5. El plano de un autocodificador simple queda desordenado y con huecos. Un VAE resume cada imagen en una nube y las junta en una zona conocida: así se puede muestrear. El precio, imágenes algo borrosas.

Lo que mucha gente cree

Creencia común La IA generativa recorta y pega trozos de imágenes que tiene guardadas: es un collage.

Lo correcto Aprende una distribución: guarda pesos, no imágenes, y genera sacando un punto al azar y decodificándolo. Lo generado se parece a los datos sin copiar ninguno. La excepción es la memorización (sobre todo de imágenes muy repetidas en los datos): un fallo real que se mide y se intenta evitar.

Compruébalo

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

Concepto¿Qué distingue a un modelo generativo de uno que solo clasifica?
Concepto¿Por qué un VAE permite muestrear mejor que un autocodificador simple?
CálculoEl autocodificador de la lección resume cada imagen de 8 × 8 píxeles en un código de 2 números. ¿Cuántas veces menos números tiene el código que la imagen?
AplicaciónUn proveedor te muestra cinco imágenes sintéticas impecables de su generador para que lo compres. ¿Qué pides primero?

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

Clasificar frente a generar

Un modelo que clasifica aprende p(y∣x)p(y\mid x): dado el dato, la etiqueta; le basta la frontera. Un modelo generativo aprende la distribución de los propios datos, pθ(x)p_\theta(x), y permite muestrear x∼pθ(x)x\sim p_\theta(x). Se entrena, en general, por máxima verosimilitud:

θ⋆=arg⁡max⁡θ  1N∑i=1Nlog⁡pθ(xi)\theta^\star=\arg\max_\theta\;\frac1N\sum_{i=1}^{N}\log p_\theta(x_i)

La nube de los sietes

Cada dígito se mide con m=(m1,m2)m=(m_1,m_2): la tinta de su mitad de arriba y la de su mitad de abajo (cada píxel vale de 0 a 1; cada mitad tiene 32 píxeles). Los óvalos de la lección son una gaussiana en dos dimensiones ajustada por máxima verosimilitud a los 179 sietes:

μ^=1N∑imi=(10.82,  8.14),Σ^=1N∑i(mi−μ^)(mi−μ^)⊤=(2.24−0.65−0.652.45)\hat\mu=\frac1N\sum_i m_i=(10.82,\;8.14),\qquad \hat\Sigma=\frac1N\sum_i (m_i-\hat\mu)(m_i-\hat\mu)^\top=\begin{pmatrix}2.24&-0.65\\-0.65&2.45\end{pmatrix}

El óvalo pequeño es (m−μ^)⊤Σ^−1(m−μ^)=1(m-\hat\mu)^\top\hat\Sigma^{-1}(m-\hat\mu)=1 y el grande, =4=4. En una gaussiana de dos dimensiones caen dentro 1−e−1/2≈39 %1-e^{-1/2}\approx 39\,\% y 1−e−2≈86 %1-e^{-2}\approx 86\,\% de las muestras. Para muestrear se toma ε∼N(0,I)\varepsilon\sim\mathcal N(0,I) y m=μ^+Lεm=\hat\mu+L\varepsilon, con LL⊤=Σ^LL^\top=\hat\Sigma (Cholesky). La frontera entre seises y sietes de la escena es la del análisis discriminante lineal: la recta donde las dos gaussianas, con su covarianza promedio, dan la misma densidad.

Por qué no basta con cada píxel por separado

Sacar cada píxel de su propia distribución equivale a suponer p(x)=∏j=164p(xj)p(x)=\prod_{j=1}^{64}p(x_j): píxeles independientes. En los dígitos es falso (un trazo enciende a sus vecinos), y por eso salen manchas. Hace falta un modelo de cómo varían juntos.

Autocodificador

Un codificador z=Eϕ(x)z=E_\phi(x) y un decodificador x^=Dθ(z)\hat x=D_\theta(z), entrenados juntos para minimizar el error de reconstrucción:

LAE=∥x−x^∥2=∑j=164(xj−x^j)2\mathcal L_{\text{AE}}=\lVert x-\hat x\rVert^2=\sum_{j=1}^{64}(x_j-\hat x_j)^2

Los modelos de la lección son diminutos: 64→32→2→32→6464\to32\to2\to32\to64 con tanh⁡\tanh en la capa oculta y un sigmoide a la salida (4354 parámetros), entrenados con descenso del gradiente (Adam) sobre 1500 imágenes; otras 297 se reservaron para reconstruir imágenes que la red nunca vio. Error medio por imagen reservada según el tamaño del código: 3.14 con 1 número, 2.34 con 2, 1.46 con 4, 0.95 con 8 y 0.54 con 16 (adivinar siempre la imagen media daría 4.71). Con decodificador lineal y error cuadrático, un autocodificador aprende el mismo subespacio que el PCA (Goodfellow et al., 2016, §14.1); las capas no lineales lo generalizan, y Hinton y Salakhutdinov (2006) mostraron que los autocodificadores profundos aprenden códigos mucho mejores que el PCA.

Autocodificador variacional (VAE)

Se postula un código con distribución previa conocida, p(z)=N(0,I)p(z)=\mathcal N(0,I) (la «zona redonda»), y un decodificador pθ(x∣z)=N(Dθ(z),σobs2I)p_\theta(x\mid z)=\mathcal N(D_\theta(z),\sigma_{\text{obs}}^2 I). El codificador devuelve una nube, qϕ(z∣x)=N(μϕ(x),diag⁡σϕ2(x))q_\phi(z\mid x)=\mathcal N\big(\mu_\phi(x),\operatorname{diag}\sigma^2_\phi(x)\big): su centro y su tamaño. En el entrenamiento se saca un punto con el truco de reparametrización, que deja pasar el gradiente:

z=μ+σ⊙ε,ε∼N(0,I)z=\mu+\sigma\odot\varepsilon,\qquad \varepsilon\sim\mathcal N(0,I)

Lo que se maximiza es una cota inferior de log⁡pθ(x)\log p_\theta(x) (la ELBO):

log⁡pθ(x)  ≥  Eqϕ(z∣x)[log⁡pθ(x∣z)]−DKL(qϕ(z∣x) ∥ p(z))\log p_\theta(x)\;\ge\;\mathbb E_{q_\phi(z\mid x)}\big[\log p_\theta(x\mid z)\big]-D_{\mathrm{KL}}\big(q_\phi(z\mid x)\,\Vert\,p(z)\big)

El primer término es el error de reconstrucción; el segundo, la penalización que acerca cada nube a la zona estándar. Para gaussianas diagonales tiene forma cerrada:

DKL=12∑k(μk2+σk2−ln⁡σk2−1)D_{\mathrm{KL}}=\tfrac12\sum_{k}\big(\mu_k^2+\sigma_k^2-\ln\sigma_k^2-1\big)

La pérdida usada en la lección es ∥x−x^∥2/(2σobs2)+DKL\lVert x-\hat x\rVert^2/(2\sigma_{\text{obs}}^2)+D_{\mathrm{KL}} con σobs2=0.1\sigma_{\text{obs}}^2=0.1 (4420 parámetros). Los dos términos compiten: la reconstrucción separa y encoge las nubes; la penalización las junta y las ensancha. Para generar: z∼N(0,I)z\sim\mathcal N(0,I) y x=Dθ(z)x=D_\theta(z). Con un decodificador gaussiano de varianza fija, la mejor reconstrucción de un código es el promedio de las imágenes que caben en él: de ahí la borrosidad. Kingma y Welling (2019, §2.8.2) la atribuyen a que el ajuste nunca es perfecto y señalan que se atenúa con modelos de inferencia y generativos más flexibles.

Muestrear con cada modelo

Para el autocodificador no hay una zona definida de la cual muestrear; el recurso ingenuo es sacar puntos uniformes en el recuadro de sus códigos. Un clasificador sencillo (regresión logística entrenada con las mismas 1500 imágenes) etiquetó 3000 muestras de cada modelo: el autocodificador dio 22 % de cuatros, 21 % de seises y 0.7 % de ochos; el VAE, entre 6 % y 14 % de cada dígito (los datos reales tienen cerca de 10 % de cada uno). Con ocho semillas distintas el resultado se repite: el VAE siempre queda más parejo (véase train_toy.py).

En código

import numpy as np

# cinco sietes medidos con (tinta arriba, tinta abajo): ejemplo
m = np.array([[12.0, 7.8], [10.4, 8.9], [11.3, 7.4], [9.6, 8.5], [10.9, 8.1]])
mu = m.mean(axis=0)                        # centro de la nube
cov = np.cov(m.T, bias=True)               # forma del óvalo (máxima verosimilitud: divide entre N)
L = np.linalg.cholesky(cov)                # L @ L.T == cov
rng = np.random.default_rng(0)
nuevos = mu + rng.standard_normal((3, 2)) @ L.T   # muestrear: tres puntos nuevos de la nube
print(mu.round(2).tolist(), nuevos.round(1).tolist())
# [10.84, 8.14] [[10.9, 8.0], [11.4, 7.9], [10.4, 8.5]]

# Una nube de VAE: centro y tamaño (uno por cada número del código)
c, s = np.array([0.8, -0.3]), np.array([0.5, 0.8])
kl = 0.5 * np.sum(c**2 + s**2 - np.log(s**2) - 1)   # penalización frente a la zona estándar N(0, I)
z = c + s * np.array([0.2, -1.0])                   # un punto sacado de la nube (truco de reparametrización)
print(round(float(kl), 4), z.round(2).tolist())
# 0.7263 [0.9, -1.1]

Lo que viene

Los VAE fueron propuestos de forma independiente por Kingma y Welling (2013) y por Rezende, Mohamed y Wierstra (2014). Las redes generativas adversarias (lección 16) aprenden a generar sin escribir una densidad: compitiendo. La difusión (lección 17) aprende a quitar ruido, y su versión latente trabaja dentro del espacio comprimido de un autocodificador.

Lente de decisión

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

  • Lo generado refleja la mezcla de los datos de entrenamiento, y a veces la distorsiona: en la demostración, el autocodificador casi nunca producía ochos. Pide que midan la mezcla de lo generado, no solo que muestren ejemplos.
  • Cinco imágenes impecables no son evidencia. Pide muestras al azar, sin selección, y una medida de calidad y de variedad (por ejemplo, cuántos de cien intentos son utilizables y cuántos se parecen entre sí).
  • «No es un collage» es la regla, no una garantía: con modelos grandes y datos repetidos hay memorización documentada. Pregunta por la deduplicación de datos, las pruebas de memorización y el origen y la licencia de lo que se usó para entrenar.
  • Generar es muestrear: la misma instrucción da resultados distintos. Si necesitas reproducir un resultado, fija la semilla y registra la configuración; y recuerda que los datos sintéticos no traen información que no estuviera ya en los datos originales.

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