📖 Teoría

Regularización

Qué es el sobreajuste, cómo diagnosticarlo y cómo combatirlo: L1, L2, dropout, data augmentation, early stopping y más. Todas las técnicas para que tus modelos generalicen y funcionen en el mundo real.

🎯 ¿Qué es el sobreajuste?

El sobreajuste (overfitting) es uno de los problemas centrales del aprendizaje automático y, en particular, del deep learning. Se produce cuando un modelo memoriza los datos de entrenamiento —incluyendo su ruido y sus particularidades aleatorias— en lugar de aprender los patrones generales que subyacen a esos datos.

Un modelo sobreajustado obtiene métricas excelentes sobre el conjunto de entrenamiento, pero fracasa al generalizar: ante datos nuevos, nunca vistos, su rendimiento se degrada notablemente. En la práctica, esto significa que el modelo es inútil fuera del laboratorio.

💡

Definición informal: overfitting es cuando tu modelo "se aprende las respuestas del examen" en lugar de "entender la asignatura". Saca un 10 en el examen que ya ha visto, pero suspende cualquier otro.

¿Por qué aparece el sobreajuste?

Hay varias causas fundamentales, y casi siempre actúan en combinación:

  • Exceso de capacidad del modelo: las redes neuronales profundas tienen millones —o miles de millones— de parámetros. Si el modelo tiene más capacidad de la necesaria para capturar la complejidad real del problema, puede usar esa capacidad sobrante para memorizar ruido.
  • Datos insuficientes: cuando el dataset de entrenamiento es pequeño, el modelo no tiene suficientes ejemplos para aprender patrones generales y recurre a memorizar los pocos que tiene.
  • Datos poco representativos: aunque el dataset sea grande, si no cubre bien la distribución real del problema (por ejemplo, tiene sesgos o carece de variedad), el modelo puede aprender patrones espurios que no se trasladan a la realidad.
  • Entrenamiento excesivo: si se entrena durante demasiadas épocas sin control, el modelo termina "exprimiendo" cada detalle del conjunto de entrenamiento, incluido el ruido.
  • Ausencia de regularización: sin ninguna técnica que restrinja la complejidad del modelo (L2, dropout, etc.), no hay nada que impida al optimizador llevar los pesos a valores extremos para encajar perfectamente cada punto de entrenamiento.

¿Cómo se manifiesta visualmente?

La señal más clara de overfitting es la divergencia entre las curvas de loss del entrenamiento y la validación. Durante las primeras épocas ambas descienden; pero en cierto punto, la loss de validación deja de bajar (o empieza a subir) mientras que la de entrenamiento sigue descendiendo.

🧪 Visualización: curvas de entrenamiento vs. validación

7
3
5
Ajusta los controles para observar cómo cambia la brecha entre train y validation loss. Más complejidad + menos datos + más ruido = más overfitting.

⚖️ El tradeoff bias-variance

Para entender el sobreajuste en profundidad, necesitamos comprender el dilema sesgo-varianza (bias-variance tradeoff), uno de los conceptos más fundamentales del aprendizaje automático.

El error de generalización de cualquier modelo se puede descomponer en tres términos:

Descomposición del error E_{\text{total}} = \text{Bias}^2 + \text{Variance} + \text{Ruido irreducible}

Sesgo (Bias)

El sesgo mide cuánto se aleja, en promedio, la predicción del modelo del valor real. Un modelo con alto sesgo es demasiado simple: no captura la complejidad del problema. Por ejemplo, intentar ajustar una relación cuadrática con una recta produce alto sesgo. Esto se conoce como underfitting.

Varianza (Variance)

La varianza mide cuánto cambian las predicciones del modelo si lo entrenamos con un subconjunto ligeramente diferente de datos. Un modelo con alta varianza es muy sensible a los datos específicos de entrenamiento: pequeños cambios en el dataset producen predicciones muy diferentes. Esto es precisamente el overfitting.

Ruido irreducible

Existe un error mínimo que ningún modelo puede eliminar: el ruido inherente a los datos. Datos del mundo real siempre contienen variabilidad aleatoria. Este término nos recuerda que la perfección es inalcanzable; el objetivo es minimizar bias + variance, no el error total.

📐

El dilema: al aumentar la complejidad del modelo, el sesgo baja pero la varianza sube. Al simplificarlo, la varianza baja pero el sesgo sube. El objetivo de toda técnica de regularización es encontrar el punto óptimo entre ambos extremos.

Underfitting vs Overfitting

Underfitting Punto óptimo Overfitting
Sesgo Alto Equilibrado Bajo
Varianza Baja Equilibrada Alta
Train Loss Alta Baja Muy baja
Val Loss Alta Baja Alta
Capacidad Insuficiente Adecuada Excesiva
Síntoma Modelo demasiado simple Generaliza bien Memoriza datos
⚠️

En deep learning, la intuición clásica se matiza. Redes muy sobreparametrizadas (con millones de parámetros más que datos) pueden generalizar bien si se combinan con las técnicas de regularización adecuadas. Este fenómeno, conocido como double descent (Nakkiran et al., 2019 — arXiv:1912.02292), muestra que la relación bias-variance es más compleja en la práctica de lo que sugiere la teoría clásica. En el régimen de sobreparametrización, el error de test puede volver a bajar después de un pico inicial, desafiando la curva en U clásica del tradeoff bias-variance.

🔍 Diagnóstico del sobreajuste

Antes de aplicar cualquier técnica de regularización, es imprescindible detectar y confirmar que el sobreajuste está ocurriendo. Un diagnóstico riguroso requiere una partición adecuada de los datos y la observación atenta de las métricas durante el entrenamiento.

