LECCIÓN 14 · AVANZADO

Técnicas pro

Tres herramientas que separan un entrenamiento amateur de uno profesional: ajustar el learning rate sobre la marcha, controlar gradientes que explotan, y crear tus propias capas.

1. Learning rate schedulers: bajar el paso con el tiempo

Un learning rate fijo es un compromiso: grande al principio (avanzar rápido) pero pequeño al final (afinar sin pasarse). Un scheduler lo reduce automáticamente durante el entrenamiento. StepLR lo multiplica por gamma cada step_size épocas:

opt = torch.optim.SGD(params, lr=0.1)
sched = torch.optim.lr_scheduler.StepLR(opt, step_size=2, gamma=0.5)

for epoca in range(6):
    # ... entrenar una época ...
    opt.step()
    sched.step()      ← actualizar el learning rate
salida real (lr por época)epoca 0: lr=0.1000 epoca 1: lr=0.1000 epoca 2: lr=0.0500 ← se redujo a la mitad epoca 3: lr=0.0500 epoca 4: lr=0.0250 ← otra vez epoca 5: lr=0.0250
Cada 2 épocas el lr se multiplica por 0.5. Empiezas avanzando rápido y terminas dando pasitos finos para asentarte en el mínimo. Mejora la precisión final notablemente. Llama sched.step() una vez por época, después de opt.step().

2. Gradient clipping: domar gradientes que explotan

A veces los gradientes se vuelven gigantescos y un solo paso destroza el entrenamiento (típico en redes profundas o recurrentes). El clipping los recorta a un tamaño máximo, conservando su dirección:

w = torch.tensor([3.0, 4.0], requires_grad=True)
(w.sum() * 100).backward()      # gradiente enorme

torch.nn.utils.clip_grad_norm_([w], max_norm=1.0)
salida realgrad antes: [100.0, 100.0] (norma = 141.42) grad después: [0.7071, 0.7071] (norma = 1.0)
✍️ A mano: cómo se recorta
1) norma del gradiente (longitud del vector):
     √(100² + 100²) = √(10000 + 10000) = √20000 = 141.42
2) como 141.42 > max_norm (1.0), se reduce por el factor:
     factor = max_norm / norma = 1.0 / 141.42 = 0.007071
3) multiplicar cada componente por el factor:
     100 · 0.007071 = 0.7071
     100 · 0.007071 = 0.7071
   → misma dirección (ambos iguales), pero norma ahora = 1.0  ✓

El gradiente se redujo para que su "longitud" (norma) no pase de 1, pero mantiene su dirección (ambos componentes iguales). Va en el bucle entre backward() y step().

3. Capas y modelos a medida

Ya sabes crear modelos con nn.Module. La misma idea sirve para inventar tu propia capa: hereda de nn.Module y define su forward. Por ejemplo, una capa que aplica una transformación lineal y le suma la entrada (una "conexión residual", como en el Transformer):

class BloqueResidual(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.lin = nn.Linear(dim, dim)

    def forward(self, x):
        return x + torch.relu(self.lin(x))   # salida + entrada (residual)
Esto es lo poderoso de PyTorch: como todo es nn.Module con un forward, puedes componer y crear arquitecturas arbitrarias. Las capas que ya viste (Linear, Conv2d…) son justo esto, escritas por el equipo de PyTorch.

4. Resumen — fin del nivel avanzado

Schedulers bajan el lr con el tiempo (mejor convergencia); gradient clipping evita pasos catastróficos; las capas custom te dan libertad total para diseñar arquitecturas. Con esto completas la teoría de PyTorch — desde el primer tensor hasta las técnicas profesionales. Solo queda lo mejor: aplicar todo en un proyecto real de principio a fin.