LECCIÓN 6 · CONSTRUIR Y ENTRENAR

Activaciones y funciones de pérdida

Dos piezas que ya conoces del curso de redes, ahora en su versión PyTorch: las activaciones (la chispa no-lineal) y las pérdidas (cómo se mide el error). Saber elegir la pérdida correcta es media batalla.

1. Activaciones

PyTorch las ofrece como funciones (torch.relu) o como capas (nn.ReLU()). Apliquémoslas a un mismo vector para ver su efecto:

z = torch.tensor([-2.0, -0.5, 0.0, 0.5, 2.0])
torch.relu(z)
torch.sigmoid(z)
torch.tanh(z)
salida realrelu : tensor([0.0000, 0.0000, 0.0000, 0.5000, 2.0000]) sigm : tensor([0.1192, 0.3775, 0.5000, 0.6225, 0.8808]) tanh : tensor([-0.9640, -0.4621, 0.0000, 0.4621, 0.9640])
Regla práctica: ReLU en capas ocultas; en la salida depende de la tarea (sigmoid para binaria, nada/softmax para multiclase — y a menudo la pérdida se encarga del softmax, como veremos abajo).

2. La pérdida mide cuánto te equivocas

La función de pérdida compara la predicción con el objetivo y devuelve un número a minimizar. PyTorch trae las más usadas listas en torch.nn.

3. MSELoss — para regresión (predecir un número)

Error cuadrático medio: el promedio de (predicción − objetivo)².

pred   = torch.tensor([2.5, 0.0, 2.0])
target = torch.tensor([3.0, -0.5, 2.0])
loss_fn = nn.MSELoss()
loss_fn(pred, target)
salida realMSE: 0.16667
✍️ A mano: pred=[2.5, 0.0, 2.0], target=[3.0, -0.5, 2.0]
1) error de cada elemento (pred − target):
     2.5 − 3.0  = -0.5
     0.0 − (-0.5) = 0.5
     2.0 − 2.0  = 0.0
2) elevar cada error al cuadrado:
     (-0.5)² = 0.25
     ( 0.5)² = 0.25
     ( 0.0)² = 0.00
3) promediar (sumar y dividir entre 3):
     (0.25 + 0.25 + 0.00) / 3 = 0.50 / 3 = 0.16667

Úsala cuando la salida es un valor continuo (precio, temperatura… nuestro proyecto tabular).

4. CrossEntropyLoss — para clasificación (elegir una clase)

La pérdida estrella en clasificación. Recibe los logits crudos (puntajes sin softmax) y el índice de la clase correcta. Castiga mucho estar seguro y equivocado:

logits = torch.tensor([[2.0, 0.5, 0.1]])   # puntajes para 3 clases
ce = nn.CrossEntropyLoss()

ce(logits, torch.tensor([0]))   # clase correcta = 0 (la de mayor logit)
ce(logits, torch.tensor([2]))   # clase correcta = 2 (la de menor logit)
salida realclase correcta = 0 (acierta) → 0.3168 clase correcta = 2 (falla) → 2.2168
✍️ A mano: qué hace CrossEntropy por dentro (softmax + −log)
Logits = [2.0, 0.5, 0.1]

PASO 1 — softmax (convierte logits en probabilidades):
   a) exponenciar cada logit:
        e^2.0 = 7.389056
        e^0.5 = 1.648721
        e^0.1 = 1.105171
   b) sumar:  7.389056 + 1.648721 + 1.105171 = 10.142948
   c) dividir cada uno entre la suma:
        p₀ = 7.389056 / 10.142948 = 0.728492
        p₁ = 1.648721 / 10.142948 = 0.162549
        p₂ = 1.105171 / 10.142948 = 0.108960

PASO 2 — tomar −ln de la probabilidad de la clase correcta:
   clase 0:  −ln(0.728492) = 0.3168    ✓  (prob alta → pérdida baja)
   clase 2:  −ln(0.108960) = 2.2168    ✓  (prob baja → pérdida alta)
🔑 Dos sorpresas importantes de CrossEntropyLoss:
1. Le pasas los logits crudos, NO les apliques softmax tú — la pérdida lo hace por dentro (hacerlo dos veces es un bug clásico).
2. El objetivo es el índice de la clase (0, 1, 2…), no un vector one-hot.

Fíjate cómo cuando el modelo "apuesta" por la clase correcta la pérdida es baja (0.32), y cuando apuesta fuerte por la equivocada se dispara (2.22).

5. ¿Cuál uso?

TareaSalida de la redPérdida
Regresión (un número)1 neurona, sin activaciónnn.MSELoss
Clasificación binaria1 neurona + sigmoidnn.BCELoss
Clasificación multiclaseN neuronas (logits)nn.CrossEntropyLoss
Resumen: las activaciones dan no-linealidad (ReLU en ocultas); la pérdida mide el error y depende de la tarea (MSE para regresión, CrossEntropy para clasificación). Ya tenemos modelo + pérdida. Falta quién ajuste los pesos: el optimizador, y el bucle de entrenamiento — la próxima lección.