La importancia de tres conjuntos: train, validation, test

Particionar correctamente el dataset es el primer paso para poder diagnosticar problemas de generalización. Cada subconjunto tiene un rol específico e insustituible:

Conjunto Propósito ¿Se usa para entrenar? % típico
Entrenamiento (train) Ajustar los pesos del modelo (backpropagation) 60–80%
Validación (val) Ajustar hiperparámetros, detectar overfitting, early stopping No directamente 10–20%
Test (test) Evaluación final del rendimiento real, solo se usa una vez No 10–20%
⚠️

Error frecuente: usar el conjunto de test para tomar decisiones de diseño (seleccionar hiperparámetros, elegir cuándo parar, etc.). Esto "contamina" el test set y convierte la métrica final en una estimación optimista. El test set debe reservarse exclusivamente para la evaluación final, sin retroalimentación al proceso de desarrollo.

📐

Validación cruzada (cross-validation): cuando el dataset es muy pequeño, una sola partición train/val puede ser poco fiable. Técnicas como k-fold cross-validation dividen los datos en k pliegues, usando cada uno como validación una vez y promediando los resultados. Esto da una estimación más robusta del error de generalización.

Curvas de aprendizaje: tu mejor herramienta

Las curvas de aprendizaje (learning curves) representan la evolución del loss y/o las métricas de rendimiento en función del número de épocas o del tamaño del dataset. Son la herramienta más directa para diagnosticar overfitting y underfitting.

Patrones típicos en las curvas

Buen ajuste (good fit): ambas curvas (train y val) descienden y convergen a valores similares y bajos. La brecha entre ellas es pequeña. El modelo generaliza bien.

📈

Sobreajuste (overfitting): la train loss sigue bajando, pero la val loss se estanca o sube. La brecha entre ambas curvas se agranda progresivamente. El modelo memoriza.

📉

Subajuste (underfitting): ambas curvas se estancan en valores altos. El modelo no tiene suficiente capacidad para capturar los patrones, o el learning rate es inadecuado.

Más allá de las curvas: señales complementarias

  • Brecha train/val accuracy: si la accuracy de entrenamiento es del 99% pero la de validación está en el 75%, hay overfitting claro.
  • Norma de los pesos: pesos con magnitudes muy grandes suelen indicar que el modelo se está ajustando a detalles mínimos del dataset. Monitorizar \|\mathbf{w}\|_2 puede dar señales tempranas. Para entender cómo las magnitudes de los pesos afectan a la estabilidad del entrenamiento, consulta Estabilidad del Entrenamiento.
  • Gradientes: gradientes que se mantienen grandes después de muchas épocas pueden indicar que el modelo sigue "forzando" su ajuste a los datos de entrenamiento.
  • Inspección visual: en problemas como clasificación de imágenes, visualizar las predicciones erróneas puede revelar si el modelo ha memorizado patrones espurios (artefactos, fondos, etc.).
  • Confusion matrix: examinar la matriz de confusión sobre el conjunto de validación puede mostrar si ciertas clases están siendo sobreajustadas mientras otras se ignoran.

¿Cuándo actuar?

La regla general es clara: si observas una brecha creciente entre train loss y val loss, necesitas regularizar. Pero es importante no reaccionar de manera prematura; una brecha pequeña y estable puede ser aceptable. Lo preocupante es cuando la brecha crece conforme avanzan las épocas.

🗝️

Protocolo de diagnóstico recomendado:

  1. Entrena un modelo sin regularización y observa las curvas de loss.
  2. Si hay underfitting → aumenta la capacidad o ajusta el learning rate.
  3. Si hay overfitting → aplica técnicas de regularización (las veremos a continuación).
  4. Si hay buen ajuste pero rendimiento insuficiente → revisa los datos y la representatividad.

🏗️ Antes de regularizar: ¿es el modelo adecuado?

Cuando detectamos sobreajuste, el primer impulso suele ser añadir dropout o L2. Pero antes de recurrir a técnicas de regularización, hay una pregunta más fundamental: ¿estamos usando el modelo correcto?

🗝️

Principio de parsimonia (Navaja de Occam): entre dos modelos que expliquen igualmente bien los datos, prefiere el más simple. Un modelo más simple tiene menos oportunidades de memorizar ruido y, en general, generaliza mejor.

Simplificación de la arquitectura

La forma más directa de reducir el overfitting es reducir la capacidad del modelo. En redes neuronales, la capacidad depende fundamentalmente del número de parámetros:

  • Reducir el número de capas: una red con 50 capas puede ser innecesaria si una de 10 capas captura la misma información. Empieza con una arquitectura sencilla y escala solo si el underfitting lo requiere.
  • Reducir la anchura de las capas: pasar de 512 neuronas por capa a 128 o 64 reduce drásticamente los parámetros sin necesariamente perder poder expresivo.
  • Usar arquitecturas especializadas: no toda tarea requiere una red totalmente conectada. Las CNN explotan la estructura espacial de las imágenes; las RNN/Transformers aprovechan la secuencialidad del texto. Usar la inductive bias correcta reduce la necesidad de regularización.
  • Compartir parámetros: técnicas como las convoluciones (mismos filtros en toda la imagen) o los mecanismos de atención con pesos compartidos reducen el número efectivo de parámetros libres.
📐

¿Cuántos parámetros son "demasiados"? Una regla orientativa (no estricta): si el número de parámetros entrenables es muy superior al número de ejemplos de entrenamiento, el riesgo de overfitting es alto. Pero en deep learning moderno, modelos con billones de parámetros generalizan bien gracias a regularización implícita y grandes datasets.

Selección de la arquitectura apropiada

