Comprimir y reconstruir
El encoder reduce cada observación a unos pocos números; el decoder intenta recuperarla. El cuello de botella obliga a quedarse con lo esencial.
Deep Learning · Autosupervisado · Laboratorio 02
Una red que aprende a describir cada observación con unos pocos números y a reconstruirla desde ahí. Aquí se entrena de verdad, en tu navegador, con gradientes calculados a mano: verás bajar la pérdida época a época y qué le hace cada hiperparámetro.
¿Vienes de Isolation Forest? Allí los hiperparámetros cambiaban cómo se buscan las anomalías; aquí cambian qué representa el modelo, y uno mal puesto puede dejarlo sin representar nada.
Capítulo 1 · ≈ 5 min
El encoder reduce cada observación a unos pocos números; el decoder intenta recuperarla. El cuello de botella obliga a quedarse con lo esencial.
No produce un punto, sino una distribución (media μ y desviación σ). Cada observación ocupa una región difusa, así que el espacio latente queda continuo y sin huecos.
Un segundo término empuja esas distribuciones hacia una gaussiana estándar. Gracias a eso se puede muestrear del espacio y generar datos nuevos.
L = reconstrucción + β · KL
«Que la salida se parezca a la entrada.» Empuja a usar el código latente para guardar todo lo posible.
quiere fidelidad«Que el código se parezca a una gaussiana estándar.» Empuja a no usar el código más de lo necesario.
quiere ordenEse equilibrio es el corazón del modelo, y β es el mando que lo mueve. Sin el KL tendrías un autoencoder normal; con demasiado KL el modelo deja de usar el latente por completo. Ambos extremos los verás en el capítulo 2.
La suma con β = 1 es el ELBO con signo cambiado (Kingma & Welling, 2014). Se mide en nats.
Hay un problema: si z se sortea de una distribución, no se puede derivar a través del sorteo y la red no puede entrenarse. La solución es escribir el azar aparte:
z = μ + σ · ε, con ε ~ N(0, 1)
Así el azar (ε) entra como una constante en cada paso y el gradiente puede fluir hasta μ y σ. Es la idea que hizo entrenable al VAE (Kingma & Welling, 2014; Rezende et al., 2014).
En este laboratorio el modelo se entrena con Adam y los gradientes están derivados a mano, sin librerías: puedes leer las 200 líneas de vae.js.
Para profundizar: la revisión de conjunto de Kingma & Welling (2019) y los capítulos 14 y 20 de Goodfellow et al. (2016).
Capítulo 2 · ≈ 14 min
Cada uno entrena un modelo con una configuración concreta para que veas un fenómeno. Después explora libremente.
Cada casilla es una observación: una imagen de 8×8, es decir 64 variables (una por píxel, de 0 a 1). Se generan a partir de unos pocos factores conocidos (posición, tamaño…), lo que permite comprobar después si el modelo los descubre. Sin PCA: el modelo recibe los 64 números tal cual.
Las dos partes de la pérdida, época a época. La reconstrucción (azul, eje izquierdo) debería bajar; el KL (naranja, eje derecho) cuenta cuánta información usa el latente.
Cuánta información transporta cada dimensión. Las barras grises están apagadas: el modelo no las usa.
Cada punto es una observación, colocada en su código μ. El color muestra un factor real: si varía de forma ordenada, el modelo lo capturó.
Qué produce el decoder al recorrer el espacio latente. Aquí se ve si el modelo es generativo de verdad: puntos vecinos deben dar imágenes parecidas y plausibles.
Clic en el espacio latente para elegir otra.
Para cada factor que generó los datos, con qué fuerza se alinea con alguna dimensión latente.
El error de reconstrucción como score: lo que el modelo no sabe reconstruir es candidato a anomalía.
Entrena varios modelos desde cero con distintos valores del hiperparámetro activo (2 semillas cada uno). Tarda unos segundos.
Si el KL cae a cero, el modelo ha dejado de usar el espacio latente: el encoder devuelve siempre el prior y el decoder reconstruye siempre la misma imagen promedio. Lo tramposo es que la pérdida puede parecer razonable y el modelo sigue produciendo scores y reconstrucciones. Se diagnostica con las dimensiones activas, no con la pérdida. Causas: β demasiado alto, red demasiado pequeña, o un decoder tan potente que no necesita el código (Bowman et al., 2016; Lucas et al., 2019). Remedios: recocido del KL —también en variante cíclica (Fu et al., 2019)—, bajar β, o free bits (Kingma et al., 2016).
Capítulo 3 · ≈ 8 min
Dos modelos con distinto β no son comparables por su pérdida: cada uno está minimizando una función distinta. Y aun con el mismo β, un modelo puede bajar la pérdida total subiendo la reconstrucción y hundiendo el KL — es decir, empeorando la representación. Mira siempre las dos partes por separado y cuántas dimensiones quedan activas.
| Indicador | ¿Necesita etiquetas? | Qué mide | Úsalo cuando… | Cuidado con… |
|---|---|---|---|---|
| Error de reconstrucción | No | Cuánta información se pierde al comprimir y reconstruir | Siempre: es la mitad de la pérdida | Baja siempre que subas la capacidad; por sí sola premia al autoencoder sin regularizar |
| KL | No | Información que el latente transporta | Siempre, junto a la anterior | Ni cero (colapso) ni enorme (latente disperso) |
| Dimensiones activas | No | Cuántas dimensiones tienen KL > 0.01 nats | Para elegir latent_dim y diagnosticar colapso | El umbral 0.01 es una convención, no una ley |
| −ELBO | No | Cota de la verosimilitud: reconstrucción + KL | Comparar modelos con el mismo β y los mismos datos | Es una cota, no la verosimilitud; no comparable entre β distintos |
| Inspección visual | No | Reconstrucciones y muestras del prior | Siempre; detecta colapso de un vistazo | Subjetiva; las muestras de un VAE son borrosas por naturaleza |
| Estabilidad entre semillas | No | Cuánto cambia el resultado solo por el azar | Antes de afirmar que una configuración es mejor | El latente cambia de orientación con la semilla: compara métricas, no coordenadas |
| Alineación con factores | Parcial | Si cada factor real vive en su propia dimensión (desenredo) | Datos sintéticos con factores conocidos | En datos reales no existe; las métricas de desenredo son discutidas (Locatello et al., 2019) |
| Utilidad posterior | Parcial | Si la representación mejora el modelo que la usa después | El VAE es un paso intermedio | Requiere etiquetas para esa tarea posterior |
| ROC-AUC / AP | Sí | Calidad del error de reconstrucción como detector; existe también la probabilidad de reconstrucción (An & Cho, 2015) | Detección de anomalías con casos confirmados | Con anomalías raras usa AP, no ROC-AUC (Saito & Rehmsmeier, 2015) |
No hay un "mejor VAE" en abstracto. Según el objetivo, miras cosas distintas:
β > 1 favorece que cada factor caiga en su propia dimensión (Higgins et al., 2017), y en este laboratorio se ve. Pero un estudio a gran escala mostró que el desenredo no supervisado es imposible sin supuestos previos, que las métricas discrepan entre sí y que el resultado depende tanto de la semilla como del hiperparámetro (Locatello et al., 2019). Trátalo como una propiedad deseable que hay que verificar, no como algo que β garantice.
Capítulo 4 · ≈ 8 min
Cada casilla es un modelo entrenado desde cero. Si los hiperparámetros fueran independientes, la mejor fila y la mejor columna se cruzarían en el óptimo; aquí no pasa eso: con β alto da igual cuántas dimensiones des, porque se apagan todas.
learning_rate y épocas. Si el entrenamiento no converge, cualquier otra configuración parecerá mala. Barre η en escala logarítmica.
hidden_units y profundidad. Sube hasta que la reconstrucción se aplane; si la red es muy pequeña, el modelo colapsa aunque β esté bien.
β y latent_dim a la vez, en cuadrícula o con búsqueda aleatoria. Nunca uno tras otro: interactúan.
Si aparece colapso, recocido del KL antes de tocar nada más.
La misma clasificación que usamos en todos los modelos del sitio: fíjalo, decídelo o solo reproducibilidad.
El capítulo 4 de Isolation Forest compara grid, random, bayesiana y successive halving con el mismo presupuesto; esos criterios se aplican igual aquí.
import optuna
def objective(trial):
beta = trial.suggest_float("beta", 0.2, 8.0, log=True)
latent = trial.suggest_int("latent_dim", 2, 32, log=True)
hidden = trial.suggest_categorical("hidden", [64, 128, 256])
lr = trial.suggest_float("lr", 1e-4, 1e-2, log=True)
model = VAE(latent_dim=latent, hidden=hidden)
for epoch in range(MAX_EPOCHS):
train_one_epoch(model, loader, lr=lr, beta=kl_warmup(beta, epoch))
val = evaluate(model, val_loader) # rec + KL, con beta=1 para comparar
trial.report(val["loss"], epoch)
if trial.should_prune(): # Hyperband descarta lo que no promete
raise optuna.TrialPruned()
if val["active_dims"] == 0: # colapso: configuración inservible
raise optuna.TrialPruned()
return val["loss"]
study = optuna.create_study(direction="minimize",
pruner=optuna.pruners.HyperbandPruner())
study.optimize(objective, n_trials=40)
Detalle importante: se descarta explícitamente el modelo colapsado. Sin esa comprobación, una búsqueda automática puede premiar configuraciones que no representan nada.
Capítulo 5 · ≈ 5 min
Los datos del laboratorio son sintéticos; estos casos describen situaciones típicas de forma ilustrativa, no resultados medidos.
Capítulo 6 · ≈ 4 min
latent_dim esperando mejora cuando β ya apagó las dimensiones.Referencias
✔ = ficha y contenido consultados en la fuente (texto completo o resumen oficial) al preparar este material. Datos menores como páginas o volumen conviene contrastarlos antes de una cita formal. Sin ✔: referencias estándar que no se consultaron directamente en esta revisión.