📖 Teoría

Autoencoders

De la compresión inteligente a la generación creativa: fundamentos matemáticos del autoencoder, el espacio latente, variantes (denoising, sparse, convolucional), el Autoencoder Variacional (VAE) con ELBO, KL divergence y reparameterization trick, y su papel como piedra fundacional de la IA generativa moderna.

🔬 ¿Qué es un Autoencoder?

Un autoencoder es una red neuronal que aprende a copiar su entrada en su salida — pero pasando por un cuello de botella que la obliga a aprender una representación comprimida de los datos. Parece trivial, pero ese cuello de botella es lo que lo hace poderoso.

💡

Definición formal: Un autoencoder es una función \( f: \mathbb{R}^n \to \mathbb{R}^n \) compuesta por un encoder \( g_\phi: \mathbb{R}^n \to \mathbb{R}^d \) y un decoder \( f_\theta: \mathbb{R}^d \to \mathbb{R}^n \), donde \( d < n \), tal que:

$$ \hat{x} = f_\theta(g_\phi(x)) \approx x $$

El objetivo es minimizar la diferencia entre \( x \) y \( \hat{x} \), forzando a la red a aprender las características más importantes de los datos.

El autoencoder fue introducido por Rumelhart, Hinton y Williams en 1986 como parte de su trabajo seminal sobre backpropagation. La idea original era utilizar una red con un cuello de botella para aprender representaciones internas útiles de los datos de forma no supervisada — sin necesitar etiquetas. Aunque puede parecer una tarea trivial («copiar la entrada»), el bottleneck obliga a la red a descubrir la estructura latente de los datos: patrones, correlaciones y factores de variación que definen la distribución subyacente.

Esta idea resultó ser extraordinariamente influyente. Hoy, los autoencoders y sus variantes son la base conceptual de modelos como Stable Diffusion (que opera en el espacio latente de un autoencoder), DALL-E (que usa VQ-VAE para tokenizar imágenes), y numerosas aplicaciones en detección de anomalías, compresión aprendida y drug discovery.

🏗️ Arquitectura Encoder-Decoder

Todo autoencoder tiene dos partes simétricas conectadas por el espacio latente. La arquitectura encoder-decoder es una de las ideas más influyentes del deep learning: aparece no solo en autoencoders, sino en modelos de traducción (Seq2Seq), segmentación (U-Net), generación de imágenes (Stable Diffusion), y prácticamente en cualquier tarea que requiera transformar una representación en otra.

ENCODER gφ input x n dimensiones z espacio latente d dimensiones DECODER fθ output x̂ d ≪ n (bottleneck)
🔽 Encoder \( g_\phi \)

Comprime la entrada \( x \in \mathbb{R}^n \) a una representación \( z \in \mathbb{R}^d \) donde \( d \ll n \). Aprende a extraer las características esenciales.

🎯 Espacio Latente \( z \)

Representación comprimida de baja dimensión. Captura la estructura esencial de los datos. Es el «corazón» del autoencoder.

🔼 Decoder \( f_\theta \)

Reconstruye la entrada original a partir de \( z \). Si la reconstrucción es buena, el espacio latente captura la información relevante.

📉 Función de Pérdida: Reconstrucción

El autoencoder se entrena minimizando la pérdida de reconstrucción: la diferencia entre la entrada \( x \) y la salida \( \hat{x} \). Las dos opciones más comunes:

Loss Fórmula Caso de uso
MSE (Mean Squared Error) $$ \mathcal{L}_\text{MSE} = \frac{1}{n}\sum_{i=1}^{n}(x_i - \hat{x}_i)^2 $$ Datos continuos (imágenes normalizadas [0,1], señales)
BCE (Binary Cross-Entropy) $$ \mathcal{L}_\text{BCE} = -\frac{1}{n}\sum_{i=1}^{n}\left[x_i\log\hat{x}_i + (1-x_i)\log(1-\hat{x}_i)\right] $$ Datos binarios o valores en [0,1] (imágenes binarizadas)
⚠️

La loss no mide «calidad visual»: MSE penaliza diferencias pixel a pixel, pero una imagen puede ser perceptualmente buena con MSE alto (o viceversa). Por eso existen losses perceptuales, pero para empezar, MSE y BCE funcionan bien.

📐 Undercomplete vs Overcomplete

La relación entre la dimensión de entrada \( n \) y la dimensión latente \( d \) define el tipo de autoencoder:

Undercomplete (d < n)

El bottleneck fuerza a la red a aprender las características más importantes. No puede simplemente copiar la entrada. Este es el caso estándar.

$$ d \ll n \implies \text{compresión} $$
Overcomplete (d ≥ n)

Sin restricciones adicionales, la red puede aprender la identidad (copiar todo sin aprender nada útil). Se necesita regularización (sparsity, denoising, etc.) para que sea útil.

$$ d \geq n \implies \text{necesita regularización} $$

🔗 Conexión con PCA

