Módulo 5 · Redes neuronales

Lección 7 de 7

Derivadas y gradientes

En esta páginaEl problema

El problema

Un modelo tiene un solo parámetro w y su pérdida es L(w) = (w − 3)². El mínimo está en w = 3. Tres ejecuciones empiezan en w = 0 y aplican la misma regla de descenso por gradiente. Solo cambia el learning rate η:

ηw tras 3 pasostras 20 pasosL tras 20 pasos
0.11.4642.9650.001
1.06.0000.0009.000
1.18.184−112.01313227.944

La primera ejecución se acerca a 3. La segunda salta entre 0 y 6 sin avanzar. La tercera se aleja cada vez más, aunque la regla es la misma.

Derivada como pendiente

La derivada de L en un punto mide cuánto cambia L por cada unidad que cambia w, cerca de ese punto. Una forma de aproximarla es la diferencia finita: se mueve w una distancia pequeña h, se mide el cambio de L y se divide entre h.

Pregunta. En w = 0, ¿el cociente es positivo o negativo? Al reducir h, ¿hacia qué número se acerca?

Ver resultado

La tabla da el cociente (L(0 + h) − L(0)) / h para cuatro valores de h: la primera columna es el desplazamiento y la segunda el cambio de L dividido entre ese desplazamiento. Por ejemplo, la fila con 0.1 y −5.9 dice que al pasar de w = 0 a w = 0.1 la pérdida baja de 9 a 8.41, un cambio de −0.59 que equivale a −5.9 por unidad de w.

hcociente
1.0−5.0
0.1−5.9
0.01−5.99
0.001−5.999
Lectura. El cociente se acerca a −6 cuando h se reduce. Ese límite es la derivada en w = 0. Para (w − 3)², la derivada exacta es L'(w) = 2(w − 3), que vale −6 en w = 0. El signo negativo indica que L baja si w aumenta un poco.

La diferencia centrada evalúa L a ambos lados de w: (L(w + h) − L(w − h)) / (2h). Con el mismo h suele quedar más cerca de la derivada exacta.

Pregunta. Escribe el signo de la pendiente en w = 0, 2, 3 y 5. ¿En cuál de esos puntos vale cero?

Python 3 · ejemplo reproducible
def perdida(w):
    return (w - 3) ** 2


def pendiente(f, w, h=1e-4):
    return (f(w + h) - f(w - h)) / (2 * h)


for w in [0.0, 2.0, 3.0, 5.0]:
    print(w, round(pendiente(perdida, w), 3), 2 * (w - 3))
Salida esperada

La salida esperada está oculta.

Gradiente en dos variables

Con dos parámetros, la pérdida es una superficie. Una derivada parcial mide la pendiente respecto a un parámetro mientras el otro queda fijo. El gradiente es el vector que reúne todas las derivadas parciales.

La superficie de esta sección es S(w₁, w₂) = w₁² + 3·w₂². Su mínimo está en (0, 0).

Pregunta. En el punto (2, 1), escribe el valor de S y las dos derivadas parciales. ¿Cuál es mayor?

Python 3 · ejemplo reproducible
import numpy as np


def superficie(p):
    return p[0] ** 2 + 3 * p[1] ** 2


def gradiente_numerico(f, p, h=1e-4):
    grad = np.zeros_like(p)
    for i in range(len(p)):
        paso = np.zeros_like(p)
        paso[i] = h
        grad[i] = (f(p + paso) - f(p - paso)) / (2 * h)
    return grad


punto = np.array([2.0, 1.0])
g = gradiente_numerico(superficie, punto)

print(superficie(punto))
print(np.round(g, 3))
Salida esperada

La salida esperada está oculta.

El gradiente también tiene una dirección. Desde (2, 1), el punto se mueve una distancia de 0.1 en cuatro direcciones: a lo largo de cada eje, en la dirección de g y en la contraria. En cada caso se mide el cambio de S.

Pregunta. ¿Qué dirección produce el mayor aumento de S? ¿Cuál produce la mayor disminución?

Ver resultado

La tabla da el cambio de S al mover el punto (2, 1) una distancia de 0.1 en cada dirección: la primera columna es la dirección y la segunda la diferencia S(nuevo punto) − S(2, 1). Por ejemplo, la fila eje w₂ con +0.630 dice que S sube de 7 a 7.630 al pasar a (2, 1.1).

direccióncambio de S
eje w₁+0.410
eje w₂+0.630
gradiente+0.745
contra el gradiente−0.697
Lectura. Entre las cuatro direcciones, la del gradiente sube más a S y la contraria la baja más. Esa propiedad vale en general: el gradiente apunta hacia el mayor aumento local y −g hacia la mayor disminución local. Por eso el descenso por gradiente resta el gradiente.

Regla de la cadena

Una neurona con activación sigmoide calcula su pérdida en tres etapas:

  • z = w·x + b
  • a = σ(z) = 1 / (1 + e^(−z))
  • L = (a − y)²