Elegir la familia de modelos correcta para el problema es a menudo más importante que la regularización:

Tipo de dato Arquitectura recomendada ¿Por qué?
Imágenes CNN (ResNet, EfficientNet, etc.) Explotan invarianza traslacional y estructura espacial
Texto / secuencias Transformers (BERT, GPT, etc.) Capturan dependencias de largo alcance con atención
Series temporales LSTM/GRU o Transformers temporales Modelan dependencias temporales y estacionalidad
Datos tabulares Gradient Boosting (XGBoost), MLP simple Datos tabulares no se benefician tanto de redes profundas
Grafos GNN (Graph Neural Networks) Respetan la topología y las relaciones entre nodos

📊 El papel de los datos

Antes de sofisticar el modelo o añadir regularización, pregúntate: ¿tengo los datos adecuados? En muchos casos, mejorar el dataset es más efectivo que cualquier truco de regularización.

Tamaño del dataset

La cantidad de datos tiene un impacto directo en la capacidad de generalización:

  • Más datos = más difícil de memorizar: con millones de ejemplos, el modelo se ve "obligado" a encontrar patrones generales porque no puede memorizar cada ejemplo individual.
  • Ratio datos/parámetros: cuanto mayor sea la proporción de ejemplos de entrenamiento respecto al número de parámetros, menor será el riesgo de overfitting.
  • Si no puedes conseguir más datos: técnicas como data augmentation (que veremos más adelante), transfer learning (aprovechar modelos preentrenados en datasets grandes) y few-shot learning pueden mitigar la escasez de datos.

Variedad y representatividad

No basta con tener muchos datos; deben ser variados y representativos de la distribución real del problema:

  • Diversidad de condiciones: en clasificación de imágenes, ¿incluyes fotos con distintas iluminaciones, ángulos, fondos, resoluciones? Si el modelo solo ve fotos de estudio, fallará en condiciones reales.
  • Balance de clases: datasets desbalanceados (por ejemplo, 95% clase A y 5% clase B) pueden llevar al modelo a "ignorar" la clase minoritaria. Técnicas como oversampling, undersampling o ajuste de pesos de loss pueden ayudar.
  • Ausencia de sesgos: si los datos contienen sesgos sistemáticos (por ejemplo, todas las fotos de gatos son de interior y las de perros son de exterior), el modelo puede aprender atajos espurios en lugar de los patrones relevantes.
  • Calidad del etiquetado: etiquetas incorrectas o inconsistentes introducen ruido que el modelo puede memorizar. La limpieza de datos es una forma de "regularización" implícita.
💡

Regla práctica: antes de añadir una capa de regularización, hazte estas preguntas:

  1. ¿Puedo simplificar el modelo sin perder expresividad?
  2. ¿Puedo conseguir más datos o mejorar su calidad?
  3. ¿Estoy usando la arquitectura adecuada para mi problema?

Solo después de responder "no" a todas, recurre a las técnicas de regularización explícita.

📏 Regularización L1 y L2

Las regularizaciones L1 y L2 son las técnicas más clásicas y fundamentales. Funcionan añadiendo un término de penalización a la función de loss que castiga los pesos grandes, obligando al modelo a mantener pesos pequeños y, por tanto, a producir funciones más suaves y menos propensas al overfitting.

La idea tiene raíces profundas en la estadística: la regularización L2 (Ridge regression) fue propuesta por Hoerl y Kennard en 1970, y la L1 (Lasso) fue formalizada por Robert Tibshirani en 1996. En el contexto de redes neuronales, ambas se interpretan desde una perspectiva bayesiana: L2 equivale a asumir un prior gaussiano centrado en cero sobre los pesos (p(\mathbf{w}) \propto e^{-\lambda\|\mathbf{w}\|_2^2}), mientras que L1 equivale a un prior laplaciano (p(\mathbf{w}) \propto e^{-\lambda\|\mathbf{w}\|_1}). Maximizar la probabilidad posterior con estos priors es exactamente minimizar la loss regularizada.

La idea general

En lugar de minimizar únicamente la función de pérdida original \mathcal{L}_{\text{data}}, minimizamos una versión aumentada:

Loss regularizada \mathcal{L}_{\text{total}} = \mathcal{L}_{\text{data}} + \lambda \cdot \Omega(\mathbf{w})

Donde \lambda es el coeficiente de regularización (un hiperparámetro que controla la fuerza de la penalización) y \Omega(\mathbf{w}) es el término de penalización que depende de la norma de los pesos.

Regularización L2 (Ridge / Weight Decay)

La regularización L2 penaliza la suma de los cuadrados de los pesos:

Penalización L2 \Omega_{L2}(\mathbf{w}) = \frac{1}{2}\sum_{i} w_i^2 = \frac{1}{2}\|\mathbf{w}\|_2^2

La loss total queda:

Loss + L2 \mathcal{L}_{\text{total}} = \mathcal{L}_{\text{data}} + \frac{\lambda}{2}\sum_{i} w_i^2

Efecto sobre los gradientes: al calcular el gradiente respecto a cada peso w_i, el término L2 añade \lambda w_i. Esto significa que en cada paso de actualización, los pesos se "encogen" proporcionalmente a su valor:

Actualización con L2 w_i \leftarrow w_i - \eta\left(\frac{\partial \mathcal{L}_{\text{data}}}{\partial w_i} + \lambda w_i\right) = (1 - \eta\lambda)w_i - \eta\frac{\partial \mathcal{L}_{\text{data}}}{\partial w_i}

Por eso también se llama weight decay: en cada paso, los pesos se multiplican por (1 - \eta\lambda), un factor ligeramente menor que 1.

📐