Si usamos un autoencoder lineal (sin funciones de activación) con loss MSE, la solución óptima es exactamente PCA (Principal Component Analysis):

$$ \text{AE lineal} + \text{MSE} \iff \text{PCA (subespacio de máxima varianza)} $$

Sea \( W_e \in \mathbb{R}^{d \times n} \) la matriz del encoder y \( W_d \in \mathbb{R}^{n \times d} \) la del decoder. La reconstrucción es:

$$ \hat{x} = W_d W_e x $$

Minimizar \( \|x - W_d W_e x\|^2 \) sobre todos los datos equivale a encontrar la proyección de rango \( d \) que maximiza la varianza explicada. Por el teorema de Eckart-Young, la solución óptima son los primeros \( d \) componentes principales de la matriz de covarianza de los datos.

Pero: al añadir no-linealidades (ReLU, sigmoid...), el autoencoder puede capturar manifolds no lineales que PCA no puede ver. Esa es su ventaja.

🎓

PCA vs Autoencoder no lineal:

  • PCA: encuentra el mejor subespacio lineal. Rápido, interpretable, limitado.
  • AE no lineal: encuentra un manifold no lineal. Más expresivo, puede capturar estructuras complejas.
  • El AE es una generalización no lineal de PCA.
📚

Referencias

  • Rumelhart et al. (1986). Learning representations by back-propagating errors. Nature 323 — Trabajo original con autoencoders.
  • Baldi & Hornik (1989). Neural networks and principal component analysis. Neural Networks 2(1) — Demostración de la equivalencia AE lineal ↔ PCA.
  • Goodfellow et al. (2016). Deep Learning, Capítulo 14: Autoencoders. deeplearningbook.org — Referencia completa sobre autoencoders.

🌌 El Espacio Latente: el corazón del autoencoder

El espacio latente es la representación de baja dimensión que el autoencoder aprende internamente. Es el punto donde la red ha destilado la información esencial, descartando redundancia y ruido.

🧠

Intuición: Imagina que tienes 10.000 fotos de caras. Cada imagen tiene 784 píxeles (28×28), pero la «esencia» de cada cara se puede describir con muchos menos números: forma de la nariz, color de ojos, ángulo de la cabeza... El espacio latente captura esos factores de variación en un vector compacto \( z \).

El Bottleneck y la Hipótesis del Manifold

La hipótesis del manifold dice que los datos reales de alta dimensión viven en un subespacio (manifold) de dimensión mucho menor. El autoencoder aprende este manifold:

1
Datos en alta dimensión: Una imagen MNIST es un vector en \( \mathbb{R}^{784} \), pero el «espacio de dígitos válidos» tiene dimensión intrínseca mucho menor (~10-20).
2
Bottleneck fuerza compresión: Si \( d = 2 \), el encoder debe comprimir 784 dimensiones en 2. Solo puede hacerlo si encuentra la estructura subyacente.
3
El decoder reconstruye: Si la reconstrucción es buena, el espacio latente de 2D captura la esencia del manifold de dígitos.
MANIFOLD HYPOTHESIS ℝ⁷⁸⁴ (alta dim) manifold encoder ℝ² (latente) clusters decoder ℝ⁷⁸⁴ (reconstruido)

La hipótesis del manifold es uno de los supuestos más importantes en deep learning. Proporciona la justificación teórica de por qué las redes neuronales profundas pueden funcionar tan bien con datos de alta dimensión: si los datos realmente viven en un manifold de dimensión mucho menor, la tarea de la red no es «entender» todas las \( 2^{784} \) posibles configuraciones de píxeles, sino solo el pequeño subconjunto que corresponde a imágenes válidas.

🗺️ Visualización del Espacio Latente

Cuando \( d = 2 \), podemos visualizar directamente el espacio latente como un scatter plot. Para \( d > 2 \), usamos técnicas como t-SNE o UMAP para proyectar a 2D.

🖼️

Si entrenas un autoencoder con \( d = 2 \) sobre MNIST, cada dígito forma un cluster en el espacio latente. Los dígitos similares (como 4 y 9) estarán cerca. Esto demuestra que el espacio latente captura la semántica de los datos.

🔄 Interpolación en el Espacio Latente

Una propiedad fascinante: si tomamos dos puntos \( z_A \) y \( z_B \) en el espacio latente y generamos puntos intermedios, el decoder produce transiciones suaves entre las imágenes correspondientes:

$$ z_\alpha = (1 - \alpha) \cdot z_A + \alpha \cdot z_B, \quad \alpha \in [0, 1] $$
A α=0 α=0.25 α=0.5 α=0.75 B α=1 decoder(zα) → transición suave

Esta interpolación es clave: demuestra que el espacio latente es continuo y tiene estructura semántica. Es el fundamento de la generación de contenido nuevo.

⚠️

Problema: En un autoencoder estándar, la interpolación no siempre funciona bien. Los puntos intermedios pueden caer en «zonas muertas» del espacio latente que nunca se vieron durante el entrenamiento. Este es uno de los problemas que el VAE resuelve.

