Skip to content

English · Español

02 — La regla de la cadena y el gradiente de softmax + cross-entropy

🇪🇸 La regla de la cadena para funciones vectoriales es producto de Jacobianos. Aplicada a softmax + cross-entropy, colapsa en una línea: ∂CE/∂x = softmax(x) - one_hot(y). Esta página deriva ambos.

Esta es la página de teoría de la Fase 4. Vuelve a derivar todo lo de abajo desde una página en blanco hasta que sea mecánico.


Regla de la cadena de una variable

Para h(x) = f(g(x)):

\[ h'(x) = f'(g(x)) \cdot g'(x) \]

La derivada de la función externa evaluada en la interna, por la derivada de la interna. Memoriza.

Trabajado: h(x) = sin(x²). Con f(u) = sin(u), g(x) = x²:

\[ h'(x) = \cos(x^2) \cdot 2x \]

Regla de la cadena multivariable (la versión que usa backprop)

Para f: R^k → R^m y g: R^n → R^k, sea h = f ∘ g: R^n → R^m. Entonces:

\[ J_h(x) = J_f(g(x)) \cdot J_g(x) \]

Producto matricial de Jacobianas. Formas: (m × n) = (m × k) · (k × n). La dimensión interna k coincide por construcción (salida de g = entrada de f).

Para una pérdida escalar L = f(g(x)) con f: R^k → R y g: R^n → R^k:

  • J_f = ∇_y L^T es un vector fila 1 × k (transpuesta del gradiente).
  • J_g es k × n.
  • J_h = ∇_x L^T = ∇_y L^T · J_g es 1 × n.

Transponiendo para obtener el gradiente como columna:

\[ \nabla_x L = J_g^T \cdot \nabla_y L \]

Esta es la fórmula que usa backprop. Léela como: "para calcular el gradiente respecto a la entrada de g, multiplica la Jacobiana de g (transpuesta) por el gradiente respecto a la salida de g".

Aplica capa por capa:

\[ \nabla_x L = J_{L_1}^T \cdot J_{L_2}^T \cdots J_{L_n}^T \cdot \nabla_{y_n} L \]

leyendo de derecha a izquierda. Empieza con el gradiente en la salida (∇_y L, normalmente trivial — p. ej., para L = escalar, ∇_y L = 1). En cada capa, multiplica por la Jacobiana transpuesta de la capa. Continúa de vuelta hasta la entrada.

Este recorrido es lo que implementa cada framework de autograd. La Fase 7 lo programará directamente.

¿Por qué de derecha a izquierda?

Dos formas equivalentes de calcular el gradiente:

  1. Modo directo (izquierda a derecha): propaga Jacobianas hacia adelante, multiplicando (m × k) × (k × n) en cada paso. Para redes profundas con m = 1 (pérdida escalar) pero n enorme (millones de parámetros), cada paso es un gran producto matriz-matriz.

  2. Modo inverso (derecha a izquierda): empieza con el vector ∇_y L: (m,) = (1,) y propágalo hacia atrás. En cada paso, solo multiplicas matriz × vector, nunca matriz × matriz.

Para redes profundas donde m = 1 (una pérdida) y n enorme (parámetros del modelo), el modo inverso es enormemente más barato: O(parámetros) por pase hacia atrás vs O(parámetros²) para el modo directo.

A la inversa, si m es enorme (muchas salidas) y n pequeño (pocas entradas), gana el modo directo.

Para ML: siempre modo inverso. Esta es la razón de que "retropropagación" sea el nombre — propagas gradientes hacia atrás a través del grafo.

Trabajado: y = Wx + b luego L = ||y||²

Capas: L = f(y), y = g(x) = Wx + b.

  • f(y) = ||y||² = y^T y. ∇_y L = 2y. (Columna.)
  • g(x) = Wx + b. J_g(x) = W.

Por tanto:

\[ \nabla_x L = J_g^T \cdot \nabla_y L = W^T \cdot 2y = 2 W^T (W x + b) \]

Comprobación: expansión directa. L(x) = ||Wx + b||² = (Wx + b)^T (Wx + b). Gradiente directo: ∇_x L = 2 W^T (Wx + b). Misma respuesta.

El punto no es esta fórmula concreta; es que la regla de la cadena vía Jacobianas produce la respuesta correcta mecánicamente, sin re-derivar desde cero cada vez.

Trabajado: gradiente de cross-entropy a través de softmax

Esta es la derivación de gradiente más importante en la Fase 4. La haremos de tres formas: a mano, vía regla de la cadena, y como comprobación.

Planteamiento

  • Logits x ∈ R^V (V = tamaño del vocab).
  • Probabilidades p = softmax(x), así que p_i = e^{x_i} / Σ_j e^{x_j}.
  • Etiqueta verdadera y ∈ {0, ..., V-1}.
  • Pérdida L = -log p_y = -x_y + log Σ_j e^{x_j} (la forma CE estable de la Fase 2).

Queremos ∂L/∂x_i para cada i.

A mano

\[ \frac{\partial L}{\partial x_i} = \frac{\partial}{\partial x_i} \left( -x_y + \log \sum_j e^{x_j} \right) \]

Dos términos.

Primer término: ∂(-x_y)/∂x_i = -δ_{iy} (delta de Kronecker: 1 si i = y, sino 0).

Segundo término: ∂(log Σ_j e^{x_j}) / ∂x_i. Sea S = Σ_j e^{x_j}. Entonces log S se deriva como (1/S) · ∂S/∂x_i = (1/S) · e^{x_i} = p_i.

Combina:

\[ \frac{\partial L}{\partial x_i} = -\delta_{iy} + p_i = (p - \text{one\_hot}(y))_i \]

O como vector:

\[ \boxed{\nabla_x L = p - \text{one\_hot}(y)} \]

Esto colapsa a una sola línea. Sin log, sin exp, sin inversa — el cálculo intermedio engorroso se cancela.

Por qué es bello

Tres razones:

  1. Numérico: calcular softmax(x) - one_hot(y) es una resta. Sin riesgo de underflow / overflow más allá de lo que stable_softmax ya maneja. El autograd de la Fase 7 implementa esto directamente como el backward de cross-entropy.

  2. Computacional: si el pase hacia adelante del modelo calculó p = softmax(x), entonces el pase hacia atrás solo resta one_hot. Cero cómputo nuevo necesario. Por eso cada framework fusiona softmax + cross-entropy como una sola op.

  3. Pedagógico: la función de pérdida más usada en aprendizaje profundo tiene el gradiente más limpio posible. Hay una razón por la que esta es la pérdida que todos usan para clasificación — su cálculo es amable.

Vía la regla de la cadena

Por completitud, hagámosla como composición de dos Jacobianas (así vemos la cancelación explícita del matmul).

  • Capa interna: p = softmax(x). Jacobiana J_softmax(x) = diag(p) - p p^T (la derivarás en el lab 00).
  • Capa externa: L = -log(p_y). Gradiente ∇_p L = -(1/p_y) · e_y donde e_y es el vector base estándar. Así que (∇_p L)_i = -δ_{iy} / p_y.

Aplica regla de la cadena:

\[ \nabla_x L = J_{softmax}^T \cdot \nabla_p L = (\text{diag}(p) - p p^T) \cdot \left( -\frac{1}{p_y} e_y \right) \]

Calcula el producto matriz-vector:

  • diag(p) · (-e_y / p_y) = -e_y (la entrada e_y-ésima es p_y · (-1/p_y) = -1; las demás son 0).
  • p p^T · (-e_y / p_y) = -p_y · p / p_y = -p.

Por tanto:

\[ \nabla_x L = -e_y - (-p) = p - e_y = p - \text{one\_hot}(y) \]

Misma respuesta. La estructura diagonal-más-rango-uno de la Jacobiana intermedia se cancela exactamente en la resta one-hot.

Esto es lo que el código de autograd de la Fase 7 implementará implícitamente — es por lo que cross_entropy_with_logits existe como op fusionada en cada framework.

Comprobación vía diferencias finitas

En el lab 00 verificarás el resultado analítico con diferenciación numérica. Usa diferencias centradas:

\[ \frac{\partial L}{\partial x_i} \approx \frac{L(x + h e_i) - L(x - h e_i)}{2h} \]

con h = 1e-4. Las respuestas numérica y analítica deben coincidir hasta aproximadamente 1e-4 — limitado por el error de truncamiento .

Si ves un desacuerdo mayor que 1e-3, tu derivación analítica está mal. (No le eches la culpa al ruido numérico sin re-derivar.)

Trabajado: gradiente de (W_2 relu(W_1 x))

Una cadena de dos capas lineal-relu-lineal. No la derivaremos completamente aquí (el lab 01 lo hace), pero el recorrido de la regla de la cadena es:

  1. Forward:
  2. z_1 = W_1 x
  3. a_1 = relu(z_1)
  4. z_2 = W_2 a_1
  5. L = función escalar de z_2

  6. Backward (empieza con g_2 = ∇_{z_2} L):

  7. ∇_{a_1} L = W_2^T g_2 (Jacobiana de capa lineal = W^T).
  8. ∇_{z_1} L = ∇_{a_1} L * relu'(z_1) (elemento a elemento * porque la Jacobiana de relu es diagonal).
  9. ∇_x L = W_1^T · ∇_{z_1} L.

Los gradientes de los parámetros:

  • ∇_{W_2} L = g_2 · a_1^T (producto exterior — el gradiente es contribución de rango 1 por muestra).
  • ∇_{W_1} L = (∇_{z_1} L) · x^T.

Volverás a derivar esto en el lab 01 y verificarás con diferencias finitas.

Problemas de práctica

Soluciones en solutions/02-chain-rule-and-backprop-ref.md en la apertura de fase.

  1. Deriva ∂/∂x sigmoid(x). Demuestra que es igual a σ(x)(1 - σ(x)).
  2. Deriva J_softmax(x) para x de longitud n. Demuestra que es diag(p) - p p^T.
  3. Deriva ∂ L / ∂ W_1 para la MLP de 2 capas de arriba. Demuestra que la respuesta es el producto exterior g_1 · x^T donde g_1 = ∇_{z_1} L.
  4. La función "log-softmax" es log_softmax(x)_i = x_i - log_sum_exp(x). Deriva su Jacobiana. Demuestra que ∂ log_softmax(x)_i / ∂ x_j = δ_{ij} - p_j.
  5. ¿Por qué -log(softmax(x)_y) colapsa tan limpiamente mientras que softmax(x)_y por sí mismo no? (Pista: el log deshace uno de los exp.)
  6. Para la pérdida L = -log softmax(x)_y, verifica numéricamente (diferencias centradas, h = 1e-4) que ∇_x L = softmax(x) - one_hot(y) para un x específico de tu elección.

Recapitulación en un párrafo

La regla de la cadena para funciones vectoriales es multiplicación de matrices Jacobianas, aplicada de derecha a izquierda al calcular el gradiente de una pérdida escalar — el "modo inverso" de la diferenciación automática que implementa backprop. El gradiente más limpio en aprendizaje profundo es para cross-entropy a través de softmax: ∇_x L = softmax(x) - one_hot(y), derivable en tres líneas y calculado esencialmente gratis a partir del pase hacia adelante. Cada operación de autograd de la Fase 7 en adelante es solo un caso especial de "almacenar valores intermedios durante el forward; recorrer el grafo al revés, multiplicando por Jacobianas transpuestas". El trabajo de la Fase 4 es hacer esa derivación refleja; la Fase 7 la convierte en código.


Siguiente: theory/03-optimizers-derived.md.