Intuición geométrica: L2 restringe los pesos a una "bola" centrada en el origen. Cuanto mayor es \lambda, más pequeña es la bola. Los pesos se distribuyen de manera uniforme: ninguno domina excesivamente. Esto produce modelos con decisiones más suaves.

⚠️

Weight decay ≠ L2 en Adam/AdamW: en SGD, L2 y weight decay son equivalentes. Pero en optimizadores adaptativos como Adam, no lo son. AdamW implementa weight decay "desacoplado" (decoupled weight decay), que es más correcto y funciona mejor en la práctica (Loshchilov & Hutter, 2019 — arXiv:1711.05101). Si usas Adam, usa AdamW con weight decay, no Adam con L2. Para más detalles sobre optimizadores y weight decay, consulta Schedulers y Optimizers.

Regularización L1 (Lasso)

La regularización L1 penaliza la suma de los valores absolutos de los pesos:

Penalización L1 \Omega_{L1}(\mathbf{w}) = \sum_{i} |w_i| = \|\mathbf{w}\|_1
Loss + L1 \mathcal{L}_{\text{total}} = \mathcal{L}_{\text{data}} + \lambda \sum_{i} |w_i|

Efecto clave: sparsity. A diferencia de L2, que encoge todos los pesos uniformemente, L1 tiende a llevar muchos pesos exactamente a cero. Esto produce modelos dispersos (sparse), donde solo un subconjunto de parámetros está activo.

¿Por qué ocurre esto? El gradiente de |w_i| es \text{sign}(w_i), que siempre tiene magnitud 1 independientemente del valor del peso. Incluso pesos muy pequeños reciben un "empujón" constante hacia cero, lo que eventualmente los elimina.

💡

Intuición geométrica: L1 restringe los pesos a un "diamante" (rombo). Las esquinas del diamante están alineadas con los ejes, lo que hace que la solución óptima caiga frecuentemente sobre un eje, es decir, con algún peso exactamente a cero.

L1 vs L2: comparación directa

Aspecto L1 (Lasso) L2 (Ridge)
Penalización \sum|w_i| \sum w_i^2
Efecto sobre pesos Lleva muchos pesos a cero (sparse) Encoge todos los pesos uniformemente
Selección de features Sí (elimina features irrelevantes) No (mantiene todos las features)
Robustez ante multicolinealidad Selecciona una feature del grupo Distribuye pesos entre features correladas
Uso en deep learning Menos común; útil para pruning Muy común (weight decay estándar)
Forma geométrica Diamante / rombo Esfera / bola

Elastic Net: lo mejor de ambos mundos

Elastic Net combina las penalizaciones L1 y L2 con un parámetro \alpha \in [0, 1] que controla la proporción entre ambas:

Elastic Net \Omega_{\text{EN}}(\mathbf{w}) = \alpha \|\mathbf{w}\|_1 + \frac{(1-\alpha)}{2}\|\mathbf{w}\|_2^2

Elastic Net hereda la capacidad de L1 para producir modelos sparse, pero la componente L2 estabiliza la selección de variables cuando hay features altamente correladas.

🧪 Explorador: efecto de L1 vs L2 sobre los pesos

0.30
Observa cómo L2 encoge todos los pesos proporcionalmente, mientras que L1 los lleva a cero selectivamente.

Valores típicos de λ

El coeficiente de regularización \lambda es un hiperparámetro crucial. Valores típicos de weight decay en deep learning:

  • CNNs (ResNet, etc.): \lambda = 10^{-4} (0.0001) es un valor estándar.
  • Transformers (BERT, GPT): \lambda = 0.01 con AdamW es habitual.
  • Redes pequeñas / MLPs: \lambda \in [10^{-5}, 10^{-3}], ajustar con validación.

En la práctica: L2/weight decay es la regularización "por defecto" en deep learning. Prácticamente todos los frameworks la incluyen como parámetro del optimizador (weight_decay en PyTorch, kernel_regularizer en Keras). Empieza con \lambda = 10^{-4} y ajusta desde ahí.

🎲 Dropout

Dropout (Srivastava et al., 2014 — JMLR, Vol. 15) es, junto con weight decay, la técnica de regularización más utilizada en deep learning. Su idea es sorprendentemente sencilla: durante cada paso de entrenamiento, se desactivan aleatoriamente una fracción de las neuronas.

¿Cómo funciona?

En cada forward pass durante el entrenamiento, cada neurona de la capa con dropout se "apaga" con probabilidad p (típicamente 0.2–0.5). Esto equivale a multiplicar la salida de cada neurona por una máscara binaria aleatoria:

Dropout \mathbf{h}' = \mathbf{m} \odot \mathbf{h}, \quad m_i \sim \text{Bernoulli}(1-p)

Donde \mathbf{m} es la máscara binaria (1 = activa, 0 = apagada) y \odot es el producto elemento a elemento.

¿Por qué funciona?

  • Previene la co-adaptación: las neuronas no pueden depender de que otras neuronas específicas estén presentes. Cada neurona debe aprender a ser útil por sí misma, lo que produce representaciones más robustas.
  • Ensamble implícito: dropout puede verse como entrenar un ensamble exponencial de sub-redes. Con n neuronas, existen 2^n posibles sub-redes. En la inferencia, se usa la "media" de todas ellas (aproximada por el escalado).
  • Inyección de ruido: añadir aleatoriedad al entrenamiento actúa como una forma de regularización que dificulta la memorización.

Inverted Dropout (implementación estándar)

En la práctica, se usa inverted dropout: durante el entrenamiento, las activaciones supervivientes se escalan por \frac{1}{1-p} para mantener la esperanza de la salida igual. De esta forma, en inferencia no se necesita hacer ningún ajuste:

Inverted Dropout \mathbf{h}' = \frac{1}{1-p}\mathbf{m} \odot \mathbf{h} \quad \text{(entrenamiento)}, \qquad \mathbf{h}' = \mathbf{h} \quad \text{(inferencia)}
⚠️

¡Recuerda desactivar dropout en inferencia! En PyTorch, esto se hace con model.eval(). En Keras/TF, las capas Dropout se desactivan automáticamente cuando training=False. Olvidar esto produce predicciones ruidosas e inconsistentes.

Valores típicos de p

Tipo de capa p (tasa de dropout) Notas
Capas densas (fully connected) 0.3–0.5 Valor estándar; 0.5 en el paper original
Capas convolucionales 0.1–0.3 o no usar Las CNN tienen menos parámetros por capa; se prefiere Spatial Dropout
Capas recurrentes (LSTM/GRU) 0.2–0.5 Dropout solo en conexiones no recurrentes (variational dropout para las recurrentes)
Transformers 0.1 Dropout en atención y feed-forward; p=0.1 es el estándar (BERT, GPT)
Cerca de la salida 0.5 Capas finales suelen tolerar más dropout

Variantes de Dropout

  • Spatial Dropout (Dropout2D): en lugar de apagar neuronas individuales, apaga canales enteros en una CNN. Más efectivo que el dropout estándar para datos espaciales.
  • DropConnect: en lugar de apagar neuronas, apaga conexiones (pesos) individuales. Más fino pero más costoso computacionalmente.
  • DropBlock: apaga regiones contiguas del feature map. Más efectivo en CNNs porque el dropout estándar deja información residual en píxeles vecinos.
  • Variational Dropout: usa la misma máscara para todos los timesteps en una secuencia RNN, lo que permite dropout efectivo en conexiones recurrentes.
  • Monte Carlo Dropout: mantiene dropout activo en inferencia y hace múltiples forward passes para estimar la incertidumbre de las predicciones. Útil para cuantificar la confianza del modelo.

🧪 Visualización: efecto de dropout en una red neuronal

6
0.30
Configura el número de capas ocultas y de neuronas por capa. Cada clic en «Nueva máscara» genera una máscara de dropout aleatoria distinta. Las neuronas atenuadas están desactivadas en ese paso de entrenamiento.

⏹️ Early Stopping

Early stopping consiste en detener el entrenamiento cuando la métrica de validación deja de mejorar, en lugar de esperar a que se completen todas las épocas programadas.

Es una de las técnicas de regularización más simples y efectivas. No modifica el modelo ni la función de pérdida; simplemente elige el punto óptimo en la trayectoria de entrenamiento.

¿Cómo se implementa?

  • Se monitoriza la validation loss (o validation accuracy) al final de cada época.
  • Se define un parámetro de paciencia (patience): cuántas épocas sin mejora se toleran.
  • Opcionalmente, se define un min_delta: la mejora mínima para considerar que ha habido progreso.
  • Se guarda el mejor modelo (model checkpoint) y se restaura al final.

¿Por qué funciona? El entrenamiento de una red neuronal pasa por dos fases: primero, aprende los patrones generales (baja tanto train como val loss); después, empieza a memorizar ruido (sigue bajando train loss pero val loss sube). Early stopping corta en la transición entre ambas fases.

📐

Conexión con L2: se ha demostrado matemáticamente que early stopping con SGD tiene un efecto equivalente a la regularización L2. Detener el entrenamiento antes de convergencia implícitamente restringe la norma de los pesos, ya que los pesos no han tenido tiempo de crecer.

Valores típicos de paciencia

  • Redes convolucionales: 5–15 épocas de paciencia.
  • Transformers / modelos grandes: 3–5 épocas (el entrenamiento es caro).
  • MLPs simples: 10–20 épocas.

Early stopping es una técnica especialmente atractiva porque no introduce hiperparámetros adicionales al modelo (a diferencia de L2 o dropout, que requieren ajustar \lambda o p). El único parámetro relevante es la paciencia, que no es crítico: un valor ligeramente subóptimo solo significa parar un poco antes o un poco después del óptimo, lo que rara vez tiene un impacto significativo. En la práctica, early stopping se combina siempre con model checkpointing: se guarda el estado del modelo cada vez que mejora la métrica de validación, y al final del entrenamiento se restaura el mejor modelo guardado.

📊 Batch Normalization como regularizador

Aunque Batch Normalization (Ioffe & Szegedy, 2015 — arXiv:1502.03167) se diseñó originalmente para acelerar el entrenamiento y combatir el problema del internal covariate shift, tiene un efecto regularizador secundario notable.

Batch Normalization \hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}}, \qquad y_i = \gamma\hat{x}_i + \beta

¿Por qué regulariza?

  • Ruido estadístico: como la media \mu_B y la varianza \sigma_B^2 se calculan sobre el mini-batch (no sobre todo el dataset), introducen ruido estocástico similar al de dropout.
  • Suavizado de la superficie de loss: al normalizar las activaciones, batch norm hace que la superficie de pérdida sea más lisa, lo que dificulta la memorización de patrones ruidosos.
💡

En la práctica: si usas Batch Normalization, puedes reducir o incluso eliminar el dropout en ciertas capas. Muchas arquitecturas modernas (ResNet, EfficientNet) usan batch norm como principal fuente de regularización, junto con weight decay.

🧰 Otras técnicas de regularización

Además de L1, L2, dropout, early stopping y batch norm, existen numerosas técnicas de regularización que conviene conocer. Muchas de ellas se combinan en la práctica.

Label Smoothing

