Estabilidad del Entrenamiento
Convergencia, gradientes evanescentes y explosivos, gradient clipping, batch normalization y todas las técnicas para que tus redes profundas entrenen de forma estable y eficiente.
¿Qué es la convergencia?
Entrenar una red neuronal es, en esencia, un problema de optimización: buscamos los valores de los pesos \mathbf{w} que minimizan una función de pérdida \mathcal{L}(\mathbf{w}). Decimos que el entrenamiento converge cuando el optimizador consigue encontrar un mínimo (o un punto suficientemente bueno) de esa función, y la loss deja de cambiar significativamente.
En la práctica, la convergencia perfecta es rara. Lo que buscamos es una convergencia práctica: que la loss descienda de manera estable hasta un valor bajo, que el modelo generalice bien, y que el proceso sea eficiente en tiempo y recursos.
Convergencia ≠ buen rendimiento. Un modelo puede converger a un mínimo malo (un plateau, un mínimo local subóptimo, o simplemente memorizar los datos). La convergencia es condición necesaria pero no suficiente para un buen modelo.
Tipos de problemas de convergencia
En deep learning, los problemas de convergencia se manifiestan de varias formas:
| Problema | Síntoma | Causa habitual |
|---|---|---|
| No converge | La loss oscila sin descender o sube | Learning rate demasiado alto, gradientes explosivos |
| Converge lentamente | La loss baja pero tarda centenares de épocas | Learning rate demasiado bajo, gradientes evanescentes |
| Se atasca | La loss se estanca en un valor alto (plateau) | Saddle points, mala inicialización, activaciones inadecuadas |
| Inestable | La loss oscila mucho entre pasos/épocas | Batch size muy pequeño, learning rate alto, gradientes ruidosos |
| Diverge | Loss = NaN o Inf | Explosión de gradientes, overflow numérico |
Factores que afectan a la convergencia
La convergencia y la estabilidad del entrenamiento dependen de la interacción entre múltiples factores. Entender cada uno es esencial para diagnosticar y solucionar problemas.
Tasa de aprendizaje (Learning Rate)
La tasa de aprendizaje \eta es, sin duda, el hiperparámetro más importante del entrenamiento. Controla el tamaño de los pasos que da el optimizador en el espacio de parámetros:
- η demasiado alto: los pasos son tan grandes que el optimizador "salta" sobre los mínimos, la loss oscila o crece, y en casos extremos los pesos divergen hacia el infinito (gradientes explosivos). La loss puede volverse NaN.
- η demasiado bajo: los pasos son tan pequeños que la convergencia es extremadamente lenta. El optimizador puede quedarse atrapado en mínimos locales o saddle points de los que no puede escapar.
- η adecuado: la loss desciende de manera suave y consistente. En la práctica, el rango adecuado es bastante estrecho y depende del problema, la arquitectura y el optimizador.
Para profundizar en learning rate y técnicas avanzadas de ajuste, consulta el submódulo Fundamentos › Entrenamiento de Redes Neuronales, donde se cubre SGD, tasa de aprendizaje, epochs y backpropagation en detalle.
Tamaño del lote (Batch Size)
El batch size determina cuántos ejemplos se procesan antes de actualizar los pesos. Tiene un impacto profundo en la convergencia y la estabilidad:
| Batch size | Gradientes | Convergencia | Generalización |
|---|---|---|---|
| Muy pequeño (1–8) | Muy ruidosos, alta varianza | Inestable, muchas oscilaciones | Buena (ruido actúa como regularización) |
| Pequeño (16–64) | Ruidosos pero manejables | Razonablemente estable | Buena |
| Medio (128–512) | Buena estimación del gradiente real | Estable, suave | Buena si se ajusta el LR |
| Grande (1024+) | Estimación precisa, baja varianza | Muy estable pero puede estancarse | Puede empeorar (sharp minima) |
La regla del scaling lineal: si multiplicas el batch size por k, deberías multiplicar también el learning rate por k (Goyal et al., 2017 — arXiv:1706.02677). Sin este ajuste, batches grandes convergen más lento porque cada paso tiene el mismo tamaño pero usa una estimación más precisa (menos ruido, menos exploración).
Optimizadores y Momentum
El optimizador determina cómo se usan los gradientes para actualizar los pesos. Un buen optimizador puede transformar un problema de convergencia en uno sencillo:
- SGD vanilla: sigue la dirección del gradiente puro. Puede ser lento en superficies con curvatura desigual (valles estrechos y alargados).
- SGD + Momentum: acumula "inercia" de gradientes pasados, lo que acelera la convergencia en la dirección consistente y suaviza las oscilaciones. Mejora notablemente la estabilidad.
- Adam / AdamW: combinan momentum con tasas de aprendizaje adaptativas por parámetro. Son más robustos ante malas elecciones de learning rate y convergen rápidamente, pero pueden tener problemas de generalización sin weight decay adecuado.
Para un estudio completo de todos los optimizadores (AdaGrad, RMSprop, Adam, AdamW, NAdam, Lion…) y los learning rate schedulers (Step, Cosine, Warmup, OneCycleLR…), consulta Técnicas de Entrenamiento › Schedulers y Optimizers.
Learning Rate Schedulers
Los schedulers ajustan dinámicamente el learning rate durante el entrenamiento. Son fundamentales para la estabilidad porque permiten:
- Warmup: empezar con un LR muy pequeño e ir subiendo gradualmente. Esto evita inestabilidades al inicio del entrenamiento, cuando los pesos son aleatorios y los gradientes pueden ser muy grandes.
- Decay progresivo: reducir el LR conforme avanza el entrenamiento, permitiendo al optimizador "afinar" el mínimo encontrado con pasos cada vez más pequeños.
- Cosine Annealing: un schedule suave que ha demostrado excelentes resultados en la práctica, especialmente combinado con warmup.
Inicialización de pesos
La inicialización de los pesos tiene un impacto directo en la estabilidad de las primeras épocas de entrenamiento. Una mala inicialización puede provocar que los gradientes se desvanezcan o exploten desde el primer paso:
- Inicialización a cero: todas las neuronas calculan lo mismo → no aprenden. Nunca uses esta inicialización.
- Inicialización aleatoria sin escalar: con varianzas demasiado grandes o pequeñas, las activaciones se saturan o se anulan, provocando gradientes inestables.
- Xavier/Glorot: escala la varianza según el número de neuronas de entrada y salida. Adecuada para activaciones como tanh y sigmoid.
- He/Kaiming: escala la varianza considerando que ReLU "apaga" la mitad de las neuronas. Estándar para redes con ReLU.
Para un estudio detallado de las técnicas de inicialización (Xavier, He, ortogonal, LSUV…), consulta Técnicas de Entrenamiento › Inicialización de Pesos.
Gradientes evanescentes y explosivos
Esta es la sección más importante de este submódulo. Los gradientes evanescentes (vanishing gradients) y los gradientes explosivos (exploding gradients) son los dos problemas fundamentales que dificultan el entrenamiento de redes profundas. Entenderlos es esencial para diagnosticar problemas y aplicar soluciones efectivas.
¿Qué son?
Cuando entrenamos una red neuronal, el algoritmo de backpropagation calcula los gradientes de la loss respecto a cada peso, propagando la señal de error desde la última capa hasta la primera. El problema surge porque estos gradientes se calculan como un producto encadenado de derivadas parciales. Si las derivadas individuales son todas menores que 1, su producto tiende a cero exponencialmente. Si son mayores que 1, el producto crece exponencialmente.
Estos problemas fueron identificados ya en los años 90 por Sepp Hochreiter en su tesis doctoral (1991) y formalizados más extensamente por Hochreiter et al. (2001 — «Gradient Flow in Recurrent Nets») y por Yoshua Bengio, Patrice Simard y Paolo Frasconi (1994 — IEEE Trans. Neural Netw.). Durante más de una década, estos problemas limitaron severamente la profundidad de las redes que se podían entrenar. Las soluciones modernas — ReLU, buena inicialización, normalización, conexiones residuales — han ido apareciendo gradualmente entre 2010 y 2016, transformando lo que antes era un callejón sin salida en una limitación manejable.
| Problema | ¿Qué pasa? | Consecuencia |
|---|---|---|
| Vanishing gradients | Los gradientes se hacen exponencialmente pequeños al propagarse hacia las capas iniciales | Las primeras capas no aprenden o lo hacen extremadamente lento. La red se comporta como si solo las últimas capas estuvieran entrenándose. |
| Exploding gradients | Los gradientes crecen exponencialmente al propagarse hacia las capas iniciales | Los pesos se actualizan con valores enormes, la loss oscila violentamente o se vuelve NaN. El entrenamiento diverge. |
Fundamento matemático
Para entender por qué ocurren estos problemas, necesitamos mirar la regla de la cadena aplicada a redes profundas. Consideremos una red feedforward con L capas. La salida de la capa l es:
donde \mathbf{W}^{(l)} es la matriz de pesos, \mathbf{a}^{(l)} son las activaciones, y \sigma es la función de activación. Para calcular el gradiente de la loss \mathcal{L} respecto a los pesos de la capa l, aplicamos la regla de la cadena:
El término clave es el producto de Jacobianos. Cada Jacobiano \frac{\partial \mathbf{a}^{(k)}}{\partial \mathbf{a}^{(k-1)}} puede descomponerse como:
donde \text{diag}(\sigma'(\mathbf{z}^{(k)})) es una matriz diagonal con las derivadas de la función de activación evaluadas en el pre-activation. El gradiente completo se convierte en:
Aquí está la clave: si cada factor |\sigma'(z) \cdot W| < 1, el producto de L - l factores tiende a 0 exponencialmente. Si cada factor es > 1, el producto tiende a \infty. Con 50, 100 o 200 capas, incluso factores de 0.9 o 1.1 producen valores astronómicamente pequeños o grandes.
Ejemplo numérico: el efecto exponencial
Imaginemos una red con 50 capas y un factor de escala constante \gamma por capa:
| Factor por capa \gamma | Gradiente en capa 1 (relativo) | Resultado |
|---|---|---|
| 0.5 | 0.5^{50} \approx 8.9 \times 10^{-16} | 🧊 Vanishing: gradiente prácticamente cero |
| 0.9 | 0.9^{50} \approx 5.2 \times 10^{-3} | ⚠️ Vanishing moderado: la capa 1 aprende ~200× más lento |
| 1.0 | 1.0^{50} = 1 | ✅ Perfecto: gradiente estable |
| 1.1 | 1.1^{50} \approx 117 | ⚠️ Exploding moderado: actualizaciones 117× más grandes |
| 2.0 | 2.0^{50} \approx 1.13 \times 10^{15} | 💥 Exploding: gradiente astronómico, NaN seguro |
Causas del vanishing gradient
1. Funciones de activación saturantes
Las funciones sigmoid y tanh tienen derivadas que se acercan a cero cuando su entrada es muy positiva o muy negativa:
La derivada de la sigmoid tiene un máximo de 0.25 (en z=0). En la práctica, para la mayoría de las neuronas la derivada es mucho menor que 0.25 porque las entradas rara vez están centradas exactamente en cero. Esto significa que cada capa multiplica el gradiente por un factor ≤ 0.25, y con pocas capas el gradiente desaparece.
Tanh es mejor que sigmoid (derivada máxima de 1 en vez de 0.25), pero sigue siendo saturante: para |z| > 3 la derivada es prácticamente cero.
Solución: usar ReLU (\max(0, z)) o sus variantes (Leaky ReLU, ELU, GELU, SiLU). ReLU tiene derivada 1 para z > 0 y 0 para z < 0. Aunque las neuronas "muertas" (z < 0 siempre) son un problema, es mucho más estable que las funciones saturantes en redes profundas.
2. Mala inicialización de pesos
Si los pesos se inicializan con una varianza demasiado pequeña, las activaciones se comprimen hacia cero capa tras capa. Si se inicializan con varianza demasiado grande, se saturan las activaciones en las regiones donde \sigma'(z) \approx 0. En ambos casos, los gradientes se desvanecen.
3. Redes muy profundas sin técnicas de estabilización
Incluso con ReLU y buena inicialización, redes con decenas o centenares de capas pueden sufrir degradación del gradiente si no se emplean técnicas adicionales como batch normalization, conexiones residuales (skip connections), o normalización por capas.
Causas del exploding gradient
1. Pesos con norma espectral grande
Si los valores singulares de las matrices de pesos \mathbf{W}^{(k)} son mayores que 1, el producto de matrices a lo largo de las capas amplifica el gradiente exponencialmente. Esto es especialmente problemático cuando la inicialización asigna valores grandes a los pesos.
2. Learning rate demasiado alto
Un learning rate excesivo puede crear un ciclo destructivo: los pesos se actualizan demasiado, lo que produce gradientes más grandes en el siguiente paso, que a su vez producen actualizaciones aún mayores. Este feedback positivo lleva rápidamente a la divergencia y valores NaN.
3. Redes recurrentes (RNNs)
Las RNNs son especialmente vulnerables a los gradientes explosivos porque la misma matriz de pesos \mathbf{W}_{hh} se multiplica consigo misma en cada paso temporal:
Si la norma espectral de \mathbf{W}_{hh} es mayor que 1/\max|\sigma'|, los gradientes explotan a lo largo del tiempo. Por eso las RNNs clásicas tienen problemas para capturar dependencias a largo plazo: los gradientes explotan o se desvanecen exponencialmente con la longitud de la secuencia. Las LSTM y GRU se diseñaron específicamente para mitigar este problema mediante mecanismos de "puertas" (gates) que controlan el flujo del gradiente.
Señales de alarma de exploding gradients:
- La loss salta a valores muy grandes repentinamente
- Aparecen valores
NaNoInfen la loss o en los pesos - La norma del gradiente crece aceleradamente (monitorízala con
torch.nn.utils.clip_grad_norm_) - Los pesos se vuelven muy grandes (>100) en pocas épocas
¿Dónde aparecen estos problemas?
| Tipo de red | Vanishing | Exploding | Soluciones habituales |
|---|---|---|---|
| MLPs profundas (>10 capas) | Muy frecuente con sigmoid/tanh | Posible con mala inicialización | ReLU, He init, batch norm |
| CNNs profundas | Sí, especialmente antes de ResNets | Menos frecuente | Skip connections (ResNets), batch norm |
| RNNs clásicas | Muy frecuente (secuencias largas) | Muy frecuente (secuencias largas) | LSTM, GRU, gradient clipping |
| Transformers | Menos frecuente (attention es poco profundo) | Posible en modelos muy grandes | Layer norm, warmup, residual connections |
| GANs | En el generador | En el discriminador | Spectral norm, gradient penalty, WGAN |
Soluciones preventivas
Antes de recurrir a técnicas como gradient clipping o batch normalization (que veremos a continuación), hay decisiones de diseño que pueden prevenir los problemas de gradientes inestables desde el principio. Estas soluciones atacan las causas raíz.
1. Elegir buenas funciones de pérdida
La función de pérdida no solo mide el error del modelo, sino que su derivada es el punto de partida de la cadena de gradientes. Algunas funciones de pérdida producen gradientes más estables que otras:
| Función de pérdida | Gradiente respecto a la salida | Estabilidad |
|---|---|---|
| MSE + Sigmoid | 2(\hat{y}-y) \cdot \sigma'(z) → incluye \sigma' que satura | ❌ Mala — gradientes muy pequeños cuando la predicción está lejos del target (la sigmoid satura y su derivada → 0) |
| Cross-Entropy + Sigmoid | \hat{y} - y → la derivada de la sigmoid se cancela | ✅ Buena — el gradiente es simplemente la diferencia entre predicción y target, sin factores saturantes |
| Cross-Entropy + Softmax | \hat{y}_i - y_i → misma cancelación | ✅ Buena — estándar para clasificación multiclase |
¿Por qué se cancelan? La derivada de la Cross-Entropy respecto al logit z es \frac{\partial \mathcal{L}}{\partial z} = \sigma(z) - y. Observa que \sigma'(z) no aparece — se cancela al combinar la derivada de la loss con la de la sigmoid. Esto elimina el factor saturante que causaba vanishing gradients con MSE.
Consejo práctico: para clasificación, usa siempre Cross-Entropy (o su variante binaria, Binary Cross-Entropy). Para regresión, MSE o Huber loss son adecuadas porque la capa de salida suele ser lineal (sin activación saturante).
2. Arquitecturas con conexiones residuales (skip connections)
Las conexiones residuales, introducidas en ResNet (He et al., 2015 — arXiv:1512.03385), son la solución más elegante al problema de vanishing gradients en redes profundas. La idea es simple pero poderosa:
En lugar de que la capa aprenda la transformación completa, aprende solo el residuo \mathcal{F}(\mathbf{x}) = \text{salida deseada} - \mathbf{x}. La conexión de identidad (+ \mathbf{a}^{(l-1)}) crea una "autopista" por la que el gradiente puede fluir sin multiplicaciones adicionales.
¿Por qué funcionan?
Al calcular el gradiente a través de un bloque residual:
El término + \mathbf{I} (matriz identidad) garantiza que el gradiente nunca sea menor que 1 en el camino directo. Incluso si \frac{\partial \mathcal{F}}{\partial \mathbf{a}^{(l-1)}} se desvanece, la identidad preserva el gradiente. Esto es lo que permite entrenar redes de 100, 200 o incluso 1000 capas.
Dato histórico: antes de ResNets, las redes de más de ~20 capas entrenaban peor que las de menos capas (paradoja de la degradación). ResNet demostró que se podían entrenar redes de 152 capas superando a redes de 20 capas, algo que parecía imposible. ResNet ganó el ImageNet Challenge 2015 con un error del 3.57%.
Variantes de conexiones residuales
| Variante | Fórmula | Usado en |
|---|---|---|
| Residual clásico | y = F(x) + x | ResNet |
| Dense connections | y = F([x_0, x_1, ..., x_l]) | DenseNet — cada capa recibe las features de todas las anteriores |
| Highway connections | y = T(x) \cdot F(x) + (1-T(x)) \cdot x | Highway Networks — puerta aprendible |
| Pre-activation residual | BN → ReLU → Conv → BN → ReLU → Conv + skip | ResNet v2 (He et al., 2016) |
3. Inicialización adecuada de pesos
La inicialización de pesos busca que, en el forward pass, la varianza de las activaciones se mantenga constante a lo largo de las capas, y en el backward pass, la varianza de los gradientes se mantenga constante. Si ambas condiciones se cumplen, no hay ni vanishing ni exploding.
La diferencia entre ambas: Xavier asume que la activación es lineal alrededor de cero (válido para tanh), mientras que He tiene en cuenta que ReLU "apaga" la mitad de las neuronas (multiplica la varianza por 2 para compensar).
Para un estudio completo de las técnicas de inicialización (Xavier, He, ortogonal, LSUV, fixup…) y su derivación matemática, consulta Técnicas de Entrenamiento › Inicialización de Pesos.
Checklist de prevención:
- ✅ Usar Cross-Entropy para clasificación (no MSE + sigmoid)
- ✅ Usar ReLU o variantes (no sigmoid/tanh en capas ocultas)
- ✅ Usar He init para redes con ReLU, Xavier para tanh
- ✅ Usar skip connections en redes con más de ~10-20 capas
- ✅ Usar LSTM/GRU en vez de RNN vanilla para secuencias
- ✅ Empezar con un learning rate conservador y usar warmup
Gradient Clipping
Gradient clipping es la técnica más directa para combatir los gradientes explosivos. La idea es simple: si el gradiente es demasiado grande, lo recortamos antes de usarlo para actualizar los pesos. Es como poner un "limitador de velocidad" al optimizador.
La técnica fue formalizada y popularizada por Pascanu, Mikolov & Bengio (2013), quienes proporcionaron un análisis riguroso de la dinámica de los gradientes en RNNs y demostraron que el gradient clipping por norma es una solución sencilla y efectiva. Desde entonces, se ha convertido en una técnica estándar en prácticamente todos los frameworks de entrenamiento de modelos de secuencia.
Existen dos variantes principales:
Clipping por norma (Clip-by-Norm)
Esta es la variante más usada y recomendada. Si la norma total del vector de gradientes supera un umbral \theta, se reescala proporcionalmente para que la norma sea exactamente \theta:
Ventaja clave: preserva la dirección del gradiente. Solo reduce su magnitud. El optimizador sigue moviéndose en la dirección correcta, pero con pasos más pequeños cuando los gradientes son demasiado grandes.
import torch
import torch.nn as nn
model = MyModel()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
max_norm = 1.0 # Umbral de clipping
for batch in dataloader:
optimizer.zero_grad()
loss = criterion(model(batch.x), batch.y)
loss.backward()
# Clipping por norma — ANTES de optimizer.step()
total_norm = torch.nn.utils.clip_grad_norm_(
model.parameters(),
max_norm=max_norm
)
# total_norm contiene la norma ORIGINAL (antes del clipping)
# Útil para monitorizar si el clipping se activa frecuentemente
optimizer.step()
# Monitorización: ¿se está activando el clipping?
if total_norm > max_norm:
print(f"⚠️ Gradiente clipped: {total_norm:.2f} → {max_norm}")
Clipping por valor (Clip-by-Value)
En esta variante, cada componente individual del gradiente se recorta al rango [-\theta, \theta]:
Problema: el clipping por valor puede cambiar la dirección del gradiente. Si algunos componentes se recortan y otros no, el vector resultante apunta en una dirección diferente al gradiente original. Por eso, el clipping por norma es generalmente preferible.
# Clipping por valor — menos recomendado
torch.nn.utils.clip_grad_value_(
model.parameters(),
clip_value=0.5 # Cada componente se recorta a [-0.5, 0.5]
)
Comparación de métodos
| Método | Preserva dirección | Hiperparámetro | Uso recomendado |
|---|---|---|---|
| Clip-by-Norm | ✅ Sí | \theta = norma máxima (típico: 1.0–5.0) | Estándar en la mayoría de aplicaciones. Especialmente RNNs y Transformers. |
| Clip-by-Value | ❌ No | \theta = valor máximo por componente | Poco usado. Solo cuando necesitas acotar componentes individuales. |
¿Cuándo usar gradient clipping?
- RNNs / LSTMs / GRUs: casi siempre. Las RNNs son inherentemente propensas a gradientes explosivos por la multiplicación repetida de la misma matriz. Un \theta = 1.0 es un buen punto de partida.
- Transformers / LLMs: muy frecuente. GPT-2 usa \theta = 1.0, GPT-3 usa \theta = 1.0, BERT usa \theta = 1.0.
- GANs: a veces, para estabilizar el entrenamiento del discriminador.
- CNNs / MLPs estándar: raramente necesario si se usa buena inicialización y batch normalization. Pero no hace daño tenerlo como "red de seguridad".
Cabe mencionar que los optimizadores adaptativos como Adam ya proporcionan cierta protección contra gradientes grandes al escalar cada componente por su segundo momento. Sin embargo, en la práctica se observa que gradient clipping sigue siendo beneficioso incluso con Adam, especialmente en las primeras épocas de entrenamiento cuando los gradientes son más inestables. Esto es consistente con la observación de Zhang et al. (2020), quienes muestran que el warmup del learning rate y el gradient clipping cumplen funciones complementarias para estabilizar el entrenamiento temprano.
Consejo práctico: monitoriza la norma del gradiente durante el entrenamiento. Si ves picos (spikes) seguidos de caídas en la loss, gradient clipping puede ayudar. Empieza con \theta = 1.0 y ajusta: si el clipping se activa en >50% de los pasos, aumenta \theta; si nunca se activa, puedes bajarlo o quitarlo.
Batch Normalization
Batch Normalization (Ioffe & Szegedy, 2015 — arXiv:1502.03167) es una de las técnicas más influyentes en deep learning moderno. Permite entrenar redes más profundas, con learning rates más altos, y convergencia más rápida. Es tan fundamental que se incluye por defecto en casi todas las arquitecturas modernas.
Para contextualizar su impacto: antes de BatchNorm (2015), entrenar una red como VGG-16 requería un ajuste meticuloso del learning rate, una inicialización cuidadosa y centenares de épocas. Con BatchNorm, redes similares convergen en una fracción del tiempo y son mucho más robustas a la elección de hiperparámetros. El paper original acumuló más de 40 000 citas y la técnica se considera uno de los avances más importantes de la década de 2010 en deep learning, junto con ReLU, ResNets, los Transformers y los mecanismos de atención.
Motivación: Internal Covariate Shift
Durante el entrenamiento, los pesos de cada capa cambian en cada paso de optimización. Esto significa que la distribución de las entradas de cada capa cambia constantemente — lo que Ioffe y Szegedy llamaron Internal Covariate Shift.
Imagina que la capa 5 ha aprendido a trabajar con entradas que tienen media 0 y varianza 1. Pero entonces los pesos de la capa 4 se actualizan, y ahora las entradas a la capa 5 tienen media 3 y varianza 10. La capa 5 tiene que "readaptarse" constantemente a distribuciones cambiantes, lo que ralentiza y desestabiliza el entrenamiento.
Nota: la teoría del Internal Covariate Shift como explicación de por qué BatchNorm funciona ha sido cuestionada (Santurkar et al., 2018 — arXiv:1805.11604). Estudios posteriores sugieren que BatchNorm funciona principalmente porque suaviza el paisaje de la loss (smoothing the loss landscape), haciendo que la optimización sea más estable y permitiendo learning rates más altos.
Forward pass: las matemáticas
Batch Normalization se aplica típicamente antes de la función de activación (aunque hay debate sobre si es mejor antes o después). Dado un mini-batch \mathcal{B} = \{x_1, \ldots, x_m\} de m ejemplos, BatchNorm realiza cuatro operaciones:
Donde:
- \epsilon es una constante pequeña (típicamente 10^{-5}) para evitar división por cero.
- \gamma (escala) y \beta (desplazamiento) son parámetros aprendibles que permiten a la red "desnormalizar" si lo necesita. Son inicializados a \gamma = 1 y \beta = 0.
¿Por qué γ y β? Si solo normalizamos, restringimos la representación de la red: cada capa solo podría producir activaciones con media 0 y varianza 1. Los parámetros \gamma y \beta devuelven la capacidad expresiva: si la red necesita que una capa tenga media 5 y varianza 3, puede aprenderlo ajustando estos parámetros. En el caso extremo donde \gamma = \sigma_\mathcal{B} y \beta = \mu_\mathcal{B}, BatchNorm se convierte en la identidad.
¿Dónde se aplica?
En una capa típica con pesos \mathbf{W}, bias \mathbf{b} y activación \sigma:
| Sin BatchNorm | Con BatchNorm (pre-activation) |
|---|---|
| \sigma(\mathbf{W}\mathbf{x} + \mathbf{b}) | \sigma(\text{BN}(\mathbf{W}\mathbf{x})) |
Observa que el bias \mathbf{b} se elimina cuando se usa BatchNorm, porque la resta de la media lo anularía de todos modos. El parámetro \beta de BatchNorm cumple la misma función.
Backward pass
BatchNorm es diferenciable, por lo que los gradientes se pueden propagar a través de él. Las derivadas son más complejas que las de una capa lineal porque la normalización introduce dependencias entre los ejemplos del mini-batch:
El factor \frac{\gamma}{\sqrt{\sigma^2_\mathcal{B}+\epsilon}} actúa como un reescalado del gradiente: si la varianza de las activaciones es grande, los gradientes se reducen; si es pequeña, se amplifican. Esto es lo que estabiliza el flujo del gradiente automáticamente.
Entrenamiento vs Inferencia
Un detalle crucial de BatchNorm es que se comporta de manera diferente durante el entrenamiento y durante la inferencia:
| Aspecto | Entrenamiento | Inferencia |
|---|---|---|
| Estadísticas usadas | Media y varianza del mini-batch actual | Media y varianza acumuladas (running mean/var) |
| ¿Depende del batch? | Sí — la normalización depende de los otros ejemplos del batch | No — usa estadísticas fijas, cada ejemplo es independiente |
| Actualización de running stats | Sí: \bar{\mu} \leftarrow (1-\alpha)\bar{\mu} + \alpha \mu_\mathcal{B} | No — las estadísticas se congelan |
| En PyTorch | model.train() |
model.eval() |
Error común: olvidar llamar a model.eval() antes de la
inferencia. Si BatchNorm sigue en modo entrenamiento durante la evaluación, las predicciones
dependerán del batch y serán inestables. Este es uno de los bugs más frecuentes en PyTorch.
import torch
import torch.nn as nn
class StableNet(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden_dim, bias=False), # Sin bias
nn.BatchNorm1d(hidden_dim), # BN antes de activación
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim, bias=False),
nn.BatchNorm1d(hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim, bias=False),
nn.BatchNorm1d(hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, output_dim) # Última capa sin BN
)
def forward(self, x):
return self.net(x)
model = StableNet(784, 256, 10)
# Entrenamiento
model.train() # BatchNorm usa estadísticas del mini-batch
for batch_x, batch_y in train_loader:
pred = model(batch_x)
loss = criterion(pred, batch_y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
# Evaluación — ¡CRUCIAL!
model.eval() # BatchNorm usa running mean/var
with torch.no_grad():
for batch_x, batch_y in test_loader:
pred = model(batch_x)
# pred ahora es determinista e independiente del batch
¿Por qué funciona BatchNorm?
Aunque la explicación original de Internal Covariate Shift ha sido cuestionada, hay varias razones por las que BatchNorm mejora el entrenamiento:
- Suaviza el paisaje de la loss (Santurkar et al., 2018): BatchNorm hace que la función de pérdida sea más suave (menos curva), lo que significa que los gradientes cambian menos bruscamente. Esto permite usar learning rates más altos sin divergir.
- Estabiliza los gradientes: al mantener las activaciones en un rango controlado, evita que los gradientes se desvanezcan o exploten. El factor 1/\sqrt{\sigma^2 + \epsilon} actúa como un regulador automático.
- Efecto regularizador: dado que las estadísticas dependen del mini-batch, cada ejemplo se normaliza con estadísticas ligeramente diferentes, lo que introduce ruido y actúa como una forma suave de regularización (similar a dropout).
- Permite learning rates más altos: en la práctica, BatchNorm permite multiplicar el learning rate por 5-10× respecto a redes sin normalización. Esto acelera significativamente la convergencia.
Otras técnicas de normalización
Batch Normalization revolucionó el entrenamiento de redes profundas, pero tiene limitaciones importantes: depende del tamaño del mini-batch, no funciona bien con batches muy pequeños, y resulta problemática en secuencias de longitud variable (RNNs) y en la generación de imágenes. Para superar estas restricciones, la comunidad investigadora ha desarrollado múltiples alternativas que normalizan a lo largo de dimensiones diferentes. Hoy en día, cada familia de arquitecturas tiende a usar su propia técnica de normalización.
Todas las técnicas comparten el mismo esquema general: (1) calcular estadísticas (media y/o varianza) sobre un subconjunto de las dimensiones del tensor, (2) normalizar, y (3) aplicar parámetros aprendibles de escala y desplazamiento. La diferencia clave está en sobre qué dimensiones se calculan las estadísticas.
Diagrama de dimensiones para un tensor 4D (N, C, H, W)
Layer Normalization
Layer Normalization fue propuesta por Jimmy Lei Ba, Jamie Ryan Kiros y Geoffrey E. Hinton en 2016 (arXiv:1607.06450). A diferencia de BatchNorm, que normaliza a lo largo de la dimensión del batch (calculando estadísticas para cada canal compartidas entre todos los ejemplos), Layer Normalization calcula las estadísticas por cada ejemplo individual, a lo largo de todas las features/canales.
Fórmula
Para un vector de activaciones \mathbf{x} \in \mathbb{R}^H (donde H es el número de features en la capa):
La diferencia fundamental con BatchNorm es que \mu y \sigma^2 se calculan por cada ejemplo (sobre las features), en lugar de por cada feature (sobre el batch). Esto tiene una consecuencia crucial: LayerNorm no depende del tamaño del batch ni necesita mantener estadísticas acumuladas (running stats) como BatchNorm.
¿Por qué se usa en Transformers?
Layer Normalization se convirtió en el componente estándar de los Transformers desde el paper original de Vaswani et al. (2017), «Attention Is All You Need». Las razones son varias:
- Independencia del batch: en NLP, los batches suelen tener secuencias de longitudes muy diferentes (incluso con padding). BatchNorm calcularía estadísticas mezclando tokens reales con tokens de padding, contaminando la normalización. LayerNorm normaliza cada secuencia y cada posición de forma independiente.
-
Estabilidad en inferencia: como no depende de estadísticas del batch,
LayerNorm se comporta exactamente igual en entrenamiento y en inferencia. No hay
riesgo de bugs por olvidar
model.eval(). - Funciona con batch size = 1: esencial para generación autorregresiva (GPT, LLaMA), donde cada token se genera de uno en uno.
Pre-LN vs Post-LN
En la arquitectura Transformer existen dos variantes principales según la ubicación de LayerNorm:
| Variante | Fórmula del bloque | Usado en | Estabilidad |
|---|---|---|---|
| Post-LN (original) | y = \text{LN}(x + \text{SubLayer}(x)) | BERT, Transformer original | Necesita warmup cuidadoso |
| Pre-LN | y = x + \text{SubLayer}(\text{LN}(x)) | GPT-2, GPT-3, la mayoría de LLMs | Más estable, no requiere warmup |
La variante Pre-LN, analizada en detalle por Xiong et al. (2020, arXiv:2002.04745), ha demostrado ser significativamente más estable durante el entrenamiento porque los gradientes fluyen de manera más uniforme a través de las conexiones residuales. Por esta razón, la mayoría de los modelos de lenguaje grandes (GPT-2, GPT-3, PaLM, etc.) adoptan Pre-LN.
Dato relevante: BERT utiliza Post-LN, lo que requiere un schedule de warmup cuidadoso (típicamente 10 000 pasos con rampa lineal). Sin warmup, el entrenamiento de BERT con Post-LN diverge. Con Pre-LN, el warmup es menos crítico o incluso innecesario.
RMSNorm (Root Mean Square Normalization)
RMSNorm fue propuesta por Biao Zhang y Rico Sennrich en 2019 (arXiv:1910.07467). Su hipótesis central es que la resta de la media (re-centering) de LayerNorm es innecesaria y que todo el beneficio proviene del re-escalado por la magnitud. Esto simplifica la operación y la hace más eficiente computacionalmente.
Fórmula
Comparado con LayerNorm, RMSNorm:
- No calcula la media \mu → ahorra una reducción completa sobre el vector.
- No resta la media → ahorra una sustracción elemento a elemento.
- Solo divide por la RMS (root mean square) de los valores.
- Típicamente no incluye el parámetro de bias \beta (solo la escala \gamma).
¿Por qué importa la eficiencia?
En LLMs con miles de millones de parámetros, la normalización se ejecuta cientos de veces por cada forward pass (una vez por sub-capa de cada bloque Transformer). Incluso una reducción modesta en el coste de cada normalización — estimada en un 10-15% de aceleración respecto a LayerNorm — se traduce en un ahorro significativo de tiempo y de energía a escala.
Adopción en modelos modernos
RMSNorm se ha convertido en la normalización estándar de los LLMs más recientes:
- LLaMA / LLaMA 2 / LLaMA 3 (Meta, 2023-2024): Pre-RMSNorm en todos los bloques.
- Gemma (Google, 2024): RMSNorm como normalización principal.
- Mistral / Mixtral (Mistral AI, 2023-2024): RMSNorm.
- Qwen 2 (Alibaba, 2024): RMSNorm.
- PaLM / PaLM 2 (Google, 2022-2023): RMSNorm con Pre-LN.
Intuición: la idea detrás de RMSNorm es que lo que importa no es que las activaciones tengan media cero, sino que tengan una escala controlada. Dividir por la RMS normaliza la «energía» del vector sin alterar su dirección relativa al origen. Empíricamente, esto funciona tan bien como LayerNorm en la mayoría de los casos.
Group Normalization
Group Normalization fue propuesta por Yuxin Wu y Kaiming He en 2018 (arXiv:1803.08494). Divide los canales en G grupos y calcula las estadísticas de normalización dentro de cada grupo, para cada ejemplo.
Fórmula
Para un tensor de activaciones \mathbf{x} \in \mathbb{R}^{C \times H \times W} con G grupos (cada grupo tiene C/G canales):
donde \mathcal{S}_g es el conjunto de canales y posiciones espaciales que pertenecen al grupo g.
¿Por qué es útil?
El problema principal de BatchNorm en visión por computador ocurre cuando el batch size es pequeño (1-4 imágenes). Esto sucede frecuentemente en tareas que requieren imágenes de alta resolución como detección de objetos y segmentación semántica, donde la memoria de la GPU limita el tamaño del batch. Con batches tan pequeños, las estadísticas de BatchNorm son muy ruidosas y el rendimiento se degrada.
Group Normalization no depende del tamaño del batch en absoluto. Wu y He demostraron que con batch size = 2, GroupNorm supera a BatchNorm por un amplio margen en ImageNet. Con batch size ≥ 32, BatchNorm suele ser ligeramente superior.
Casos especiales
- G = C → cada grupo tiene 1 canal → equivale a Instance Normalization.
- G = 1 → un solo grupo con todos los canales → equivale a Layer Normalization.
- Valores típicos: G = 32 (por defecto en el paper).
Dato práctico: los modelos de la familia Detectron2 (Mask R-CNN, Faster R-CNN) de Meta AI usan Group Normalization por defecto en los backbones ResNet-FPN para detección y segmentación. También se usa en Segment Anything (SAM).
Instance Normalization
Instance Normalization fue propuesta por Dmitry Ulyanov, Andrea Vedaldi y Victor Lempitsky en 2016 (arXiv:1607.08022), originalmente en el contexto de la transferencia de estilo (style transfer). Normaliza cada canal de cada ejemplo de forma independiente, calculando la media y varianza solo sobre las dimensiones espaciales (H, W).
Fórmula
Cada canal de cada imagen tiene sus propias estadísticas. Esto elimina la información de contraste (el nivel medio de activación de cada canal) preservando la estructura espacial relativa.
¿Cuándo usar Instance Normalization?
- Style transfer y generación de imágenes: InstanceNorm elimina la información de estilo (que está codificada en las estadísticas de los canales) dejando el contenido, lo que es exactamente lo que se necesita en transferencia de estilo.
- GANs para generación de imágenes: muchas arquitecturas de GANs (StyleGAN, Pix2Pix) usan InstanceNorm o variantes.
- Adaptive Instance Normalization (AdaIN): variante donde \gamma y \beta se derivan de otra señal (por ejemplo, el estilo deseado), permitiendo control explícito del estilo. Introducida por Huang y Belongie (2017, arXiv:1703.06868).
Weight Normalization
Weight Normalization fue propuesta por Tim Salimans y Diederik P. Kingma en 2016 (arXiv:1602.07868). A diferencia de las técnicas anteriores, que normalizan las activaciones, Weight Normalization reparametriza los pesos directamente separando su magnitud de su dirección:
Donde g es un escalar aprendible que controla la magnitud y \mathbf{v} es un vector aprendible que controla la dirección. Esta reparametrización desacopla la norma del peso de su dirección, lo que suaviza el paisaje de la loss y mejora el acondicionamiento del problema de optimización.
Ventajas y desventajas
- ✅ No depende del batch (como LayerNorm, GroupNorm).
- ✅ Menor coste computacional que BatchNorm (no calcula estadísticas del batch).
- ✅ Útil en modelos generativos (GANs, VAEs) donde la dependencia del batch es problemática.
- ❌ No incluye normalización de las activaciones, por lo que es menos efectiva que BatchNorm para redes muy profundas.
- ❌ Se usa con menor frecuencia en la práctica actual; ha sido reemplazada por GroupNorm y RMSNorm en la mayoría de contextos.
Spectral Normalization
Spectral Normalization fue propuesta por Takeru Miyato et al. en 2018 (arXiv:1802.05957) específicamente para estabilizar el entrenamiento de GANs. Normaliza cada capa dividiendo la matriz de pesos por su valor singular más grande (norma espectral):
Esto garantiza que la constante de Lipschitz de cada capa sea exactamente 1, lo que estabiliza el gradiente del discriminador y previene el mode collapse. En la práctica, \sigma_{\max} se estima eficientemente con el método de la potencia (power iteration) sin necesidad de una SVD completa.
Uso principal: discriminadores de GANs (SNGAN, BigGAN). También se ha aplicado en Transformers para mejorar la estabilidad del entrenamiento de modelos de difusión y en algunas variantes de ViT.
Tabla comparativa completa
La siguiente tabla resume todas las técnicas de normalización discutidas, con sus características clave, ventajas, desventajas y casos de uso principales:
| Técnica | Normaliza sobre | ¿Dep. batch? | Parámetros extra | Caso de uso principal | Paper |
|---|---|---|---|---|---|
| Batch Norm | [N, H, W] por canal | ✅ Sí | \gamma, \beta + running stats | CNNs, MLPs (batch ≥ 16) | Ioffe & Szegedy, 2015 |
| Layer Norm | [C, H, W] (o features) por ejemplo | ❌ No | \gamma, \beta | Transformers, RNNs, NLP | Ba et al., 2016 |
| RMSNorm | Features por ejemplo (solo RMS) | ❌ No | \gamma (sin \beta) | LLMs modernos (LLaMA, Gemma) | Zhang & Sennrich, 2019 |
| Group Norm | [C/G, H, W] por ejemplo y grupo | ❌ No | \gamma, \beta, G | Detección, segmentación (batch pequeño) | Wu & He, 2018 |
| Instance Norm | [H, W] por ejemplo y canal | ❌ No | \gamma, \beta (opcionales) | Style transfer, GANs | Ulyanov et al., 2016 |
| Weight Norm | Pesos (reparametrización) | ❌ No | g (magnitud) | GANs, modelos generativos | Salimans & Kingma, 2016 |
| Spectral Norm | Pesos (\sigma_{\max}) | ❌ No | Ninguno extra | Discriminadores de GANs | Miyato et al., 2018 |
Pros y contras de cada técnica
| Técnica | Pros | Contras |
|---|---|---|
| Batch Norm | Muy efectiva en CNNs; permite LR altos; efecto regularizador | Depende del batch size; funciona mal con batches pequeños; train≠eval |
| Layer Norm | Sin dependencia del batch; estándar en Transformers; train=eval | Menos efectiva que BN en CNNs; algo más lenta que RMSNorm |
| RMSNorm | Más eficiente que LayerNorm; igual de efectiva; estándar en LLMs | No re-centra (podría importar en algunos casos); menos estudiada históricamente |
| Group Norm | Robusta con cualquier batch size; flexible (G ajustable) | Hiperparámetro extra (G); algo peor que BN con batches grandes |
| Instance Norm | Excelente para style transfer; elimina info de contraste/estilo | No apropiada para clasificación; pierde información de canal |
| Weight Norm | Simple; bajo coste; sin dependencia del batch | Menos potente que BN/LN para redes profundas; rara vez se usa actualmente |
| Spectral Norm | Estabiliza GANs; garantiza Lipschitz; coste bajo | Específica para GANs; no mejora la convergencia general |
¿Cuál elegir? Guía rápida por arquitectura
- CNN con batches grandes (≥ 32): → Batch Normalization
- CNN con batches pequeños (≤ 8): → Group Normalization
- Transformer / modelo de lenguaje: → LayerNorm o RMSNorm
- LLM moderno (LLaMA, Mistral, etc.): → RMSNorm (Pre-LN)
- RNN / LSTM / GRU: → LayerNorm
- Style transfer / generación de estilo: → Instance Normalization
- GAN (discriminador): → Spectral Normalization
- GAN (generador): → Instance Normalization o Batch Normalization
Referencias bibliográficas
- Ioffe, S. & Szegedy, C. (2015). Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. arXiv:1502.03167
- Ba, J. L., Kiros, J. R. & Hinton, G. E. (2016). Layer Normalization. arXiv:1607.06450
- Zhang, B. & Sennrich, R. (2019). Root Mean Square Layer Normalization. arXiv:1910.07467
- Wu, Y. & He, K. (2018). Group Normalization. arXiv:1803.08494
- Ulyanov, D., Vedaldi, A. & Lempitsky, V. (2016). Instance Normalization: The Missing Ingredient for Fast Stylization. arXiv:1607.08022
- Salimans, T. & Kingma, D. P. (2016). Weight Normalization: A Simple Reparameterization to Accelerate Training of Deep Neural Networks. arXiv:1602.07868
- Miyato, T. et al. (2018). Spectral Normalization for Generative Adversarial Networks. arXiv:1802.05957
- Santurkar, S. et al. (2018). How Does Batch Normalization Help Optimization? arXiv:1805.11604
- Xiong, R. et al. (2020). On Layer Normalization in the Transformer Architecture. arXiv:2002.04745
- Huang, X. & Belongie, S. (2017). Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization. arXiv:1703.06868
- Touvron, H. et al. (2023). LLaMA: Open and Efficient Foundation Language Models. arXiv:2302.13971
Resumen de este submódulo — Estabilidad del Entrenamiento:
- La convergencia depende de LR, batch size, optimizador, inicialización
- Los vanishing/exploding gradients son el problema central en redes profundas
- Usar buenas funciones de activación (ReLU), pérdida (Cross-Entropy) e inicialización (He/Xavier) previene muchos problemas
- Las skip connections (ResNets) permiten entrenar redes muy profundas
- Gradient clipping es esencial en RNNs y Transformers
- Batch Normalization estabiliza las activaciones y los gradientes automáticamente
- Usar la normalización adecuada según la arquitectura: BN para CNNs, LayerNorm para Transformers, RMSNorm para LLMs, GroupNorm para batches pequeños