Phase 03 - Lesson 07
Regularización
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Tu modelo alcanza 99% en los datos de entrenamiento y 60% en los de prueba. Memorizó en lugar de aprender. La regularización es el impuesto que cobras sobre la complejidad para forzar la generalización.
Tipo: Build Lenguajes: Python Prerrequisitos: Lección 03.06 (Optimizadores) Tiempo: ~75 minutos
Objetivos de Aprendizaje
- Implementar dropout con escalado invertido, weight decay L2, batch normalization, layer normalization y RMSNorm desde cero
- Medir la diferencia de exactitud entre entrenamiento y prueba y diagnosticar overfitting usando experimentos de regularización
- Explicar por qué los transformers usan LayerNorm en lugar de BatchNorm y por qué los LLM modernos prefieren RMSNorm
- Aplicar la combinación correcta de técnicas de regularización según la severidad del overfitting
El Problema
Una red neuronal con suficientes parámetros puede memorizar cualquier dataset. Esto no es hipotético -- Zhang et al. (2017) lo demostraron entrenando redes estándar en ImageNet con etiquetas aleatorias. Las redes alcanzaron una pérdida de entrenamiento cercana a cero con asignaciones de etiquetas completamente aleatorias. Memorizaron un millón de pares entrada-salida aleatorios sin ningún patrón que aprender. La pérdida de entrenamiento era perfecta. La exactitud de prueba era cero.
Este es el problema del overfitting, y empeora a medida que los modelos crecen. GPT-3 tiene 175 mil millones de parámetros. El conjunto de entrenamiento tiene cerca de 500 mil millones de tokens. Con tantos parámetros, el modelo tiene capacidad suficiente para memorizar fragmentos significativos de los datos de entrenamiento de forma literal. Sin regularización, simplemente regurgitaría ejemplos de entrenamiento en lugar de aprender patrones generalizables.
La diferencia entre el desempeño de entrenamiento y el desempeño de prueba es el gap de overfitting. Cada técnica de esta lección ataca ese gap desde un ángulo distinto. El dropout obliga a la red a no depender de ninguna neurona individual. El weight decay impide que cualquier peso individual crezca demasiado. La batch normalization suaviza el paisaje de la pérdida para que el optimizador encuentre mínimos más planos y generalizables. La layer normalization hace lo mismo, pero funciona donde la batch normalization falla (batches pequeños, secuencias de longitud variable). La RMSNorm lo hace un 10% más rápido al descartar el cálculo de la media. Cada técnica es simple. Juntas, son la diferencia entre un modelo que memoriza y uno que generaliza.
El Concepto
El Espectro del Overfitting
Todo modelo se ubica en algún punto de un espectro que va desde el underfitting (demasiado simple para capturar el patrón) hasta el overfitting (tan complejo que captura ruido). El punto ideal está en el medio, y la regularización empuja a los modelos hacia él desde el lado del overfit.
graph LR
Under["Underfitting<br/>Entren.: 60%<br/>Prueba: 58%<br/>Modelo demasiado simple"] --> Good["Buen Ajuste<br/>Entren.: 95%<br/>Prueba: 92%<br/>Generaliza bien"]
Good --> Over["Overfitting<br/>Entren.: 99.9%<br/>Prueba: 65%<br/>Memorizó ruido"]
Dropout["Dropout"] -->|"Empuja a la izquierda"| Over
WD["Weight Decay"] -->|"Empuja a la izquierda"| Over
BN["BatchNorm"] -->|"Empuja a la izquierda"| Over
Aug["Data Augmentation"] -->|"Empuja a la izquierda"| Over
Dropout
La técnica de regularización más simple, con la interpretación más elegante. Durante el entrenamiento, pon en cero aleatoriamente la salida de cada neurona con probabilidad p.
output = activation(z) * mask where mask[i] ~ Bernoulli(1 - p)
Con p = 0.5, la mitad de las neuronas se ponen en cero en cada pasada forward. La red debe aprender representaciones redundantes porque no puede predecir cuáles neuronas estarán disponibles. Esto evita la co-adaptación -- neuronas que aprenden a depender de la presencia de otras neuronas específicas.
La interpretación como ensemble: una red con N neuronas y dropout crea 2^N posibles subredes (toda combinación de cuáles neuronas están activas o inactivas). Entrenar con dropout entrena aproximadamente todas las 2^N subredes de forma simultánea, cada una en mini-batches distintos. Al momento de la prueba, usas todas las neuronas (sin dropout) y escalas las salidas por (1 - p) para igualar el valor esperado durante el entrenamiento. Esto equivale a promediar las predicciones de 2^N subredes -- un ensemble enorme a partir de un solo modelo.
En la práctica, el escalado se aplica durante el entrenamiento en lugar de la prueba (dropout invertido):
During training: output = activation(z) * mask / (1 - p)
During testing: output = activation(z) (no change needed)
Esto es más limpio porque el código de prueba no necesita saber nada sobre dropout.
Tasas por defecto: p = 0.1 para transformers, p = 0.5 para MLP, p = 0.2-0.3 para CNN. Más dropout = regularización más fuerte = mayor riesgo de underfitting.
Weight Decay (Regularización L2)
Agrega la magnitud al cuadrado de todos los pesos a la pérdida:
total_loss = task_loss + (lambda / 2) * sum(w_i^2)
El gradiente del término de regularización es lambda * w. Esto significa que, en cada paso, cada peso se encoge hacia cero en una fracción proporcional a su magnitud. Los pesos grandes se penalizan más. El modelo es empujado hacia soluciones donde ningún peso individual domina.
Por qué esto ayuda a la generalización: los modelos con overfit tienden a tener pesos grandes que amplifican el ruido en los datos de entrenamiento. El weight decay mantiene los pesos pequeños, lo que limita la capacidad efectiva del modelo y lo obliga a depender de características robustas y generalizables en lugar de peculiaridades memorizadas.
El hiperparámetro lambda controla la intensidad. Valores típicos:
- 0.01 para AdamW en transformers
- 1e-4 para SGD en CNN
- 0.1 para modelos con overfit severo
Como se discutió en la lección 06: weight decay y regularización L2 son equivalentes en SGD, pero no en Adam. Usa siempre AdamW (weight decay desacoplado) al entrenar con Adam.
Batch Normalization
Normaliza la salida de cada capa a lo largo del mini-batch antes de pasarla a la siguiente capa.
Para un mini-batch de activaciones en alguna capa:
mu = (1/B) * sum(x_i) (batch mean)
sigma^2 = (1/B) * sum((x_i - mu)^2) (batch variance)
x_hat = (x_i - mu) / sqrt(sigma^2 + eps) (normalize)
y = gamma * x_hat + beta (scale and shift)
Gamma y beta son parámetros aprendibles que permiten a la red deshacer la normalización si eso es óptimo. Sin ellos, estarías forzando la salida de cada capa a tener media cero y varianza unitaria, lo que podría no ser lo que la red quiere.
División entre entrenamiento e inferencia: Durante el entrenamiento, mu y sigma provienen del mini-batch actual. Durante la inferencia, usas promedios móviles acumulados durante el entrenamiento (media móvil exponencial con momentum = 0.1, lo que significa 90% del viejo + 10% del nuevo).
Por qué funciona la BatchNorm sigue en debate. El artículo original afirmaba que reduce el "internal covariate shift" (la distribución de las entradas de las capas cambiando a medida que las capas anteriores se actualizan). Santurkar et al. (2018) mostraron que esa explicación es incorrecta. La razón real: la BatchNorm hace que el paisaje de la pérdida sea más suave. Los gradientes son más predictivos, las constantes de Lipschitz son menores y el optimizador puede dar pasos más grandes con seguridad. Por eso la BatchNorm permite usar tasas de aprendizaje más altas y converger más rápido.
La BatchNorm tiene una limitación fundamental: depende de las estadísticas del batch. Con batch size 1, la media y la varianza no tienen sentido. Con batches pequeños (< 32), las estadísticas son ruidosas y perjudican el desempeño. Esto importa para tareas como la detección de objetos (donde los límites de memoria restringen el tamaño del batch) y el modelado de lenguaje (donde las longitudes de las secuencias varían).
Layer Normalization
Normaliza a lo largo de las características en lugar de a lo largo del batch. Para una sola muestra:
mu = (1/D) * sum(x_j) (feature mean)
sigma^2 = (1/D) * sum((x_j - mu)^2) (feature variance)
x_hat = (x_j - mu) / sqrt(sigma^2 + eps)
y = gamma * x_hat + beta
D es la dimensión de las características. Cada muestra se normaliza de forma independiente -- sin dependencia del tamaño del batch. Por eso los transformers usan LayerNorm en lugar de BatchNorm. Las secuencias tienen longitudes variables, los tamaños de batch suelen ser pequeños (o 1 durante la generación) y el cálculo es idéntico entre entrenamiento e inferencia.
La LayerNorm en transformers se aplica después de cada bloque de self-attention y cada bloque feed-forward (Post-LN), o antes de ellos (Pre-LN, que es más estable para el entrenamiento).
RMSNorm
LayerNorm sin la sustracción de la media. Propuesta por Zhang & Sennrich (2019).
rms = sqrt((1/D) * sum(x_j^2))
y = gamma * x / rms
Eso es todo. Sin cálculo de media, sin parámetro beta. La observación: el re-centrado (sustracción de la media) en LayerNorm contribuye muy poco al desempeño del modelo, pero cuesta cómputo. Eliminarlo da la misma exactitud con cerca de un 10% menos de overhead.
LLaMA, LLaMA 2, LLaMA 3, Mistral y la mayoría de los LLM modernos usan RMSNorm en lugar de LayerNorm. A la escala de miles de millones de parámetros y billones de tokens, ese ahorro del 10% es significativo.
Comparación de Normalizaciones
graph TD
subgraph "Batch Normalization"
BN_D["Normaliza a lo largo del BATCH<br/>para cada característica"]
BN_S["Batch: [x1, x2, x3, x4]<br/>Característica 1: normaliza [x1f1, x2f1, x3f1, x4f1]"]
BN_P["Necesita batch > 32<br/>Entrenamiento distinto de eval<br/>Usada en CNN"]
end
subgraph "Layer Normalization"
LN_D["Normaliza a lo largo de las CARACTERÍSTICAS<br/>para cada muestra"]
LN_S["Muestra x1: normaliza [f1, f2, f3, f4]"]
LN_P["Independiente del batch<br/>Entrenamiento igual a eval<br/>Usada en Transformers"]
end
subgraph "RMS Normalization"
RN_D["Como LayerNorm<br/>pero omite la sustracción de la media"]
RN_S["Solo divide por la RMS<br/>Sin centrado"]
RN_P["10% más rápida que LayerNorm<br/>Misma exactitud<br/>Usada en LLaMA, Mistral"]
end
Data Augmentation como Regularización
No es una modificación del modelo, sino una modificación de los datos. Transforma las entradas de entrenamiento preservando las etiquetas:
- Imágenes: crop aleatorio, flip, rotación, color jitter, cutout
- Texto: sustitución de sinónimos, back-translation, eliminación aleatoria
- Audio: time stretch, pitch shift, adición de ruido
El efecto es idéntico al de la regularización: aumenta el tamaño efectivo del conjunto de entrenamiento, dificultando que el modelo memorice ejemplos específicos. Un modelo que ve cada imagen solo una vez en su forma original puede memorizarla. Un modelo que ve 50 versiones aumentadas de cada imagen se ve forzado a aprender la estructura invariante.
Early Stopping
El regularizador más simple: detén el entrenamiento cuando la pérdida de validación empiece a aumentar. El modelo aún no ha hecho overfit en ese punto. En la práctica, das seguimiento a la pérdida de validación en cada época, guardas el mejor modelo y continúas entrenando durante una ventana de "paciencia" (típicamente 5-20 épocas). Si la pérdida de validación no mejora dentro de la ventana de paciencia, te detienes y cargas el mejor modelo guardado.
Cuándo Aplicar Qué
flowchart TD
Gap{"¿Gap de exactitud<br/>entrenamiento-prueba?"} -->|"> 10%"| Heavy["Regularización fuerte"]
Gap -->|"5-10%"| Medium["Regularización moderada"]
Gap -->|"< 5%"| Light["Regularización ligera"]
Heavy --> D5["Dropout p=0.3-0.5"]
Heavy --> WD2["Weight decay 0.01-0.1"]
Heavy --> Aug["Data augmentation agresiva"]
Heavy --> ES["Early stopping"]
Medium --> D3["Dropout p=0.1-0.2"]
Medium --> WD1["Weight decay 0.001-0.01"]
Medium --> Norm["BatchNorm o LayerNorm"]
Light --> D1["Dropout p=0.05-0.1"]
Light --> WD0["Weight decay 1e-4"]
Constrúyelo
Paso 1: Dropout (Modo Entrenamiento y Eval)
import random
import math
class Dropout:
def __init__(self, p=0.5):
self.p = p
self.training = True
self.mask = None
def forward(self, x):
if not self.training:
return list(x)
self.mask = []
output = []
for val in x:
if random.random() < self.p:
self.mask.append(0)
output.append(0.0)
else:
self.mask.append(1)
output.append(val / (1 - self.p))
return output
def backward(self, grad_output):
grads = []
for g, m in zip(grad_output, self.mask):
if m == 0:
grads.append(0.0)
else:
grads.append(g / (1 - self.p))
return grads
Paso 2: Weight Decay L2
def l2_regularization(weights, lambda_reg):
penalty = 0.0
for w in weights:
penalty += w * w
return lambda_reg * 0.5 * penalty
def l2_gradient(weights, lambda_reg):
return [lambda_reg * w for w in weights]
Paso 3: Batch Normalization
class BatchNorm:
def __init__(self, num_features, momentum=0.1, eps=1e-5):
self.gamma = [1.0] * num_features
self.beta = [0.0] * num_features
self.eps = eps
self.momentum = momentum
self.running_mean = [0.0] * num_features
self.running_var = [1.0] * num_features
self.training = True
self.num_features = num_features
def forward(self, batch):
batch_size = len(batch)
if self.training:
mean = [0.0] * self.num_features
for sample in batch:
for j in range(self.num_features):
mean[j] += sample[j]
mean = [m / batch_size for m in mean]
var = [0.0] * self.num_features
for sample in batch:
for j in range(self.num_features):
var[j] += (sample[j] - mean[j]) ** 2
var = [v / batch_size for v in var]
for j in range(self.num_features):
self.running_mean[j] = (1 - self.momentum) * self.running_mean[j] + self.momentum * mean[j]
self.running_var[j] = (1 - self.momentum) * self.running_var[j] + self.momentum * var[j]
else:
mean = list(self.running_mean)
var = list(self.running_var)
self.x_hat = []
output = []
for sample in batch:
normalized = []
out_sample = []
for j in range(self.num_features):
x_h = (sample[j] - mean[j]) / math.sqrt(var[j] + self.eps)
normalized.append(x_h)
out_sample.append(self.gamma[j] * x_h + self.beta[j])
self.x_hat.append(normalized)
output.append(out_sample)
return output
Paso 4: Layer Normalization
class LayerNorm:
def __init__(self, num_features, eps=1e-5):
self.gamma = [1.0] * num_features
self.beta = [0.0] * num_features
self.eps = eps
self.num_features = num_features
def forward(self, x):
mean = sum(x) / len(x)
var = sum((xi - mean) ** 2 for xi in x) / len(x)
self.x_hat = []
output = []
for j in range(self.num_features):
x_h = (x[j] - mean) / math.sqrt(var + self.eps)
self.x_hat.append(x_h)
output.append(self.gamma[j] * x_h + self.beta[j])
return output
Paso 5: RMSNorm
class RMSNorm:
def __init__(self, num_features, eps=1e-6):
self.gamma = [1.0] * num_features
self.eps = eps
self.num_features = num_features
def forward(self, x):
rms = math.sqrt(sum(xi * xi for xi in x) / len(x) + self.eps)
output = []
for j in range(self.num_features):
output.append(self.gamma[j] * x[j] / rms)
return output
Paso 6: Entrenamiento Con y Sin Regularización
def sigmoid(x):
x = max(-500, min(500, x))
return 1.0 / (1.0 + math.exp(-x))
def make_circle_data(n=200, seed=42):
random.seed(seed)
data = []
for _ in range(n):
x = random.uniform(-2, 2)
y = random.uniform(-2, 2)
label = 1.0 if x * x + y * y < 1.5 else 0.0
data.append(([x, y], label))
return data
class RegularizedNetwork:
def __init__(self, hidden_size=16, lr=0.05, dropout_p=0.0, weight_decay=0.0):
random.seed(0)
self.hidden_size = hidden_size
self.lr = lr
self.dropout_p = dropout_p
self.weight_decay = weight_decay
self.dropout = Dropout(p=dropout_p) if dropout_p > 0 else None
self.w1 = [[random.gauss(0, 0.5) for _ in range(2)] for _ in range(hidden_size)]
self.b1 = [0.0] * hidden_size
self.w2 = [random.gauss(0, 0.5) for _ in range(hidden_size)]
self.b2 = 0.0
def forward(self, x, training=True):
self.x = x
self.z1 = []
self.h = []
for i in range(self.hidden_size):
z = self.w1[i][0] * x[0] + self.w1[i][1] * x[1] + self.b1[i]
self.z1.append(z)
self.h.append(max(0.0, z))
if self.dropout and training:
self.dropout.training = True
self.h = self.dropout.forward(self.h)
elif self.dropout:
self.dropout.training = False
self.h = self.dropout.forward(self.h)
self.z2 = sum(self.w2[i] * self.h[i] for i in range(self.hidden_size)) + self.b2
self.out = sigmoid(self.z2)
return self.out
def backward(self, target):
eps = 1e-15
p = max(eps, min(1 - eps, self.out))
d_loss = -(target / p) + (1 - target) / (1 - p)
d_sigmoid = self.out * (1 - self.out)
d_out = d_loss * d_sigmoid
for i in range(self.hidden_size):
d_relu = 1.0 if self.z1[i] > 0 else 0.0
d_h = d_out * self.w2[i] * d_relu
self.w2[i] -= self.lr * (d_out * self.h[i] + self.weight_decay * self.w2[i])
for j in range(2):
self.w1[i][j] -= self.lr * (d_h * self.x[j] + self.weight_decay * self.w1[i][j])
self.b1[i] -= self.lr * d_h
self.b2 -= self.lr * d_out
def evaluate(self, data):
correct = 0
total_loss = 0.0
for x, y in data:
pred = self.forward(x, training=False)
eps = 1e-15
p = max(eps, min(1 - eps, pred))
total_loss += -(y * math.log(p) + (1 - y) * math.log(1 - p))
if (pred >= 0.5) == (y >= 0.5):
correct += 1
return total_loss / len(data), correct / len(data) * 100
def train_model(self, train_data, test_data, epochs=300):
history = []
for epoch in range(epochs):
total_loss = 0.0
correct = 0
for x, y in train_data:
pred = self.forward(x, training=True)
self.backward(y)
eps = 1e-15
p = max(eps, min(1 - eps, pred))
total_loss += -(y * math.log(p) + (1 - y) * math.log(1 - p))
if (pred >= 0.5) == (y >= 0.5):
correct += 1
train_loss = total_loss / len(train_data)
train_acc = correct / len(train_data) * 100
test_loss, test_acc = self.evaluate(test_data)
history.append((train_loss, train_acc, test_loss, test_acc))
if epoch % 75 == 0 or epoch == epochs - 1:
gap = train_acc - test_acc
print(f" Epoch {epoch:3d}: train_acc={train_acc:.1f}%, test_acc={test_acc:.1f}%, gap={gap:.1f}%")
return history
Úsalo
PyTorch proporciona toda la normalización y regularización como módulos:
import torch
import torch.nn as nn
model = nn.Sequential(
nn.Linear(784, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, 128),
nn.BatchNorm1d(128),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(128, 10),
)
model.train()
out_train = model(torch.randn(32, 784))
model.eval()
out_test = model(torch.randn(1, 784))
El toggle model.train() / model.eval() es crítico. Activa/desactiva el dropout e indica a la BatchNorm que use estadísticas del batch o estadísticas móviles. Olvidar model.eval() antes de la inferencia es uno de los bugs más comunes en deep learning. Tu exactitud de prueba fluctuará aleatoriamente porque el dropout sigue activo y la BatchNorm está usando estadísticas del mini-batch.
Para transformers, el patrón es distinto:
class TransformerBlock(nn.Module):
def __init__(self, d_model=512, nhead=8, dropout=0.1):
super().__init__()
self.attention = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
self.norm1 = nn.LayerNorm(d_model)
self.ff = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.GELU(),
nn.Linear(d_model * 4, d_model),
nn.Dropout(dropout),
)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
attended, _ = self.attention(x, x, x)
x = self.norm1(x + self.dropout(attended))
x = self.norm2(x + self.ff(x))
return x
LayerNorm, no BatchNorm. Dropout p=0.1, no p=0.5. Estos son los valores por defecto de los transformers.
Entrégalo
Esta lección produce:
outputs/prompt-regularization-advisor.md-- un prompt que diagnostica overfitting y recomienda la estrategia de regularización correcta
Ejercicios
Implementa spatial dropout para datos 2D: en lugar de descartar neuronas individuales, descarta canales de características completos. Simula esto tratando grupos de características consecutivas como canales y descartando grupos enteros. Compara el gap entrenamiento-prueba con el dropout estándar en el dataset del círculo con hidden_size=32.
Implementa label smoothing de la lección 05 combinado con el dropout de esta lección. Entrena con cuatro configuraciones: ninguno, solo dropout, solo label smoothing, ambos. Mide el gap final de exactitud entrenamiento-prueba para cada uno. ¿Qué combinación da el menor gap?
Agrega una capa de BatchNorm entre la capa oculta y la activación en tu red del dataset del círculo. Entrena con y sin BatchNorm en las tasas de aprendizaje 0.01, 0.05 y 0.1. La BatchNorm debería permitir un entrenamiento estable en tasas de aprendizaje más altas, donde la red común diverge.
Implementa early stopping: da seguimiento a la pérdida de prueba en cada época, guarda los mejores pesos y detente si la pérdida de prueba no mejora durante 20 épocas. Ejecuta la red regularizada durante 1000 épocas. Reporta en qué época se obtuvo la mejor exactitud de prueba y cuántas épocas de cómputo ahorraste.
Compara LayerNorm vs RMSNorm en una red de 4 capas (no solo 2). Inicializa ambas con los mismos pesos. Entrena durante 200 épocas y compara la exactitud final, la velocidad de entrenamiento (tiempo por época) y las magnitudes de los gradientes en la primera capa. Verifica que la RMSNorm es más rápida con la misma exactitud.
Términos Clave
| Término | Lo que la gente dice | Lo que realmente significa |
|---|---|---|
| Overfitting | "El modelo memorizó los datos" | Cuando el desempeño de entrenamiento de un modelo supera significativamente su desempeño de prueba, lo que indica que aprendió ruido en lugar de señal |
| Regularización | "Prevenir el overfitting" | Cualquier técnica que restringe la complejidad del modelo para mejorar la generalización: dropout, weight decay, normalización, augmentation |
| Dropout | "Eliminación aleatoria de neuronas" | Poner en cero neuronas aleatorias durante el entrenamiento con probabilidad p, forzando representaciones redundantes; equivalente a entrenar un ensemble |
| Weight decay | "Penalización L2" | Encoger todos los pesos hacia cero restando lambda * w en cada paso; penaliza la complejidad mediante la magnitud de los pesos |
| Batch normalization | "Normalizar por batch" | Normalizar las salidas de las capas a lo largo de la dimensión del batch usando estadísticas del batch durante el entrenamiento y promedios móviles durante la inferencia |
| Layer normalization | "Normalizar por muestra" | Normalizar a lo largo de las características dentro de cada muestra; independiente del batch, usada en transformers donde el tamaño del batch varía |
| RMSNorm | "LayerNorm sin la media" | Normalización por raíz cuadrática media; descarta la sustracción de la media de LayerNorm para un speedup del 10% con igual exactitud |
| Early stopping | "Detener antes del overfit" | Interrumpir el entrenamiento cuando la pérdida de validación deja de mejorar; el regularizador más simple, frecuentemente usado junto con otros |
| Data augmentation | "Más datos a partir de menos" | Transformar las entradas de entrenamiento (flip, crop, ruido) para aumentar el tamaño efectivo del dataset y forzar el aprendizaje de invariancia |
| Gap de generalización | "División entrenamiento-prueba" | La diferencia entre el desempeño de entrenamiento y el de prueba; la regularización busca minimizar este gap |
Lecturas Adicionales
- Srivastava et al., "Dropout: A Simple Way to Prevent Neural Networks from Overfitting" (2014) -- el artículo original del dropout con la interpretación como ensemble y experimentos extensos
- Ioffe & Szegedy, "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift" (2015) -- introdujo la BatchNorm y su procedimiento de entrenamiento, uno de los artículos de deep learning más citados
- Zhang & Sennrich, "Root Mean Square Layer Normalization" (2019) -- mostró que la RMSNorm iguala la exactitud de LayerNorm con cómputo reducido; adoptada por LLaMA y Mistral
- Zhang et al., "Understanding Deep Learning Requires Rethinking Generalization" (2017) -- el artículo emblemático que muestra que las redes neuronales pueden memorizar etiquetas aleatorias, desafiando las visiones tradicionales sobre la generalización