La regla de la cadena obtiene dL/dw como producto de las derivadas locales de cada etapa: dL/dw = dL/da · da/dz · dz/dw. Las derivadas locales son dL/da = 2(a − y), da/dz = a(1 − a) y dz/dw = x.

La celda plegada guarda los datos de la neurona (x, b, y y peso) y define sigmoide.

Mostrar códigoOcultar códigoPython
Python 3 · ejemplo reproducible
x, b, y = 1.5, 0.0, 1.0
peso = 0.8


def sigmoide(z):
    return 1 / (1 + np.exp(-z))

Pregunta. Con x = 1.5, b = 0, y = 1 y w = 0.8, escribe el signo de cada factor y el signo del producto. ¿Conviene aumentar o disminuir w?

Ver resultado

La tabla recorre la neurona en w = 0.8: la primera columna es una etapa o una derivada local y la segunda su valor. Por ejemplo, la fila dL/da = 2(a − y) con −0.463 dice que, cerca de a = 0.769, la pérdida cambia −0.463 por cada unidad que aumenta a.

cantidadvalor en w = 0.8
z = w·x + b1.2
a = σ(z)0.769
dL/da = 2(a − y)−0.463
da/dz = a(1 − a)0.178
dz/dw = x1.5
dL/dw, producto de los tres factores−0.1235
Lectura. dL/da es negativo porque la salida a queda por debajo de y. da/dz y dz/dw son positivos. El producto es negativo, así que L baja si w aumenta. El valor −0.1235 necesita los tres factores. El signo, en este ejemplo, lo decide solo dL/da: a(1 − a) siempre es positivo y x = 1.5 también.

Pregunta. ¿La diferencia centrada y autograd darán el mismo valor que el producto hasta el cuarto decimal?

Python 3 · ejemplo reproducible
import torch


def perdida_neurona(w):
    return (sigmoide(w * x + b) - y) ** 2


peso_t = torch.tensor(peso, requires_grad=True)
perdida_t = (torch.sigmoid(peso_t * x + b) - y) ** 2
perdida_t.backward()

print(round(pendiente(perdida_neurona, peso), 4))
print(round(peso_t.grad.item(), 4))
Salida esperada

La salida esperada está oculta.

Descenso por gradiente

El descenso por gradiente repite un paso: w ← w − η·L'(w). η es el learning rate y fija qué fracción de la derivada se resta en cada paso.

Pregunta. Con η = 0.1 y w = 0, calcula a mano w después del primer paso. ¿Los pasos siguientes serán más largos o más cortos?

Python 3 · ejemplo reproducible
def derivada(w):
    return 2 * (w - 3)


w = 0.0
eta = 0.1
for paso in range(1, 6):
    w = w - eta * derivada(w)
    print(paso, round(w, 3), round(perdida(w), 3))
Salida esperada

La salida esperada está oculta.

El comportamiento cambia con η. Para comparar varios valores, descenso guarda la trayectoria completa. La distancia al mínimo es w − 3.

Pregunta. Con w = 0, calcula el primer paso para η = 0.1, 0.9, 1.0 y 1.1. Divide la distancia al mínimo después del paso entre la distancia antes del paso. ¿En qué casos cambia de signo?

Python 3 · ejemplo reproducible
def descenso(eta, pasos, w=0.0):
    historial = [w]
    for _ in range(pasos):
        w = w - eta * derivada(w)
        historial.append(w)
    return np.array(historial)


for eta in [0.1, 0.9, 1.0, 1.1]:
    trayectoria = descenso(eta, 20)
    distancias = trayectoria - 3
    razon = distancias[1] / distancias[0]
    print(
        f"eta={eta}: razón={razon:+.1f}  "
        f"w3={trayectoria[3]:.3f}  w20={trayectoria[20]:.3f}  "
        f"L20={perdida(trayectoria[20]):.3f}"
    )
Salida esperada

La salida esperada está oculta.

  • con η = 0.1, la razón es 0.8: la distancia se reduce sin cambiar de lado
  • con η = 0.9, la razón es −0.8: w salta de un lado al otro del mínimo, pero la distancia se reduce
  • con η = 1.0, la razón es −1: w oscila entre 0 y 6 y la distancia nunca cambia
  • con η = 1.1, la razón es −1.2: cada salto es 20 % más largo y la pérdida crece sin límite

El descenso converge solo si |1 − 2η| < 1, es decir, si 0 < η < 1. Para una parábola c·(w − 3)² la razón es 1 − 2cη: cuanto más empinada es la curva, menor es el learning rate que todavía converge.

Momentum

Momentum acumula una velocidad v: cada paso suma la derivada actual a una fracción β de la velocidad anterior y mueve w según esa velocidad.

La regla es v ← β·v + L'(w) y w ← w − η·v. torch.optim.SGD con el argumento momentum=β aplica esta misma regla.

Pregunta. Con η = 0.01, ¿qué trayectoria llega antes cerca de w = 3, la de descenso simple o la de momentum con β = 0.9? ¿Alguna pasa de 3?