En clasificación, las etiquetas one-hot son "extremas": la probabilidad objetivo para la clase correcta es 1 y 0 para todas las demás. Label smoothing (Szegedy et al., 2016) suaviza estas etiquetas:

Label Smoothing y_i^{\text{smooth}} = (1 - \epsilon)y_i + \frac{\epsilon}{K}

Donde \epsilon es el factor de smoothing (típicamente 0.1) y K es el número de clases. Esto impide que el modelo se vuelva "demasiado confiado" y produce logits mejor calibrados.

Inyección de ruido (Noise Injection)

Añadir ruido gaussiano a distintos puntos de la red es una técnica de regularización sencilla pero efectiva:

  • Ruido en las entradas: equivale a una forma de data augmentation; suaviza la función aprendida.
  • Ruido en los pesos: actúa de forma similar a la regularización Bayesiana; penaliza soluciones sensibles a pequeñas perturbaciones.
  • Ruido en los gradientes: ayuda a escapar de mínimos locales agudos (que tienden a ser soluciones sobreajustadas).

Max-Norm Constraint

Max-norm limita la norma del vector de pesos de cada neurona a un valor máximo c. Después de cada actualización de gradiente, si \|\mathbf{w}\|_2 > c, se proyecta:

Max-Norm \mathbf{w} \leftarrow \mathbf{w} \cdot \frac{c}{\max(c, \|\mathbf{w}\|_2)}

Esto actúa como una "cerca" que impide que los pesos crezcan sin límite, complementando especialmente bien al dropout (que puede causar pesos grandes al compensar neuronas apagadas).

Mixup y CutMix

Mixup (Zhang et al., 2018 — arXiv:1710.09412) crea ejemplos de entrenamiento "virtuales" mezclando linealmente pares de datos y sus etiquetas:

Mixup \tilde{x} = \lambda x_i + (1-\lambda)x_j, \qquad \tilde{y} = \lambda y_i + (1-\lambda)y_j, \qquad \lambda \sim \text{Beta}(\alpha, \alpha)

CutMix es una variante que, en lugar de mezclar toda la imagen, recorta una región rectangular de una imagen y la pega sobre otra. Ambas técnicas obligan al modelo a aprender representaciones más suaves y producen mejoras significativas en clasificación de imágenes.

Weight Tying (compartir pesos)

Weight tying consiste en forzar a varias capas o componentes a compartir los mismos pesos. Es una forma estructural de regularización que reduce el número efectivo de parámetros libres. Ejemplo clásico: en modelos de lenguaje, la capa de embedding de entrada y la capa de predicción de salida comparten la misma matriz de pesos.

Stochastic Depth

Propuesta por Huang et al. (2016 — arXiv:1603.09382) para redes residuales muy profundas: durante el entrenamiento, bloques residuales completos se "saltan" aleatoriamente (se pasa directamente la conexión residual). Esto actúa como un dropout a nivel de capas enteras y, además, acelera el entrenamiento.

Resumen de técnicas

Técnica ¿Dónde actúa? Efecto principal Hiperparámetro clave
L2 / Weight Decay Pesos Encoge pesos uniformemente λ (wd)
L1 Pesos Sparsity (elimina pesos) λ
Dropout Activaciones Previene co-adaptación p (drop rate)
Early Stopping Proceso de entrenamiento Detiene antes de memorizar patience
Batch Norm Activaciones (normalización) Ruido estadístico + suavizado batch size
Label Smoothing Etiquetas Calibra confianza ε (smoothing factor)
Max-Norm Pesos (post-update) Limita magnitud de pesos c (max norm)
Mixup / CutMix Datos de entrada Suaviza frontera de decisión α (Beta distribution)
Stochastic Depth Bloques residuales Dropout a nivel de capa p (survival prob)
Noise Injection Entradas / pesos / gradientes Robustez ante perturbaciones σ (noise std)
Data Augmentation Datos de entrada Amplía y diversifica el dataset Transformaciones específicas

🔄 Data Augmentation

Data augmentation consiste en aplicar transformaciones a los datos de entrenamiento para generar versiones modificadas de cada ejemplo. Aunque su objetivo principal es "multiplicar" el tamaño efectivo del dataset, tiene un potente efecto regularizador: el modelo nunca ve exactamente el mismo ejemplo dos veces, lo que dificulta la memorización.

La eficacia del data augmentation como regularizador ha sido demostrada tanto empíricamente como teóricamente. En el plano empírico, los resultados de Shorten & Khoshgoftaar (2019) proporcionan un survey exhaustivo de técnicas; en el plano teórico, Chen et al. (2020) demuestran que, bajo ciertas condiciones, el data augmentation induce una regularización implícita equivalente a añadir un término de penalización a la loss.

💡

¿Por qué es regularización? Data augmentation obliga al modelo a ser invariante a las transformaciones aplicadas. Si rotamos las imágenes, el modelo aprende que la orientación no importa. Si añadimos sinónimos al texto, aprende que la forma exacta de las palabras es menos importante que su significado. Esta invariancia forzada reduce la capacidad del modelo para memorizar detalles espurios.

Augmentation en imágenes

El dominio donde más se ha explorado el data augmentation es la visión por computador. Las transformaciones más utilizadas son:

Transformaciones geométricas

  • Rotación: girar la imagen un ángulo aleatorio (típicamente ±15°–30°).
  • Flip horizontal/vertical: voltear la imagen. El flip horizontal es casi universal; el vertical depende del dominio.
  • Escala (zoom): acercar o alejar aleatoriamente.
  • Traslación: desplazar la imagen algunos píxeles horizontal o verticalmente.
  • Shearing: deformar la imagen en diagonal.
  • Crop aleatorio: recortar una subregión aleatoria y redimensionarla al tamaño original. Una de las más efectivas.

