← Todo el material
Curso Intermedio 23 de agosto de 2026

Generación de imágenes — Módulo 5: autoencoders y VAE — el primer modelo que genera

Quinto módulo del curso de generación de imágenes: el salto de clasificar a generar. Qué es un autoencoder, qué es el espacio latente, y cómo el VAE lo vuelve probabilístico para muestrear e inventar imágenes nuevas — con código PyTorch real y comentado.

☰ Índice del contenido

En el Módulo 4 entrenaste una CNN para clasificar: le dabas una imagen y te devolvía una etiqueta (“gato”, “perro”, “3”). Toda la energía del modelo iba en resumir una imagen entera en una respuesta corta. Hoy damos el salto que cambia el curso completo: pasamos de clasificar a generar. La pregunta ya no es “¿qué hay en esta imagen?” sino “¿puedes fabricarme una imagen que nunca existió?”. El primer modelo que aprende a hacer eso es el autoencoder, y su versión probabilística —el VAE— es el ladrillo con el que después se construye Stable Diffusion.

La tesis del módulo

Comprimir es entender. Si una red logra reducir una imagen a un puñado de números y reconstruirla desde ahí, es porque aprendió qué información importa. El VAE convierte ese puñado de números en un espacio del que puedes muestrear — y muestrear es, por fin, generar.

Del clasificador al autoencoder

Un autoencoder (red que aprende a copiarse a sí misma) es una CNN partida en dos mitades que se miran a un espejo:

  • El encoder (codificador): toma la imagen y la va comprimiendo, capa a capa, hasta un vector chico. Ese vector es el cuello de botella.
  • El decoder (decodificador): toma ese vector chico y lo va expandiendo, capa a capa, hasta reconstruir una imagen del mismo tamaño que la original.

El truco está en el entrenamiento: le pides que la salida se parezca lo más posible a la entrada. Suena inútil —¿para qué copiar una imagen?— hasta que ves el detalle: el cuello de botella es mucho más chico que la imagen. La red no puede pasar los píxeles tal cual; tiene que resumirlos. Y para resumir bien, tiene que entender.

Ese vector chico del centro es el espacio latente (representación compacta donde vive lo esencial de la imagen — su “esencia” numérica, sin los detalles redundantes). Una imagen de 28×28 = 784 píxeles puede quedar comprimida en, digamos, 32 números. Esos 32 números son las coordenadas de la imagen en el espacio latente.

   imagen        ENCODER    latente    DECODER      imagen
   original     (comprime)  (cuello)  (reconstruye)  final
  ┌────────┐              ┌───────┐              ┌────────┐
  │ 28×28  │              │  32   │              │ 28×28  │
  │  784   │ ──────────►  │ núme- │ ──────────►  │  784   │
  │ píxeles│              │  ros  │              │ píxeles│
  └────────┘              └───────┘              └────────┘
   784 valores        aprieta a 32          infla a 784

El encoder aprieta (mucha info a poca), el decoder infla (poca info a mucha). Si la reconstrucción sale bien, el latente guardó lo que valía la pena.

Un autoencoder en PyTorch, de verdad

Acá tienes un autoencoder simple y correcto para imágenes de 28×28 (tipo MNIST, los dígitos escritos a mano). Es intencionalmente pequeño para que se lea de un vistazo.

import torch
import torch.nn as nn

class Autoencoder(nn.Module):
    def __init__(self, latent_dim=32):
        super().__init__()
        # Encoder: 784 píxeles -> 32 números (el latente)
        self.encoder = nn.Sequential(
            nn.Flatten(),            # (B, 1, 28, 28) -> (B, 784)
            nn.Linear(784, 128),
            nn.ReLU(),
            nn.Linear(128, latent_dim),  # cuello de botella
        )
        # Decoder: 32 números -> 784 píxeles (la reconstrucción)
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 784),
            nn.Sigmoid(),            # píxeles en rango [0, 1]
            nn.Unflatten(1, (1, 28, 28)),  # de vuelta a imagen
        )

    def forward(self, x):
        z = self.encoder(x)          # z = coordenadas en el latente
        return self.decoder(z)       # imagen reconstruida

modelo = Autoencoder()
criterio = nn.MSELoss()              # ¿se parece la salida a la entrada?
opt = torch.optim.Adam(modelo.parameters(), lr=1e-3)

for imagenes, _ in loader:           # ¡ojo: NO usamos las etiquetas!
    recon = modelo(imagenes)
    perdida = criterio(recon, imagenes)  # la propia imagen es el "objetivo"
    opt.zero_grad()
    perdida.backward()
    opt.step()

Fíjate en la línea marcada: no usamos las etiquetas. Un autoencoder aprende sin que nadie le diga qué es cada imagen — la propia imagen es su objetivo. Esto se llama aprendizaje no supervisado, y es lo que hace tan potentes a estos modelos: no necesitas datasets etiquetados a mano.

El problema del autoencoder simple: no sabe generar

Ya tienes un modelo que comprime y reconstruye. ¿Por qué no lo usamos para generar imágenes nuevas? Fácil: agarras 32 números al azar, se los das al decoder, y a ver qué sale.