🧪 Explorador interactivo: Espacio Latente 2D

Experimenta con un espacio latente bidimensional simulado. Mueve el punto en el espacio latente y observa cómo cambia la «reconstrucción»:

0.00
0.00
Espacio latente
«Reconstrucción» simulada

💡 Nota: esta es una simulación didáctica. z₁ controla la «forma» y z₂ el «estilo». En un autoencoder real, los ejes latentes suelen no estar tan perfectamente alineados con conceptos humanos (a menos que uses β-VAE).

🗂️ Tipos de Autoencoders

El autoencoder «vanilla» es solo el punto de partida. Existen variantes que añaden regularización o estructura para mejorar las representaciones aprendidas. Aquí están las más importantes:

🧹 Denoising Autoencoder (DAE)

Idea: Corrompe la entrada con ruido y entrena al autoencoder a reconstruir la versión limpia. Esto fuerza a la red a aprender la estructura real de los datos, no artefactos del ruido.

$$ \tilde{x} = x + \epsilon, \quad \epsilon \sim \mathcal{N}(0, \sigma^2 I) $$ $$ \mathcal{L}_\text{DAE} = \|x - f_\theta(g_\phi(\tilde{x}))\|^2 $$
x (limpio) +ruido x̃ (ruidoso) AE x̂ ≈ x ✓ L = ‖x − x̂‖²
💡

¿Por qué funciona? Al ver muchas versiones ruidosas del mismo dato, la red aprende la distribución subyacente, no los detalles superficiales. Es equivalente a aprender el score function de la distribución — ¡la misma idea que subyace a los modelos de difusión!

Sparse Autoencoder

Idea: Permite un espacio latente overcomplete (\( d \geq n \)), pero añade una penalización de sparsity para que solo unas pocas neuronas estén activas a la vez. Cada entrada activa un subconjunto diferente de neuronas.

$$ \mathcal{L}_\text{sparse} = \|x - \hat{x}\|^2 + \lambda \sum_{j} |\hat{\rho}_j - \rho| $$

Donde \( \hat{\rho}_j \) es la activación media de la neurona \( j \) y \( \rho \) es el nivel de sparsity objetivo (típicamente \( \rho = 0.05 \), es decir, solo el 5% de las neuronas activas).

Una alternativa más suave es usar la divergencia KL entre la distribución de activaciones y una Bernoulli con parámetro \( \rho \):

$$ \Omega_\text{sparse} = \sum_{j=1}^{d} \text{KL}(\rho \| \hat{\rho}_j) = \sum_j \rho \log\frac{\rho}{\hat{\rho}_j} + (1-\rho)\log\frac{1-\rho}{1-\hat{\rho}_j} $$

Esto penaliza suavemente las neuronas que se desvían del nivel de sparsity deseado.

🔍

Aplicación moderna: Los sparse autoencoders se están usando hoy (2024-2025) para interpretar redes neuronales grandes (LLMs). Anthropic y OpenAI los usan para descomponer las activaciones internas de modelos de lenguaje en features interpretables. ¡Un uso que nadie anticipó!

📄 Bricken et al., 2023 — Towards Monosemanticity (Anthropic)

📏 Contractive Autoencoder (CAE)

Idea: Añade una penalización sobre el jacobiano del encoder, forzando a que la representación latente sea insensible a pequeñas perturbaciones en la entrada:

$$ \mathcal{L}_\text{CAE} = \|x - \hat{x}\|^2 + \lambda \left\| \frac{\partial g_\phi(x)}{\partial x} \right\|_F^2 $$

La norma de Frobenius del jacobiano mide cuánto cambia \( z \) cuando \( x \) cambia un poco. Minimizarla hace que el encoder sea «suave» — resistente a variaciones irrelevantes.

CriterioDAECAE
RegularizaciónImplícita (ruido)Explícita (jacobiano)
Coste computacionalBajo (solo añadir ruido)Alto (calcular jacobiano)
Conexión teóricaScore matchingManifold tangente
En la prácticaMás usadoMás elegante, menos práctico

Rifai et al. (2011) demostró que ambos aprenden representaciones similares cuando están bien ajustados. DAE es más práctico; CAE tiene garantías teóricas más fuertes. 📄 Rifai et al., ICML 2011

🔲 Autoencoder Convolucional

Para imágenes, usar capas densas es ineficiente. Los autoencoders convolucionales usan Conv2D en el encoder y ConvTranspose2D (o upsampling) en el decoder:

E
Encoder: Conv2D → BatchNorm → ReLU → MaxPool (repite, reduciendo resolución) → Flatten → \( z \)
D
Decoder: \( z \) → Dense → Reshape → ConvTranspose2D → BatchNorm → ReLU (repite, aumentando resolución) → Sigmoid → \( \hat{x} \)
import torch
import torch.nn as nn