Transformaciones de color/intensidad

  • Brillo, contraste, saturación: modificar aleatoriamente las propiedades cromáticas.
  • Jitter de color: pequeñas perturbaciones aleatorias en los canales RGB.
  • Normalización de canal: ajustar la distribución de cada canal.
  • Conversión a escala de grises: con cierta probabilidad, eliminar información de color.

Transformaciones avanzadas

  • CutOut / Random Erasing: ocultar una región rectangular aleatoria de la imagen, obligando al modelo a no depender de una única zona.
  • CutMix: reemplazar una región rectangular con un parche de otra imagen y mezclar las etiquetas proporcionalmente al área.
  • Mixup: mezcla lineal de dos imágenes y sus etiquetas (vista en la sección anterior).
  • AutoAugment / RandAugment: políticas de augmentation aprendidas automáticamente o generadas aleatoriamente (Cubuk et al., 2020). Simplifican la elección de transformaciones a un solo hiperparámetro (magnitud o número de operaciones).
  • TrivialAugment: aplica una única transformación aleatoria con magnitud aleatoria (Müller & Hutter, 2021). Sorprendentemente efectivo y sin hiperparámetros que ajustar.
📐

Regla de oro: las transformaciones de augmentation deben ser semánticamente invariantes. Es decir, la etiqueta no debe cambiar tras la transformación. Rotar un "6" puede convertirlo en un "9" — en ese caso, la rotación no es una transformación válida para ese problema.

Augmentation en texto (NLP)

En procesamiento de lenguaje natural, las opciones son diferentes pero igualmente valiosas:

  • Sustitución de sinónimos: reemplazar palabras por sinónimos del mismo significado.
  • Inserción aleatoria: insertar sinónimos de palabras existentes en posiciones aleatorias.
  • Eliminación aleatoria: borrar palabras del texto con cierta probabilidad.
  • Intercambio de orden: permutar pares de palabras adyacentes.
  • Back-translation: traducir el texto a otro idioma y volver a traducirlo; genera paráfrasis naturales.
  • Paráfrasis con LLMs: usar modelos de lenguaje grandes para generar versiones equivalentes del texto.
  • Noise injection textual: introducir errores tipográficos, mayúsculas aleatorias, etc.

Augmentation en datos tabulares

El data augmentation en datos tabulares es menos intuitivo pero igualmente útil:

  • SMOTE (Synthetic Minority Oversampling): genera ejemplos sintéticos de la clase minoritaria interpolando entre ejemplos existentes.
  • Ruido gaussiano: añadir ruido a las features numéricas.
  • Feature dropout: poner a cero (o al valor medio) features aleatorias, simulando datos incompletos.
  • Generación con modelos generativos: usar VAEs o GANs para generar datos tabulares sintéticos realistas.

Augmentation en series temporales

  • Jittering: añadir ruido gaussiano a los valores.
  • Scaling: multiplicar la serie por un factor aleatorio.
  • Time warping: distorsionar el eje temporal (acelerar/ralentizar segmentos).
  • Window slicing: extraer subsecuencias de distintas longitudes.
  • Permutation: dividir la serie en segmentos y reordenarlos.

Impacto práctico: data augmentation suele ser la técnica de regularización con mayor impacto en problemas de visión por computador. Es habitual ver mejoras de 2–5 puntos porcentuales en accuracy simplemente añadiendo un pipeline de augmentation bien diseñado. Combinada con transfer learning, es especialmente poderosa en datasets pequeños.

💻 Ejemplo práctico: combinando regularización

En la práctica, rara vez se usa una sola técnica de regularización. Lo habitual es combinar varias de ellas. El "combo" más clásico y efectivo en deep learning es:

  • Weight Decay (L2) en el optimizador
  • Dropout en las capas del modelo
  • Data Augmentation en el pipeline de datos
  • Early Stopping para detener el entrenamiento

A continuación, un ejemplo simplificado pero completo de clasificación de imágenes que combina las tres técnicas principales, implementado tanto en TensorFlow/Keras como en PyTorch.

📐

Escenario: clasificación de imágenes CIFAR-10 (10 clases, imágenes 32×32 RGB) con una CNN sencilla. Aplicamos random crop, random flip, dropout (p=0.3), weight decay (λ=1e-4) y early stopping (patience=5).

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

# ── 1. Data Augmentation ──────────────────────────────
data_augmentation = keras.Sequential([
    layers.RandomFlip("horizontal"),
    layers.RandomRotation(0.1),
    layers.RandomZoom(0.1),
    layers.RandomTranslation(0.1, 0.1),
], name="data_augmentation")

# ── 2. Modelo con Dropout y L2 ────────────────────────
def crear_modelo(weight_decay=1e-4, dropout_rate=0.3):
    inputs = keras.Input(shape=(32, 32, 3))
    x = data_augmentation(inputs)  # augmentation solo en train

    # Bloque convolucional 1
    x = layers.Conv2D(32, 3, padding="same",
                      kernel_regularizer=keras.regularizers.l2(weight_decay))(x)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU()(x)
    x = layers.MaxPooling2D()(x)

    # Bloque convolucional 2
    x = layers.Conv2D(64, 3, padding="same",
                      kernel_regularizer=keras.regularizers.l2(weight_decay))(x)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU()(x)
    x = layers.MaxPooling2D()(x)

    # Bloque convolucional 3
    x = layers.Conv2D(128, 3, padding="same",
                      kernel_regularizer=keras.regularizers.l2(weight_decay))(x)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU()(x)
    x = layers.GlobalAveragePooling2D()(x)

    # Clasificador con Dropout
    x = layers.Dropout(dropout_rate)(x)  # ← Dropout
    x = layers.Dense(128, activation="relu",
                     kernel_regularizer=keras.regularizers.l2(weight_decay))(x)
    x = layers.Dropout(dropout_rate)(x)  # ← Dropout
    outputs = layers.Dense(10, activation="softmax")(x)

    return keras.Model(inputs, outputs)