El problema es que casi siempre sale basura. El autoencoder simple aprendió a colocar cada imagen de entrenamiento en algún punto del espacio latente, pero no organizó ese espacio. Hay huecos gigantes entre los puntos válidos. Si caes en un hueco, el decoder no tiene idea de qué hacer y devuelve manchas sin sentido.

   latente de un autoencoder simple
   (puntos válidos dispersos, con huecos)

     •         •              •
          ??            •
   •            ??   ??
        •            •      ??   •
   ??        •              •
      • = imagen real   ?? = hueco (basura)

Necesitamos un espacio latente ordenado y continuo, donde cualquier punto que elijas —no solo los que memorizó— produzca una imagen razonable. Ahí entra el VAE.

VAE: el latente se vuelve probabilístico

El VAE (Variational Autoencoder: autoencoder variacional — un autoencoder cuyo latente es una distribución de probabilidad, no un punto fijo) cambia una idea clave del encoder.

En un autoencoder normal, el encoder dice: “esta imagen va exactamente en el punto z”. En un VAE, el encoder dice: “esta imagen va en esta zona difusa del espacio” — devuelve una media (el centro de la zona) y una desviación (qué tan grande es la zona). Luego se toma una muestra al azar dentro de esa zona, y esa muestra es la que va al decoder.

Ese pequeño cambio lo cambia todo. Como el modelo entrena con muestras al azar cerca de cada imagen, el decoder aprende a manejar zonas enteras, no puntos aislados. El espacio latente se vuelve continuo: ya no hay huecos donde caer.

Para lograrlo, el VAE entrena con dos pérdidas al mismo tiempo:

  1. Pérdida de reconstrucción — la de siempre: que la imagen reconstruida se parezca a la original. Empuja al modelo a guardar información útil.
  2. Pérdida de regularización (KL) — la nueva: empuja a que todas esas zonas difusas se agrupen alrededor del centro (una gaussiana estándar, la campana de Gauss centrada en cero). La divergencia KL (medida de cuánto se aleja una distribución de otra) castiga a las zonas que se van muy lejos o se hacen muy raras.

La segunda pérdida es la que ordena el espacio. Gracias a ella, todo el latente queda apretado alrededor del origen — y entonces, para generar, basta con muestrear ruido gaussiano (números al azar de la campana centrada en cero), pasarlo por el decoder, y obtienes una imagen nueva que nunca estuvo en los datos.

Ojo con el equilibrio: las dos pérdidas tiran para lados opuestos. La reconstrucción quiere zonas chiquititas y precisas (para copiar bien); la KL quiere zonas anchas y solapadas (para que el espacio sea continuo). Un VAE bien entrenado negocia entre ambas. Si le das demasiado peso a la KL, las imágenes salen genéricas; muy poco, y vuelves a tener huecos.

Autoencoder vs VAE

AutoencoderVAE
El latente esun punto fijouna distribución (media + desviación)
Pérdidassolo reconstrucciónreconstrucción + KL
¿Genera imágenes nuevas?no confiable (huecos)sí, muestreando del latente
Espacio latentedisperso, con huecoscontinuo y ordenado
Uso típicocompresión, quitar ruidogeneración, base de difusión
Aprende con etiquetasno (no supervisado)no (no supervisado)

Errores comunes

  • Confundir el latente con “la imagen comprimida en ZIP”. No es compresión sin pérdida: el autoencoder tira todo lo que considera redundante y se queda solo con lo esencial. Reconstruye algo parecido, no idéntico.
  • Creer que un autoencoder simple genera. Reconstruye lo que ya vio, pero muestrear puntos al azar da basura. La capacidad de generar es exactamente lo que el VAE agrega.
  • Olvidar la pérdida KL. Sin ella, un VAE es solo un autoencoder con pasos extra: el espacio no queda ordenado y pierdes la magia de poder muestrear.
  • Esperar imágenes nítidas del VAE. Es su talón de Aquiles, y lo vemos abajo.

El precio del VAE: imágenes borrosas

El VAE fue el primer modelo que generó imágenes nuevas de forma confiable — un hito. Pero tiene un defecto conocido: sus imágenes tienden a salir borrosas. La razón es la pérdida de reconstrucción (MSE): cuando el modelo no está seguro de un detalle, la forma de minimizar el error es promediar todas las posibilidades. Y el promedio de muchas versiones nítidas de un borde es un borde difuso.

Un VAE genera una cara reconocible pero suave, como vista a través de un vidrio empañado. Eso motiva los dos capítulos que vienen: las GANs, que atacan la borrosidad con un rival que exige nitidez, y los modelos de difusión, que la resuelven de otra forma. Pero ninguno tira al VAE a la basura — lo van a reutilizar como pieza de compresión. Guárdalo.

La frase para llevarse

“El autoencoder demostró que comprimir es entender. El VAE ordenó ese entendimiento en un mapa continuo — y por primera vez, una máquina pudo pasear por ese mapa e inventar imágenes que nadie le mostró.”

Qué viene

Módulo 6: GANs, el duelo entre generador y discriminador. Si el VAE genera borroso porque promedia, las GANs atacan justo eso con una idea brillante: poner a dos redes a competir hasta que las imágenes falsas sean indistinguibles de las reales. Veremos por qué funciona, por qué es tan inestable de entrenar, y qué es el temido mode collapse.

Material relacionado

Generación de imágenesAutoencoderVAEEspacio latentePyTorch

Preguntas sobre este curso

Regístrate gratis para hacer preguntas y guardar tu progreso.

✉ Entérate de lo nuevo

Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.