class ConvAutoencoder(nn.Module):
    def __init__(self, latent_dim=32):
        super().__init__()
        # Encoder: 1x28x28 → 32x7x7 → latent
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 16, 3, stride=2, padding=1),  # 16x14x14
            nn.BatchNorm2d(16), nn.ReLU(),
            nn.Conv2d(16, 32, 3, stride=2, padding=1), # 32x7x7
            nn.BatchNorm2d(32), nn.ReLU(),
            nn.Flatten(),
            nn.Linear(32*7*7, latent_dim)
        )
        # Decoder: latent → 32x7x7 → 1x28x28
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 32*7*7),
            nn.Unflatten(1, (32, 7, 7)),
            nn.ConvTranspose2d(32, 16, 3, stride=2, padding=1, output_padding=1),
            nn.BatchNorm2d(16), nn.ReLU(),
            nn.ConvTranspose2d(16, 1, 3, stride=2, padding=1, output_padding=1),
            nn.Sigmoid()
        )

    def forward(self, x):
        z = self.encoder(x)
        return self.decoder(z), z

model = ConvAutoencoder(latent_dim=16)
print(f"Parámetros: {sum(p.numel() for p in model.parameters()):,}")
# Loss: nn.MSELoss() o nn.BCELoss()
import tensorflow as tf
from tensorflow.keras import layers, Model

def build_conv_autoencoder(latent_dim=32):
    # Encoder
    encoder_input = layers.Input(shape=(28, 28, 1))
    x = layers.Conv2D(16, 3, strides=2, padding='same', activation='relu')(encoder_input)
    x = layers.BatchNormalization()(x)
    x = layers.Conv2D(32, 3, strides=2, padding='same', activation='relu')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Flatten()(x)
    z = layers.Dense(latent_dim, name='latent')(x)
    encoder = Model(encoder_input, z, name='encoder')

    # Decoder
    decoder_input = layers.Input(shape=(latent_dim,))
    x = layers.Dense(7 * 7 * 32, activation='relu')(decoder_input)
    x = layers.Reshape((7, 7, 32))(x)
    x = layers.Conv2DTranspose(16, 3, strides=2, padding='same', activation='relu')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Conv2DTranspose(1, 3, strides=2, padding='same', activation='sigmoid')(x)
    decoder = Model(decoder_input, x, name='decoder')

    # Full autoencoder
    ae_output = decoder(encoder(encoder_input))
    autoencoder = Model(encoder_input, ae_output, name='autoencoder')
    autoencoder.compile(optimizer='adam', loss='mse')
    return autoencoder, encoder, decoder

ae, enc, dec = build_conv_autoencoder(latent_dim=16)
ae.summary()

📋 Resumen comparativo

Tipo Regularización Fórmula clave Ventaja
Vanilla Solo bottleneck \( d < n \) Simple, línea base
Denoising Ruido en la entrada \( \tilde{x} = x + \epsilon \) Robusto, aprende distribución
Sparse Penalización activaciones \( \lambda \sum |\hat{\rho}_j - \rho| \) Features interpretables
Contractive Penalización jacobiano \( \lambda \|J\|_F^2 \) Insensible a perturbaciones
Convolucional Estructura espacial Conv2D + ConvTranspose2D Eficiente para imágenes
VAE → Probabilístico \( \text{KL}(q_\phi \| p) \) Generativo, espacio continuo

🌟 VAE: de reconstruir a generar

El autoencoder estándar aprende a reconstruir, pero no a generar. ¿Por qué? Porque su espacio latente no tiene estructura probabilística: los puntos se distribuyen de forma irregular, con «huecos» donde el decoder no sabe qué producir.

🎯

El problema fundamental: Si tomo un punto aleatorio \( z \) del espacio latente de un AE estándar y lo paso por el decoder, el resultado suele ser basura. El espacio latente no es continuo ni está regularizado.

La solución del VAE: Forzar al espacio latente a seguir una distribución conocida (Gaussiana estándar). Así, cualquier punto muestreado de \( \mathcal{N}(0, I) \) produce una salida coherente.

AE ESTÁNDAR vs VAE AE estándar — latente irregular zona muerta 💀 VAE — latente regularizado ~ N(0,I) cualquier z ~ N(0,I) → salida coherente ✓

La diferencia visual es clara: el espacio latente del AE estándar tiene «agujeros» — regiones donde ningún dato del entrenamiento fue codificado, y por lo tanto el decoder no tiene información sobre qué producir. El VAE, al forzar que todo el espacio latente se parezca a una gaussiana estándar, garantiza que cualquier punto muestreado producirá una salida coherente. Este es el salto conceptual que convierte a un autoencoder en un modelo generativo.

📐 Formulación Probabilística del VAE

El VAE (Kingma & Welling, 2013) reformula el autoencoder como un modelo generativo probabilístico. En lugar de aprender una función determinista encoder-decoder, el VAE aprende una distribución sobre el espacio latente. Esta reformulación, que se apoya en la inferencia variacional — una de las técnicas más potentes del ML bayesiano — es lo que permite al VAE generar muestras nuevas de forma principled.

El marco conceptual tiene cuatro elementos:

1
Prior: Asumimos que las variables latentes siguen una distribución conocida: \( p(z) = \mathcal{N}(0, I) \)
2
Likelihood (decoder): La distribución de los datos dado \( z \): \( p_\theta(x|z) \) — parametrizada por el decoder
3
Posterior: Queremos \( p(z|x) \), pero es intratable (requiere integrar sobre todo \( z \))
4
Aproximación variacional (encoder): Aproximamos \( p(z|x) \) con \( q_\phi(z|x) = \mathcal{N}(\mu_\phi(x), \sigma^2_\phi(x) I) \) — parametrizada por el encoder
💡

Clave: El encoder no produce un punto \( z \), sino los parámetros de una distribución (\( \mu \) y \( \sigma \)). Cada entrada \( x \) se mapea a una «nube» gaussiana en el espacio latente, no a un punto fijo.

📊 ELBO: la función objetivo del VAE

El objetivo del VAE es maximizar la Evidence Lower BOund (ELBO), que es una cota inferior de la log-verosimilitud de los datos:

$$ \log p(x) \geq \underbrace{\mathbb{E}_{q_\phi(z|x)}[\log p_\theta(x|z)]}_{\text{reconstrucción}} - \underbrace{D_\text{KL}(q_\phi(z|x) \| p(z))}_{\text{regularización}} = \text{ELBO} $$
📐 Término de Reconstrucción
$$ \mathbb{E}_{q_\phi(z|x)}[\log p_\theta(x|z)] $$

«¿Qué tan bien reconstruye el decoder?» Equivale a la loss de reconstrucción (MSE o BCE). Queremos maximizar esto.

🎯 Término de Regularización (KL)
$$ D_\text{KL}(q_\phi(z|x) \| p(z)) $$

«¿Qué tan cerca está la distribución latente de N(0,I)?» Penaliza desviaciones. Queremos minimizar esto.

En la práctica, la loss del VAE es:

$$ \mathcal{L}_\text{VAE} = \underbrace{\|x - \hat{x}\|^2}_{\text{reconstrucción}} + \underbrace{\frac{1}{2}\sum_{j=1}^{d}\left(\mu_j^2 + \sigma_j^2 - \log\sigma_j^2 - 1\right)}_{\text{KL con N(0,I)}} $$

Para dos gaussianas univariadas \( q = \mathcal{N}(\mu, \sigma^2) \) y \( p = \mathcal{N}(0, 1) \):

$$ D_\text{KL}(q \| p) = \int q(z) \log\frac{q(z)}{p(z)} dz $$

Desarrollando con las densidades gaussianas:

$$ = \frac{1}{2}\left(\mu^2 + \sigma^2 - \log\sigma^2 - 1\right) $$

Para \( d \) dimensiones independientes, simplemente sumamos. Esta fórmula cerrada es una de las razones por las que el VAE usa gaussianas: la KL se calcula analíticamente, sin necesidad de Monte Carlo.

🔧 Reparameterization Trick

El problema: En el forward pass, necesitamos muestrear \( z \sim q_\phi(z|x) = \mathcal{N}(\mu, \sigma^2) \). Pero el muestreo es una operación no diferenciable — no podemos hacer backpropagation a través de él.

La solución: Reparametrizar el muestreo separando la parte estocástica:

$$ z = \mu_\phi(x) + \sigma_\phi(x) \odot \epsilon, \quad \epsilon \sim \mathcal{N}(0, I) $$
❌ Sin reparametrización encoder sample decoder ∇ bloqueado ❌ no se puede hacer backprop ✅ Con reparametrización encoder → μ, σ z = μ + σ·ε diferenciable! ε~N(0,I) externo (no trainable) decoder ∇ fluye por μ y σ ✅
🧠

Genial truco: En vez de muestrear de \( \mathcal{N}(\mu, \sigma^2) \), muestreamos \( \epsilon \) de \( \mathcal{N}(0, 1) \) (fijo) y luego hacemos \( z = \mu + \sigma \cdot \epsilon \). Los gradientes fluyen a través de \( \mu \) y \( \sigma \) sin problemas. El \( \epsilon \) es solo ruido externo, no parte del grafo computacional.

🎨 Generación con VAE

Una vez entrenado, generar nuevas muestras es trivial:

1
Muestrear: \( z \sim \mathcal{N}(0, I) \) — un punto aleatorio del espacio latente
2
Decodificar: \( \hat{x} = f_\theta(z) \) — pasar por el decoder
3
Resultado: Una nueva muestra que parece «real» porque el espacio latente está organizado como \( \mathcal{N}(0, I) \)
⚖️

El trade-off reconstrucción vs regularización:

  • Si priorizas reconstrucción: El espacio latente puede ser irregular, pero las reconstrucciones son nítidas.
  • Si priorizas KL: El espacio latente es suave y regular, pero las reconstrucciones pueden ser borrosas.
  • Las imágenes VAE tienden a ser ligeramente borrosas — este es el «precio» de un espacio latente bien regularizado. Los GANs y modelos de difusión abordan esto de otras formas.