Python 3 · ejemplo reproducible
def con_momentum(eta, beta, pasos, w=0.0):
    historial = [w]
    v = 0.0
    for _ in range(pasos):
        v = beta * v + derivada(w)
        w = w - eta * v
        historial.append(w)
    return np.array(historial)


simple = descenso(0.01, 60)
momentum = con_momentum(0.01, 0.9, 60)
for paso in [10, 20, 30, 40, 50, 60]:
    print(paso, round(simple[paso], 3), round(momentum[paso], 3))

w_t = torch.tensor(0.0, requires_grad=True)
optimizador = torch.optim.SGD([w_t], lr=0.01, momentum=0.9)
for _ in range(60):
    optimizador.zero_grad()
    ((w_t - 3) ** 2).backward()
    optimizador.step()
print(round(w_t.item(), 3))
Salida esperada

La salida esperada está oculta.

Explorador

Learning rate en un valle

Da pasos con distintos learning rates y observa cuándo el punto baja al fondo del valle, cuándo lo cruza de un lado a otro y cuándo se sale.

Qué probar

Después activa momentum y compara su recorrido y su número de pasos con los del descenso simple.

w₁w₂

Arrastra el círculo hueco para cambiar el inicio.

Parámetros

La flecha naranja es −g, el gradiente con el signo cambiado: la dirección en la que la pérdida baja más rápido desde el punto actual. La ✕ marca el mínimo.

Inicio en (-2.70, -0.60) con pérdida 5.63. El gradiente mide 8.95; con η = 0.05, el primer paso avanza 0.45 en sentido contrario.

Práctica

Traza guiada

Antes de procesar, escribe df(1.0), el valor de w después de cada uno de los tres pasos y la razón entre distancias al mínimo w = −1.

Python 3 · ejemplo reproducible
def f(w):
    return (w + 1) ** 2


def df(w):
    return 2 * (w + 1)


w = 1.0
for paso in range(3):
    w = w - 0.25 * df(w)

Comprueba con print dentro del ciclo. Compara también df(1.0) con pendiente(f, 1.0). Si la razón no coincide con 1 − 2η, revisa que la distancia se mida respecto a −1 y no respecto a 0.

Parábola más empinada

Cambia la pérdida a 5·(w − 3)² y su derivada a 10·(w − 3). Antes de procesar, calcula la razón 1 − 10η para η = 0.05, 0.1, 0.15 y 0.25. Predice cuál converge sin cambiar de lado, cuál llega al mínimo en un solo paso, cuál oscila y converge, y cuál diverge. Después ejecuta 20 pasos desde w = 0 y comprueba con assert que la pérdida final es menor que 1e-6 en los tres primeros casos.

Descenso en dos variables

Aplica descenso por gradiente a superficie desde (2, 1). Escribe un cálculo que:

  1. use gradiente_numerico para el gradiente de cada paso
  2. ejecute 30 pasos con η = 0.1 e imprima S al final
  3. calcule la razón entre distancias al mínimo en cada coordenada después del primer paso
  4. encuentre un η con el que w₁ converge y w₂ diverge
  5. compruebe con assert que la razón de cada coordenada coincide con 1 − 2η y 1 − 6η con tolerancia 1e-6

Reto opcional

Con η = 0.01 y 60 pasos, ejecuta con_momentum con β = 0.5, 0.7 y 0.9. Antes de procesar, predice cuál tiene el máximo más alto de la trayectoria y cuál termina más cerca de 3. Registra trayectoria.max() y el valor final de cada una.

Errores que revisar

  • Si w se aleja del mínimo desde el primer paso sin cambiar de lado, revisa el signo de la regla. El paso resta η·L'(w). Sumarlo sube la pérdida.
  • Si w cambia de lado en cada paso y la pérdida crece, η es demasiado grande para la curvatura. Calcula la razón 1 − 2cη o reduce η a la mitad y repite.
  • Si una diferencia finita no coincide con la derivada a mano, usa la diferencia centrada con h = 1e-4. Un h como 1e-12 también falla: la resta de dos números casi iguales pierde dígitos.
  • Si al derivar una neurona a mano el producto de los factores no coincide con peso_t.grad, revisa que no falte una derivada interna, como x o a(1 − a).
  • Si la trayectoria con momentum pasa del mínimo, revisa el valor de β antes que el código. La velocidad acumulada produce ese salto aunque la regla esté bien escrita.

Cierre

Usa la razón 1 − 2η para explicar por qué, en L(w) = (w − 3)², η = 0.1 converge, η = 1.0 oscila sin avanzar y η = 1.1 diverge.

Lista de comprobación

  • ¿Qué indican el signo y la magnitud de la derivada en un punto?
  • ¿Por qué el paso de descenso va en sentido contrario al gradiente?
  • ¿Qué tres derivadas locales se multiplican para obtener dL/dw en la neurona sigmoide?
  • ¿Cómo cambia el mayor learning rate que converge cuando la parábola es más empinada?
  • ¿Qué gana y qué riesgo añade momentum en el ejemplo de η = 0.01?

Documentación oficial

Cargando progreso…