# ── 3. Datos ──────────────────────────────────────────
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype("float32") / 255.0
x_test  = x_test.astype("float32") / 255.0

# ── 4. Compilar y entrenar ────────────────────────────
model = crear_modelo(weight_decay=1e-4, dropout_rate=0.3)

model.compile(
    optimizer=keras.optimizers.AdamW(
        learning_rate=1e-3,
        weight_decay=1e-4  # ← Weight Decay (L2)
    ),
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"],
)

# ── 5. Early Stopping ─────────────────────────────────
callbacks = [
    keras.callbacks.EarlyStopping(
        monitor="val_loss",
        patience=5,
        restore_best_weights=True,
        verbose=1,
    ),
]

history = model.fit(
    x_train, y_train,
    epochs=50,
    batch_size=64,
    validation_split=0.15,
    callbacks=callbacks,
)

# ── 6. Evaluación final ───────────────────────────────
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc:.4f}")
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader, random_split

# ── 1. Data Augmentation ──────────────────────────────
train_transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(32, padding=4),  # ← Random Crop
    transforms.RandomRotation(10),
    transforms.ColorJitter(
        brightness=0.1, contrast=0.1,
        saturation=0.1
    ),
    transforms.ToTensor(),
    transforms.Normalize(
        (0.4914, 0.4822, 0.4465),
        (0.2470, 0.2435, 0.2616)
    ),
])

test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(
        (0.4914, 0.4822, 0.4465),
        (0.2470, 0.2435, 0.2616)
    ),
])

# ── 2. Modelo con Dropout y BatchNorm ─────────────────
class CNN(nn.Module):
    def __init__(self, dropout_rate=0.3):
        super().__init__()
        self.features = nn.Sequential(
            # Bloque 1
            nn.Conv2d(3, 32, 3, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2),
            # Bloque 2
            nn.Conv2d(32, 64, 3, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2),
            # Bloque 3
            nn.Conv2d(64, 128, 3, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.AdaptiveAvgPool2d(1),
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Dropout(dropout_rate),  # ← Dropout
            nn.Linear(128, 128),
            nn.ReLU(inplace=True),
            nn.Dropout(dropout_rate),  # ← Dropout
            nn.Linear(128, 10),
        )

    def forward(self, x):
        return self.classifier(self.features(x))

# ── 3. Datos ──────────────────────────────────────────
full_train = datasets.CIFAR10("./data", train=True,
                               download=True, transform=train_transform)
n_val = int(0.15 * len(full_train))
train_set, val_set = random_split(full_train,
                                   [len(full_train) - n_val, n_val])
test_set = datasets.CIFAR10("./data", train=False,
                             transform=test_transform)

train_loader = DataLoader(train_set, batch_size=64, shuffle=True)
val_loader   = DataLoader(val_set, batch_size=64)
test_loader  = DataLoader(test_set, batch_size=64)

# ── 4. Optimizador con Weight Decay ───────────────────
model = CNN(dropout_rate=0.3)
optimizer = optim.AdamW(
    model.parameters(),
    lr=1e-3,
    weight_decay=1e-4,  # ← Weight Decay (L2 desacoplado)
)
criterion = nn.CrossEntropyLoss()

# ── 5. Entrenamiento con Early Stopping ───────────────
best_val_loss = float("inf")
patience, patience_counter = 5, 0
best_state = None

for epoch in range(50):
    # ── Train ──
    model.train()  # activa Dropout y BatchNorm en modo train
    for xb, yb in train_loader:
        loss = criterion(model(xb), yb)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    # ── Validation ──
    model.eval()   # desactiva Dropout y BatchNorm en modo eval
    val_loss, correct, total = 0, 0, 0
    with torch.no_grad():
        for xb, yb in val_loader:
            out = model(xb)
            val_loss += criterion(out, yb).item() * xb.size(0)
            correct += (out.argmax(1) == yb).sum().item()
            total += xb.size(0)
    val_loss /= total

    # ── Early Stopping check ──
    if val_loss < best_val_loss:
        best_val_loss = val_loss
        patience_counter = 0
        best_state = model.state_dict().copy()
    else:
        patience_counter += 1
        if patience_counter >= patience:
            print(f"Early stopping en época {epoch+1}")
            break

# ── 6. Restaurar mejor modelo y evaluar ───────────────
model.load_state_dict(best_state)
model.eval()
correct, total = 0, 0
with torch.no_grad():
    for xb, yb in test_loader:
        correct += (model(xb).argmax(1) == yb).sum().item()
        total += xb.size(0)
print(f"Test accuracy: {correct/total:.4f}")

Resumen de la combinación:

  • Data Augmentation → amplía y diversifica los datos; el modelo ve variaciones en cada época.
  • Weight Decay (L2) → mantiene los pesos pequeños; previene soluciones extremas.
  • Dropout → previene co-adaptación de neuronas; ensamble implícito.
  • Batch Normalization → estabiliza activaciones + efecto regularizador adicional.
  • Early Stopping → red de seguridad final que detiene antes de memorizar.

Esta combinación es el punto de partida estándar en la mayoría de proyectos de deep learning. A partir de aquí, puedes ajustar intensidades y añadir o quitar técnicas según el problema.