🧪 Explorador interactivo: VAE

Experimenta con los parámetros de un VAE y observa el trade-off entre reconstrucción y regularización:

0.50
1.00
1.00
📚

Referencias clave del VAE

  • Kingma & Welling (2013). Auto-Encoding Variational Bayes. arXiv:1312.6114 — El paper original del VAE.
  • Doersch (2016). Tutorial on Variational Autoencoders. arXiv:1606.05908 — Tutorial excelente y accesible.
  • Blei, Kucukelbir & McAuliffe (2017). Variational Inference: A Review for Statisticians. arXiv:1601.00670 — Contexto de inferencia variacional.

📖 Ver también: GANs | Modelos de Difusión

🎛️ β-VAE: Representaciones Disentangled

Higgins et al. (2017) propusieron una modificación simple pero poderosa: multiplicar el término KL por un factor \( \beta \). Aunque parezca un cambio trivial, tiene implicaciones profundas sobre la estructura del espacio latente y la calidad de las representaciones aprendidas.

$$ \mathcal{L}_{\beta\text{-VAE}} = \mathbb{E}[\|x - \hat{x}\|^2] + \boldsymbol{\beta} \cdot D_\text{KL}(q_\phi(z|x) \| p(z)) $$
βEfectoTrade-off
β = 0 Autoencoder estándar (sin regularización) Buena reconstrucción, espacio latente irregular
β = 1 VAE estándar Balance reconstrucción / regularización
β > 1 Mayor presión hacia N(0,I) Representaciones disentangled, reconstrucción más borrosa
β ≫ 1 El latente colapsa a N(0,I) «Posterior collapse»: pierde toda la información
🧬

¿Qué significa «disentangled»? Que cada dimensión del espacio latente controla un factor de variación independiente. Por ejemplo, en caras: z₁ = sonrisa, z₂ = orientación, z₃ = gafas... Cambiar un eje cambia solo un atributo.

Con β > 1, el VAE está más «presionado» a distribuir la información en dimensiones ortogonales e independientes.

La KL con \( p(z) = \mathcal{N}(0, I) \) puede descomponerse (Chen et al. 2018) en:

$$ D_\text{KL}(q(z|x) \| p(z)) = \underbrace{I(x; z)}_{\text{info mutua}} + \underbrace{D_\text{KL}(q(z) \| p(z))}_{\text{marginal matching}} $$

Con β alto, se penaliza especialmente la información mutua, forzando al modelo a usar solo la información mínima necesaria en cada dimensión latente. Esto induce independencia estadística entre las dimensiones → disentanglement.

🏷️ Conditional VAE (CVAE)

El CVAE (Sohn et al., 2015) condiciona tanto el encoder como el decoder en una etiqueta \( y \) (clase, atributo, texto...). Esta extensión es fundamental: permite pasar de generar al azar a generar con control, una capacidad esencial en aplicaciones reales.

$$ q_\phi(z|x, y), \quad p_\theta(x|z, y) $$

Esto permite generar con control: «genera un dígito 7» o «genera una cara con gafas».

E
Encoder: recibe \( [x, y] \) (concatenados) → produce \( \mu, \sigma \)
D
Decoder: recibe \( [z, y] \) (concatenados) → produce \( \hat{x} \)
G
Generación: elige \( y \) (e.g., clase «7»), muestrea \( z \sim \mathcal{N}(0,I) \), decodifica \( [z, y] \) → dígito 7 generado

🧊 VQ-VAE: Cuantización Vectorial

Van den Oord et al. (2017) propusieron el VQ-VAE, que usa un espacio latente discreto en lugar de continuo. Esta idea rompe con la tradición de los VAE gaussianos y abre la puerta a combinar autoencoders con modelos autoregresivos como Transformers. El codebook de embeddings actúa como un diccionario aprendido de «palabras visuales».

1
El encoder produce un vector continuo \( z_e(x) \)
2
Se busca el embedding más cercano en un codebook \( e_k \): \( z_q = e_k \) donde \( k = \arg\min_j \|z_e - e_j\|^2 \)
3
El decoder recibe \( z_q \) (el embedding discreto)
$$ \mathcal{L}_\text{VQ-VAE} = \|x - \hat{x}\|^2 + \|z_e - \text{sg}[e_k]\|^2 + \beta\|\text{sg}[z_e] - e_k\|^2 $$

Donde \( \text{sg}[\cdot] \) es «stop gradient». Los tres términos son: reconstrucción, commitment loss (acercar encoder a embeddings), y codebook loss (acercar embeddings a encoder).

🔥

¿Por qué importa? VQ-VAE es el ancestro directo de DALL-E (OpenAI, 2021), que usa VQ-VAE para tokenizar imágenes y luego un Transformer para generar secuencias de tokens de imagen. También inspira Stable Diffusion, que opera en el espacio latente de un autoencoder (de ahí «Latent Diffusion Model»).

💻 Implementación completa: VAE

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

