Redes Neuronales — Módulo 6: entrenar en la práctica — épocas, batches, learning rate y optimizers
Sexto módulo del curso de Redes Neuronales: el loop de entrenamiento real de PyTorch paso a paso — qué es una época y un mini-batch, por qué no se entrena con todo el dataset de una, cómo el learning rate decide el tamaño del paso, y cuándo usar SGD, momentum o Adam.
☰ Índice del contenido
En el Módulo 5 vimos que backpropagation nos entrega el gradiente de cada peso: la culpa repartida. Pero un solo gradiente no entrena nada — es una foto de un instante, con un ejemplo. Entrenar de verdad es repetir ese cálculo miles de veces, sobre miles de datos, dando pasitos hacia una red mejor. Este módulo es el loop de entrenamiento real: cómo se organizan esas repeticiones (épocas y batches), cuán grande es cada paso (learning rate) y quién decide exactamente cómo moverse (los optimizers).
La tesis del módulo
Entrenar es un ciclo: mira un puñado de datos, predice, mide el error, calcula la culpa, ajusta un poquito, repite. Los tres botones que controlas son cuántos datos por vuelta (batch), cuán grande el paso (learning rate) y qué estrategia de paso usar (optimizer). Casi todo el arte del entrenamiento vive en esos tres.
Época, batch y mini-batch
Tres palabras que se confunden y conviene fijar:
- Época (epoch): una pasada completa por todo el dataset. Si tienes 60.000 imágenes, una época es haberlas visto las 60.000 una vez. Entrenas durante varias épocas — la red ve los mismos datos muchas veces y afina un poco más en cada vuelta.
- Batch: un grupo de ejemplos que la red procesa junta antes de actualizar los pesos una vez.
- Mini-batch: en la práctica todo batch es “mini” — un subconjunto chico (ej. 32, 64, 128 ejemplos) del dataset entero. La palabra distingue este enfoque de entrenar con el dataset completo de una sola vez.
Por qué no se entrena con todo de una
Podrías, en teoría, calcular la loss sobre las 60.000 imágenes juntas y actualizar una vez por época. No se hace, por dos razones:
- Memoria: meter el dataset entero por la red a la vez no cabe en la VRAM. Ya vimos en el curso de GPU que la memoria manda.
- Aprendizaje: actualizar solo una vez por época significa dar poquísimos pasos. Con mini-batches das cientos de pasos por época — la red aprende mucho más rápido, y el “ruido” de cada batch chico incluso ayuda a no quedarse pegado en malos mínimos.
El tamaño de batch es un equilibrio: muy chico y el entrenamiento es ruidoso y lento por overhead; muy grande y no cabe en memoria y los pasos se vuelven pocos. Valores como 32, 64 o 128 son el punto dulce habitual.
Learning rate: el tamaño del paso
El learning rate (tasa de aprendizaje, lr) es el número que decide cuánto mueves cada peso en la dirección que indica el gradiente. Es, probablemente, el hiperparámetro más importante de todos.
La analogía: bajar un cerro con niebla hacia el punto más bajo. El gradiente te dice hacia dónde es cuesta abajo. El learning rate es el largo de tu zancada:
learning rate MUY ALTO: rebota y diverge
loss
│ x x
│ x x
│ x x ¡nunca baja!
└──────────────────────► pasos
learning rate MUY BAJO: baja, pero tardísimo
loss
│x
│ x
│ x_
│ ‾x__
│ ‾‾x___ (llega... en un año)
└──────────────────────► pasos
learning rate JUSTO: baja firme y llega
loss
│x
│ x
│ x_
│ ‾x__
│ ‾‾x____ ______ mínimo
└──────────────────────► pasos
- Muy alto: das zancadas gigantes, te pasas del mínimo una y otra vez, la loss rebota o explota. El entrenamiento diverge.
- Muy bajo: avanzas con pasos minúsculos; llegarías, pero tardarías carísimas horas de cómputo.
- Justo: bajas firme y llegas. Encontrarlo es parte prueba y error, parte oficio. Valores típicos de arranque:
0.001a0.01.
Ojo: si tu loss se dispara a
NaN(not a number, “no es un número”) o sube en vez de bajar, el sospechoso número uno es un learning rate demasiado alto. Bájalo 10 veces (de0.01a0.001) antes de tocar cualquier otra cosa.
Optimizers: la estrategia del paso
El optimizer es el componente que toma los gradientes y decide el ajuste concreto de cada peso. Hay varios, con distinta inteligencia:
- SGD (Stochastic Gradient Descent, descenso de gradiente estocástico): el básico. Mueve cada peso directamente en contra de su gradiente, escalado por el learning rate. “Estocástico” solo significa que usa mini-batches (una muestra al azar) en vez del dataset entero. Simple y honesto, pero puede ser lento y quedarse pegado.
- Momentum (impulso): SGD con memoria. Acumula la dirección de los pasos recientes como una bola que rueda cuesta abajo y agarra inercia. Atraviesa zonas planas y amortigua los rebotes. No es otro optimizer, es SGD mejorado.
- Adam (Adaptive Moment Estimation): el default moderno. Adapta el tamaño de paso por cada peso según su historial de gradientes, y combina momentum. Requiere menos afinar el learning rate a mano y converge rápido en casi todo. Si no sabes cuál usar, usa Adam.
SGD vs Adam
| SGD (con momentum) | Adam | |
|---|---|---|
| Paso por peso | El mismo lr para todos | Adaptado a cada peso |
| Velocidad de arranque | Más lento | Rápido, converge antes |
| Sensible al learning rate | Mucho: hay que afinarlo | Menos: perdona más |
| Memoria que usa | Baja | Más (guarda estadísticas por peso) |
| Cuándo brilla | Modelos maduros, mejor resultado final | Default, prototipos, casi todo |
| Regla práctica | Si sabes lo que haces | Si quieres que funcione ya |
El training loop completo
Aquí se junta todo: batches, forward, loss, backward() del Módulo 5, y el paso del optimizer.
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
# modelo, datos y etiquetas se asumen ya definidos (Módulo 8 los arma de verdad)
modelo = MiRed()
train_loader = DataLoader(dataset, batch_size=64, shuffle=True)
# La loss mide el error; el optimizer decide el ajuste. Adam como default.
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(modelo.parameters(), lr=0.001)
EPOCAS = 10
for epoca in range(EPOCAS): # una vuelta = ver TODO el dataset
for x, y in train_loader: # cada iteración = un mini-batch
optimizer.zero_grad() # 1. borra gradientes acumulados
pred = modelo(x) # 2. FORWARD: predice el batch
loss = loss_fn(pred, y) # 3. mide el error del batch
loss.backward() # 4. BACKWARD: calcula gradientes
optimizer.step() # 5. mueve los pesos (aplica el lr)
print(f"Época {epoca+1}: loss = {loss.item():.4f}")
Los cinco pasos del batch, en orden, son el corazón de todo el deep learning:
un MINI-BATCH recorre los cinco pasos, y se repite:
┌────────────────────────────────────────────┐
▼ │
zero_grad ──► forward ──► loss ──► backward ──► step
borra predice error gradientes ajusta
pesos
▲ │
└────── siguiente batch ◄ ───────────────────┘
(al terminar todos: fin de una época)
Fíjate en el orden sagrado: primero zero_grad, porque como vimos en el Módulo 5 los gradientes se acumulan y hay que partir en cero cada batch. Después forward, loss, backward, y solo entonces step mueve los pesos. Cambiar este orden es fuente inagotable de bugs.
Ojo:
optimizer.step()es quien realmente cambia los pesos, noloss.backward(). Backward solo calcula los gradientes y los deja en.grad; step los lee y actualiza. Si olvidasstep(), la red calcula todo perfecto y no aprende nada — la loss se queda clavada.
Errores comunes
- Learning rate demasiado alto. La causa más frecuente de loss en
NaNo que no baja. Ante cualquier explosión, bájalo primero. - Olvidar
optimizer.step()ooptimizer.zero_grad(). Sinstepno aprende; sinzero_gradaprende con basura acumulada. Ambos son silenciosos: no dan error, solo entrenan mal. - Batch gigante para “ir más rápido”. Un batch enorme da menos pasos por época y no cabe en memoria. Más grande no es más rápido de aprender.
- Confundir época con iteración. Una época son muchas iteraciones (una por batch). Entrenar “10 épocas” con batches de 64 sobre 60.000 datos son casi 10.000 actualizaciones, no 10.
- Cambiar de SGD a Adam sin ajustar el learning rate. Cada optimizer vive en su rango de lr. Un lr bueno para SGD suele ser malo para Adam y viceversa.
- Juzgar el entrenamiento por la loss de train. Que baje en train no significa que la red generalice — ese es justo el tema del próximo módulo.
La frase para llevarse
“El gradiente te dice hacia dónde bajar; el learning rate, cuánto zancar; el optimizer, con qué estilo caminar. Y todo eso, batch tras batch, es entrenar.”
Qué viene
Módulo 7: overfitting y regularización. Ya sabes entrenar y hacer bajar la loss — pero una loss baja en train puede esconder una red que memoriza en vez de aprender. Veremos cómo detectar ese engaño con validation y cómo combatirlo: dropout, weight decay, batch normalization y early stopping.
Material relacionado
Preguntas sobre este curso
Regístrate gratis para hacer preguntas y guardar tu progreso.
✉ Entérate de lo nuevo
Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.