← Todo el material
Curso Intermedio 23 de agosto de 2026

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.001 a 0.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 (de 0.01 a 0.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 pesoEl mismo lr para todosAdaptado a cada peso
Velocidad de arranqueMás lentoRápido, converge antes
Sensible al learning rateMucho: hay que afinarloMenos: perdona más
Memoria que usaBajaMás (guarda estadísticas por peso)
Cuándo brillaModelos maduros, mejor resultado finalDefault, prototipos, casi todo
Regla prácticaSi sabes lo que hacesSi 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, no loss.backward(). Backward solo calcula los gradientes y los deja en .grad; step los lee y actualiza. Si olvidas step(), 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 NaN o que no baja. Ante cualquier explosión, bájalo primero.
  • Olvidar optimizer.step() o optimizer.zero_grad(). Sin step no aprende; sin zero_grad aprende 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

Redes neuronalesDeep learningEntrenamientoOptimizersPyTorch

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.