class VAE(nn.Module):
    def __init__(self, input_dim=784, hidden_dim=400, latent_dim=20):
        super().__init__()
        # Encoder
        self.fc1 = nn.Linear(input_dim, hidden_dim)
        self.fc_mu = nn.Linear(hidden_dim, latent_dim)
        self.fc_logvar = nn.Linear(hidden_dim, latent_dim)
        # Decoder
        self.fc3 = nn.Linear(latent_dim, hidden_dim)
        self.fc4 = nn.Linear(hidden_dim, input_dim)

    def encode(self, x):
        h = F.relu(self.fc1(x))
        return self.fc_mu(h), self.fc_logvar(h)

    def reparameterize(self, mu, logvar):
        """El famoso reparameterization trick"""
        std = torch.exp(0.5 * logvar)  # σ = exp(logσ²/2)
        eps = torch.randn_like(std)     # ε ~ N(0, I)
        return mu + std * eps           # z = μ + σ·ε

    def decode(self, z):
        h = F.relu(self.fc3(z))
        return torch.sigmoid(self.fc4(h))

    def forward(self, x):
        mu, logvar = self.encode(x.view(-1, 784))
        z = self.reparameterize(mu, logvar)
        return self.decode(z), mu, logvar

def vae_loss(x_recon, x, mu, logvar):
    """ELBO loss = reconstrucción + KL"""
    # Reconstrucción (BCE porque sigmoid en decoder)
    recon = F.binary_cross_entropy(x_recon, x.view(-1, 784), reduction='sum')
    # KL divergence con N(0,I) — fórmula cerrada
    kl = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
    return recon + kl

# Entrenamiento
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = VAE(latent_dim=20).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

train_loader = DataLoader(
    datasets.MNIST('./data', train=True, download=True,
                   transform=transforms.ToTensor()),
    batch_size=128, shuffle=True
)

for epoch in range(10):
    model.train()
    total_loss = 0
    for x, _ in train_loader:
        x = x.to(device)
        x_recon, mu, logvar = model(x)
        loss = vae_loss(x_recon, x, mu, logvar)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch+1}, Loss: {total_loss/len(train_loader.dataset):.2f}")

# Generar nuevas muestras
model.eval()
with torch.no_grad():
    z = torch.randn(16, 20).to(device)  # Muestrear de N(0,I)
    samples = model.decode(z)            # Decodificar
    # samples tiene forma (16, 784) → reshape a (16, 1, 28, 28)
import tensorflow as tf
from tensorflow.keras import layers, Model
import numpy as np

class Sampling(layers.Layer):
    """Reparameterization trick como capa de Keras"""
    def call(self, inputs):
        mu, log_var = inputs
        eps = tf.random.normal(shape=tf.shape(mu))
        return mu + tf.exp(0.5 * log_var) * eps

class VAE(Model):
    def __init__(self, latent_dim=20, **kwargs):
        super().__init__(**kwargs)
        self.latent_dim = latent_dim

        # Encoder
        self.encoder_dense = layers.Dense(400, activation='relu')
        self.mu_layer = layers.Dense(latent_dim)
        self.logvar_layer = layers.Dense(latent_dim)
        self.sampling = Sampling()

        # Decoder
        self.decoder_dense1 = layers.Dense(400, activation='relu')
        self.decoder_out = layers.Dense(784, activation='sigmoid')

    def encode(self, x):
        h = self.encoder_dense(x)
        return self.mu_layer(h), self.logvar_layer(h)

    def decode(self, z):
        h = self.decoder_dense1(z)
        return self.decoder_out(h)

    def call(self, x):
        x_flat = tf.reshape(x, [-1, 784])
        mu, logvar = self.encode(x_flat)
        z = self.sampling([mu, logvar])
        x_recon = self.decode(z)

        # Añadir KL loss
        kl_loss = -0.5 * tf.reduce_sum(
            1 + logvar - tf.square(mu) - tf.exp(logvar), axis=1
        )
        self.add_loss(tf.reduce_mean(kl_loss))
        return x_recon

# Entrenamiento
(x_train, _), _ = tf.keras.datasets.mnist.load_data()
x_train = x_train.astype('float32') / 255.0

vae = VAE(latent_dim=20)
vae.compile(optimizer='adam', loss='binary_crossentropy')
vae.fit(x_train.reshape(-1, 784), x_train.reshape(-1, 784),
        epochs=10, batch_size=128, validation_split=0.1)

# Generar
z_sample = np.random.normal(size=(16, 20)).astype('float32')
generated = vae.decode(z_sample).numpy().reshape(-1, 28, 28)
📚

Referencias del VAE avanzado

  • Higgins et al. (2017). beta-VAE: Learning Basic Visual Concepts with a Constrained Variational Framework. ICLR 2017 — β-VAE y disentanglement.
  • Sohn, Lee & Yan (2015). Learning Structured Output Representation using Deep Conditional Generative Models. NeurIPS 2015 — CVAE.
  • van den Oord, Vinyals & Kavukcuoglu (2017). Neural Discrete Representation Learning. arXiv:1711.00937 — VQ-VAE.
  • Chen et al. (2018). Isolating Sources of Disentanglement in VAEs. arXiv:1802.04942 — Descomposición TC de la KL.

