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

  1. 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.

  2. 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?

  3. 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.

  4. 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.

  5. 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
0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).