📖 Ver también: GANs (VAE-GAN) | Difusión (Latent Diffusion)

🌍 Aplicaciones de los Autoencoders

🔍
Detección de anomalías
Entrena sobre datos «normales». Si la reconstrucción de un nuevo dato es mala (loss alta), es una anomalía. Usado en fraude, defectos industriales, ciberseguridad.
dato nuevo normal / anomalía
📦
Compresión aprendida
El espacio latente es una representación comprimida. Más eficiente que JPEG para dominios específicos. Usado en compresión de imágenes médicas y satelitales.
imagen z comprimido
🎨
Generación (VAE)
Muestrear z ~ N(0,I) y decodificar produce nuevas muestras. Caras, moléculas, diseños de fármacos, música, arquitectura.
z aleatorio muestra nueva
🧹
Denoising
Limpiar imágenes ruidosas, restaurar documentos antiguos, mejorar señales de audio. El DAE es especialmente bueno aquí.
dato ruidoso dato limpio
🧬
Drug Discovery
VAE sobre representaciones moleculares (SMILES) para generar nuevos fármacos candidatos interpolando en el espacio latente.
molécula molécula nueva
📊
Feature Learning
El espacio latente como input para otros modelos (clasificación, clustering). Preentrenamiento no supervisado antes de fine-tuning supervisado.
datos sin etiquetar features latentes

🧪 Widget: Detector de anomalías con AE

Simula cómo un autoencoder detecta anomalías basándose en el error de reconstrucción:

0.80
15
3

🌐 Los Autoencoders en el Ecosistema Generativo

Los autoencoders no son un modelo aislado — son el fundamento conceptual de gran parte de la IA generativa moderna:

ÁRBOL GENEALÓGICO DE LA IA GENERATIVA Autoencoders ~1986 (Rumelhart) VAE Kingma 2013 VQ-VAE van den Oord 2017 DAE / Score Vincent 2008 β-VAE / CVAE DALL-E Latent Diffusion Stable Diffusion Diffusion Models DDPM, Score SDE Modelos Modernos DALL-E 2/3, Midjourney, SD XL Los autoencoders proporcionan la base: espacio latente · encoder-decoder · reconstrucción
🔗

Conexiones clave con otros modelos (que verás en los siguientes submódulos):

  • GANs: Resuelven el problema de las imágenes borrosas del VAE usando un discriminador adversarial. El VAE-GAN combina ambos.
  • Difusión: El DAE es el ancestro conceptual de los modelos de difusión (denoising iterativo). Stable Diffusion opera en el espacio latente de un autoencoder.
  • Transformers/LLMs: VQ-VAE tokeniza imágenes para que los Transformers las procesen como secuencias. Los tokenizers de texto son, en cierto sentido, encoders.

🏛️ El Legado de los Autoencoders

Concepto del AEDónde aparece hoy
Espacio latente Latent Diffusion, CLIP, embeddings en general
Encoder-Decoder Seq2Seq, U-Net, Transformers (encoder-decoder)
Bottleneck Information bottleneck theory, compression
Reparameterization trick Gumbel-Softmax, normalizing flows, score models
ELBO Inferencia variacional en todo el ML bayesiano
Denoising DDPM, score matching, data augmentation
VQ (cuantización) DALL-E, Codex, tokenización de imágenes/audio
🎓

Lo que has aprendido en este submódulo:

  1. Qué es un autoencoder y cómo funciona la arquitectura encoder-decoder
  2. El espacio latente: compresión, manifolds, interpolación
  3. Variantes: denoising, sparse, contractive, convolucional
  4. El VAE: formulación probabilística, ELBO, KL divergence, reparameterization trick
  5. VAE avanzado: β-VAE (disentanglement), CVAE, VQ-VAE
  6. Implementación completa en PyTorch y TensorFlow
  7. Aplicaciones prácticas y la conexión con el resto de la IA generativa

Con estos fundamentos, estás preparado para entender GANs, modelos de difusión y Transformers — todos ellos construyen sobre las ideas que acabas de aprender.

📚

Referencias generales

  • Goodfellow, Bengio & Courville (2016). Deep Learning, Cap. 14 — Autoencoders. deeplearningbook.org
  • Kingma & Welling (2019). An Introduction to Variational Autoencoders. arXiv:1906.02691 — Monografía completa.
  • Rombach et al. (2022). High-Resolution Image Synthesis with Latent Diffusion Models. arXiv:2112.10752 — Stable Diffusion (usa un AE latente).
  • Ramesh et al. (2021). Zero-Shot Text-to-Image Generation. arXiv:2102.12092 — DALL-E (basado en VQ-VAE).

📖 Submódulos relacionados: GANs | Modelos de Difusión | Transformers / LLMs

🏭 Casos de uso