Saltar al contenido
3/30Capítulo 3 de 30

Cuesta abajo: Gradient Descent y los dos pasos que todo el mundo se salta

Calcula el techo exacto de una tasa de aprendizaje y observa cómo una búsqueda de fuerza bruta en 3600 direcciones redescubre el gradiente.

En esta página

El capítulo anterior terminó con un valle.

No uno metafórico: una curva real, la pérdida representada frente a un único parámetro, bajando y volviendo a subir. Y la pérdida bajo ella no se escogió porque quedara limpia: se derivó a partir de una afirmación sobre el ruido en las mediciones, y el error cuadrático apareció al final como una consecuencia, no como una convención.

Así que tenemos un paisaje con un fondo, y una razón para creer que el fondo es el lugar correcto. Lo que no tenemos es una forma de llegar hasta él.

Este capítulo construye una, y es el algoritmo que entrena todos los modelos del resto de este curso: todos, sin excepción, hasta incluir los que tienen cientos de miles de millones de parámetros. Cabe en unas veinte líneas. Las dos partes difíciles no están en esas veinte líneas, y son las dos cosas que casi todas las explicaciones se saltan:

  • Por qué el signo menos. La actualización resta el gradiente. Todos los tutoriales lo escriben; muy pocos dicen por qué el gradiente es la dirección que va hacia arriba, que es el único hecho que convierte el signo menos en algo distinto de un acto de fe.
  • Qué tamaño de paso. «Demasiado grande diverge, demasiado pequeño es lento» es verdad e inútil. Hay un número exacto, se puede calcular a partir de la pérdida, y este capítulo lo calcula dos veces: una para una parábola de juguete y otra para los datos reales.

El planteamiento, y por qué no puedes limitarte a buscar

Enlace a la sección: El planteamiento, y por qué no puedes limitarte a buscar

Replanteado para que este capítulo se sostenga por sí solo: las ocho piezas de la cinta transportadora del Capítulo 1, pero con otra pregunta. No aceptar o rechazar —eso volverá más adelante—, sino predecir el peso de una pieza a partir de su anchura.

belt.pyPYTHON
import numpy as np

WIDTH  = np.array([18.0, 19.5, 20.2, 21.0, 24.0, 25.5, 23.0, 26.0])
WEIGHT = np.array([47.0, 52.0, 49.0, 55.0, 61.0, 66.0, 70.0, 58.0])

x = WIDTH  - WIDTH.mean()      # 22.15 mm
y = WEIGHT - WEIGHT.mean()     # 57.25 g

Las mediciones están centradas, exactamente igual que en el Capítulo 1 y por una razón que volverá con intereses antes de que termine este capítulo. El modelo es una recta, y^=ax+b\hat{y} = a x + b, y la pérdida es el error cuadrático medio que derivó el capítulo anterior:

L(a,b)=1ni=1n(axi+byi)2L(a, b) = \frac{1}{n} \sum_{i=1}^{n} \left(a x_i + b - y_i\right)^2

Dos parámetros. ¿Por qué no probar simplemente muchos valores? Hagámoslo de verdad: una cuadrícula de a=0a = 0 a 55 y de b=5b = -5 a 55, con pasos de 0.010.01:

TEXT
grid 501 x 1001 = 501,501 evaluations in 3.67 s
  best found: a = 2.1000, b = -0.0000, L = 24.592450

Medio millón de evaluaciones para fijar dos números con dos decimales; y ese segundo es tiempo de reloj en una máquina, así que al repetirlo puede caer entre tres y seis. El recuento de evaluaciones y el mínimo son la parte que se reproduce. Gradient descent, al final de este capítulo, obtiene cuatro decimales en ocho pasos y la respuesta float64 completa en treinta y seis.

Pero la velocidad no es el argumento, y este es el punto que decide todo el curso. La búsqueda en cuadrícula cuesta kPk^P evaluaciones para PP parámetros con kk valores cada uno. Con mil valores por eje:

modeloparámetrosevaluaciones en cuadrícula
esta recta210610^{6}
la red XOR del Capítulo 59102710^{27}
una red multicapa pequeña20.0001060,00010^{60{,}000}

La tercera fila no es un número grande, es un número sin sentido: hay aproximadamente 108010^{80} átomos en el universo observable. La búsqueda no se vuelve más lenta cuando los modelos crecen; deja de existir. Todo lo que sigue existe por esa tabla.

Una derivada es una medición que puedes tomar

Enlace a la sección: Una derivada es una medición que puedes tomar

Fija b=0b = 0 por un momento para que haya un parámetro y una curva, que es la imagen con la que te dejó el capítulo anterior. Toma un punto sobre ella, a=1a = 1, y pregunta: si empujo aa una pequeña cantidad hh, ¿cuánto se mueve la pérdida, por unidad de empuje?

L(a+h)L(a)h\frac{L(a + h) - L(a)}{h}

Esa razón es una subida sobre avance: la pendiente de la recta que pasa por dos puntos de la curva. A medida que hh se encoge, los dos puntos se deslizan hasta juntarse y la recta se convierte en la tangente. Su pendiente es la derivada L(a)L'(a): la tasa a la que cambia la pérdida por unidad de cambio en aa. No es una aproximación de nada, ni una cantidad infinitamente pequeña. Es un límite de razones ordinarias.

Merece la pena ejecutarlo, porque los números dicen algo que la definición no dice:

slope.pyPYTHON
def loss1(a):
    return np.mean((a * x - y) ** 2)

for h in [1.0, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14]:
    q = (loss1(1.0 + h) - loss1(1.0)) / h
    print(f"h = {h:<8.0e}   slope estimate = {q:.10f}   error = {abs(q + 16.385):.3e}")
TEXT
h = 1e+00      slope estimate = -8.9400000000    error = 7.445e+00
h = 1e-02      slope estimate = -16.3105500000   error = 7.445e-02
h = 1e-04      slope estimate = -16.3842555001   error = 7.445e-04
h = 1e-06      slope estimate = -16.3849925556   error = 7.444e-06
h = 1e-08      slope estimate = -16.3850003787   error = 3.787e-07
h = 1e-10      slope estimate = -16.3850444324   error = 4.443e-05
h = 1e-12      slope estimate = -16.3851154866   error = 1.155e-04
h = 1e-14      slope estimate = -17.0530256582   error = 6.680e-01

Aquí pasan dos cosas, y ambas sostienen el edificio.

El error no es vagamente proporcional a hh: es exactamente 7.445h7.445\,h. Divide hh entre cien y el error se divide entre cien, con cuatro cifras significativas cada vez. Esa constante no es decoración: es la mitad de la segunda derivada de la pérdida, y es la primera aparición de una idea que llegará dentro de dos secciones: que una curva cerca de un punto se parece a una recta más una corrección proporcional a h2h^2.

Y entonces el patrón se rompe. Por debajo de h=108h = 10^{-8} la estimación empeora, y en 101410^{-14} ya se equivoca en el segundo dígito. No ha ocurrido nada matemático; ha ocurrido la caja de coma flotante del capítulo anterior. L(a+h)L(a+h) y L(a)L(a) coinciden en sus diez primeros dígitos, restarlos destruye esos dígitos, y dividir los restos por un número diminuto amplifica lo que queda. Hay un mejor hh: aquí alrededor de 10810^{-8}, aproximadamente la raíz cuadrada del épsilon de la máquina. Ir a un valor más pequeño no es tener más cuidado, es tener menos. Recuérdalo; una función al final de este capítulo depende de ello.

La pendiente exacta, por cálculo y no por medición, es 16.385-16.385. Así que podemos dejar de medir y empezar a derivar.

Esta es la idea sobre la que se construye el resto del curso, expresada una vez y de forma clara.

Componer dos funciones es alimentar una con la otra: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Nada más.

Una red profunda no es como una composición. Es una composición. Una capa es una función; apilar capas es componerlas; la «profundidad» es el número de funciones en la cadena. Cuando el Capítulo 5 construya una red, estará construyendo f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 y nada más. Lo que significa que la regla de cálculo más importante para nuestros fines es la que diferencia una composición:

ddxf(g(x))=f(g(x))g(x)\frac{d}{dx} f(g(x)) = f'(g(x)) \cdot g'(x)

Las tasas se multiplican. Si gg cambia tres veces más rápido que xx, y ff cambia el doble de rápido que gg, entonces ff cambia seis veces más rápido que xx. Ese es todo el contenido, y es la razón por la que una señal que vuelve atravesando diez capas se multiplica por diez números; por eso el Capítulo 6 dedica una sección a lo que ocurre cuando todos esos números son ligeramente menores que uno.

Úsala sobre nuestra pérdida. Escribe el residual ri=axi+byir_i = a x_i + b - y_i, de modo que L=1nri2L = \frac{1}{n}\sum r_i^2. Cada rir_i depende de aa a través de la función interna axia x_i, cuya derivada es xix_i. Regla de la cadena, término a término:

La=1ni2rixi,Lb=1ni2ri1\frac{\partial L}{\partial a} = \frac{1}{n}\sum_i 2 r_i \cdot x_i, \qquad \frac{\partial L}{\partial b} = \frac{1}{n}\sum_i 2 r_i \cdot 1

Esos símbolos curvos \partial marcan una derivada parcial: diferencias respecto a una variable y tratas todas las demás como constantes. No pasa nada nuevo: es el mismo límite de antes, tomado a lo largo de un eje. Junta las parciales en un vector y tienes el gradiente:

L=(La, Lb)\nabla L = \left( \frac{\partial L}{\partial a},\ \frac{\partial L}{\partial b} \right)

En el punto (a,b)=(1,4)(a, b) = (1, 4), ese vector es (16.385, 8.0)(-16.385,\ 8.0). Dos números. La pregunta es qué significan, y este es el primer paso que todo el mundo se salta.

El gradiente es un vector de pendientes a lo largo de los ejes. Eso es todo lo que hemos demostrado. No es obvio —no debería serlo— que ensamblarlas en un vector produzca algo que apunte a algún sitio en particular.

Así que definamos lo que realmente queremos. Elige un vector unitario u\mathbf{u}, una dirección. La derivada direccional es la tasa a la que cambia la pérdida cuando caminas en esa dirección:

DuL=limh0L(θ+hu)L(θ)hD_{\mathbf{u}} L = \lim_{h \to 0} \frac{L(\boldsymbol{\theta} + h\mathbf{u}) - L(\boldsymbol{\theta})}{h}

La regla de la cadena convierte esto en algo calculable. Caminar a lo largo de u\mathbf{u} cambia aa a una tasa u1u_1 y bb a una tasa u2u_2, y las contribuciones se suman:

DuL=Lau1+Lbu2=LuD_{\mathbf{u}} L = \frac{\partial L}{\partial a} u_1 + \frac{\partial L}{\partial b} u_2 = \nabla L \cdot \mathbf{u}

La tasa de cambio en cualquier dirección es el producto escalar del gradiente con esa dirección. Y ahora el remate, que es una línea de geometría. Escribiendo el producto escalar con el ángulo ϕ\phi entre los vectores,

Lu=Lucosϕ=Lcosϕ\nabla L \cdot \mathbf{u} = \lVert \nabla L \rVert \, \lVert \mathbf{u} \rVert \cos\phi = \lVert \nabla L \rVert \cos\phi

puesto que u\mathbf{u} tiene longitud 1. Lo único que controlas es cosϕ\cos\phi, que es máximo en ϕ=0\phi = 0 y mínimo a media vuelta, ϕ=180\phi = 180 grados. Así que:

  • El ascenso más empinado es a lo largo de L\nabla L, y la pendiente ahí es exactamente L\lVert \nabla L \rVert.
  • El descenso más empinado es a lo largo de L-\nabla L, y la pendiente ahí es L-\lVert \nabla L \rVert.
  • Perpendicular al gradiente, la pérdida no cambia en absoluto. Por eso las líneas de un mapa de contorno cruzan el gradiente en ángulo recto.

Ese es el signo menos. No es una convención, ni un cambio de signo que alguien eligió: la dirección de descenso más rápido es el gradiente negativo porque cosϕ\cos\phi se minimiza a media vuelta, y por ninguna otra razón.

Como es una afirmación sobre todas las direcciones, pruébala contra todas las direcciones. Muestrea 3.600, una por cada décima de grado, y mide cada una con un pequeño empujón:

directions.pyPYTHON
theta = np.array([1.0, 4.0])
g = grad(theta)
print("gradient      ", g)
print("its length    ", np.linalg.norm(g))
print("its angle     ", np.degrees(np.arctan2(g[1], g[0])) % 360, "degrees")

best = max(
    ((loss(theta + 1e-6 * u) - loss(theta - 1e-6 * u)) / 2e-6, np.degrees(ang))
    for ang, u in (
        (a, np.array([np.cos(a), np.sin(a)])) for a in np.arange(3600) * 2 * np.pi / 3600
    )
)
print("steepest slope", best[0], "at", best[1], "degrees")
TEXT
gradient       [-16.385   8.   ]
its length     18.23371122399386
its angle      153.97598928042032 degrees
steepest slope 18.233709624837502 at 154.0 degrees

Una búsqueda que no sabe nada de gradientes, sobre 3.600 direcciones, encuentra su subida más empinada en 154,0 grados: la propia dirección del gradiente, dentro de la resolución de 0,1 grados de la búsqueda. Y la pendiente que encuentra ahí, 18,2337, es la longitud del gradiente con seis cifras. El teorema no es una historia sobre lo que significan los gradientes; es un hecho medible, y esa es la medición.

Por qué un pequeño paso cuesta abajo ayuda de verdad

Enlace a la sección: Por qué un pequeño paso cuesta abajo ayuda de verdad

Ahora el segundo paso que se suele saltar. Sabemos qué dirección es hacia abajo. De ahí no se deduce que caminar en esa dirección reduzca la pérdida, porque «hacia abajo» es una afirmación sobre un empujón infinitesimal y un paso no es infinitesimal.

El puente es la linealización. Cerca de un punto, una función suave es su tangente más una corrección:

L(θ+δ)=L(θ)+Lδ+O(δ2)L(\boldsymbol{\theta} + \boldsymbol{\delta}) = L(\boldsymbol{\theta}) + \nabla L \cdot \boldsymbol{\delta} + O(\lVert\boldsymbol{\delta}\rVert^2)

Esa es la expansión de Taylor de primer orden. El O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) descartado es la curvatura: el mismo término que hacía que la estimación de la tabla de pendientes fallara exactamente por 7.445h7.445\,h. Introduce el paso que pretendemos dar, δ=ηL\boldsymbol{\delta} = -\eta \nabla L:

L(θηL)L(θ)ηL2L(\boldsymbol{\theta} - \eta \nabla L) \approx L(\boldsymbol{\theta}) - \eta \lVert \nabla L \rVert^2

La pérdida baja en ηL2\eta \lVert \nabla L \rVert^2. Cada parte de eso es no negativa, así que la promesa es real: para un η\eta lo bastante pequeño, porque el término ignorado crece como η2\eta^2 y al final se lo come. Esa es toda la teoría. Aquí está la promesa cumpliéndose, y después rompiéndose:

TEXT
eta = 0.2       promised    66.49364500   delivered   -16.01619240   ratio -0.240868
eta = 0.1       promised    33.24682250   delivered    12.61936315   ratio  0.379566
eta = 0.01      promised     3.32468225   delivered     3.11840766   ratio  0.937957
eta = 0.001     promised     0.33246822   delivered     0.33040548   ratio  0.993796
eta = 0.0001    promised     0.03324682   delivered     0.03322620   ratio  0.999380
eta = 1e-05     promised     0.00332468   delivered     0.00332448   ratio  0.999938

Léelo desde abajo. A medida que η\eta se encoge, la bajada entregada converge a la prometida —ratio 0,99938, luego 0,99994—, que es el teorema de Taylor acertando. Léelo desde arriba y en η=0.2\eta = 0.2 la «bajada» entregada es menos dieciséis. El paso fue cuesta abajo y la pérdida subió.

Así que la regla de actualización es

θθηL(θ)\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta \nabla L(\boldsymbol{\theta})

y viene con una condición que nadie enuncia: que η\eta sea lo bastante pequeño. Lo bastante pequeño comparado con qué, exactamente, es la siguiente sección.

La tasa de aprendizaje tiene un techo, y se puede calcular

Enlace a la sección: La tasa de aprendizaje tiene un techo, y se puede calcular

Empieza con el valle más simple que existe, f(x)=x2f(x) = x^2, donde f(x)=2xf'(x) = 2x. Un paso de gradient descent es

xxη2x=x(12η)x \leftarrow x - \eta \cdot 2x = x\,(1 - 2\eta)

La posición se multiplica por (12η)(1 - 2\eta) en cada paso. Eso es una sucesión geométrica, y las sucesiones geométricas tienen exactamente una regla: se encogen cuando el multiplicador es menor que 1 en valor absoluto y crecen en caso contrario. Así que 12η<1\lvert 1 - 2\eta \rvert < 1, que es 0<η<10 < \eta < 1.

La frontera está exactamente en η=1\eta = 1. No «alrededor de 1», ni «1 suele ser demasiado grande». En η=1\eta = 1 el multiplicador es 1-1 y el punto rebota entre xx y x-x para siempre, sin acercarse ni escapar. Por debajo, converge; por encima, diverge. El intervalo vuelve a dividirse en η=0.5\eta = 0.5, donde el multiplicador cambia de signo: por debajo de eso la aproximación es monótona, por encima el punto se pasa y alterna lados, y exactamente en 0.50.5 el multiplicador es 0 y un único paso aterriza en el mínimo.

Cuatro regímenes, a partir de cuatro líneas de álgebra. Ve y cruza tú mismo las fronteras:

14 pasos, termina en x = -0.0836.

Ver los datos en una tabla
Pasoxf(x)
0⁨-1.9000⁩⁨3.6100⁩
1⁨-1.5200⁩⁨2.3104⁩
2⁨-1.2160⁩⁨1.4787⁩
3⁨-0.9728⁩⁨0.9463⁩
4⁨-0.7782⁩⁨0.6057⁩
5⁨-0.6226⁩⁨0.3876⁩
6⁨-0.4981⁩⁨0.2481⁩
7⁨-0.3985⁩⁨0.1588⁩
8⁨-0.3188⁩⁨0.1016⁩
9⁨-0.2550⁩⁨0.0650⁩
10⁨-0.2040⁩⁨0.0416⁩
11⁨-0.1632⁩⁨0.0266⁩
12⁨-0.1306⁩⁨0.0170⁩
13⁨-0.1045⁩⁨0.0109⁩
14⁨-0.0836⁩⁨0.0070⁩
Descenso de gradiente interactivo

Catorce pasos con una tasa de 0,1, desde x=1.9x = -1.9, terminando en 0.0836-0.0836. Sube la tasa a 0,5 y el primer paso cae en el fondo. Súbela a 0,9 y termina en el mismo 0.0836-0.0836 que con 0,1 —misma distancia, estilo opuesto, porque 12η\lvert 1 - 2\eta \rvert es 0,8 para ambos—, pero llega zigzagueando de un lado al otro del valle en vez de bajar por un solo lado.

Y ahora el caso interesante:

14 pasos, termina en x = -1.9000.

Ver los datos en una tabla
Pasoxf(x)
0⁨-1.9000⁩⁨3.6100⁩
1⁨1.9000⁩⁨3.6100⁩
2⁨-1.9000⁩⁨3.6100⁩
3⁨1.9000⁩⁨3.6100⁩
4⁨-1.9000⁩⁨3.6100⁩
5⁨1.9000⁩⁨3.6100⁩
6⁨-1.9000⁩⁨3.6100⁩
7⁨1.9000⁩⁨3.6100⁩
8⁨-1.9000⁩⁨3.6100⁩
9⁨1.9000⁩⁨3.6100⁩
10⁨-1.9000⁩⁨3.6100⁩
11⁨1.9000⁩⁨3.6100⁩
12⁨-1.9000⁩⁨3.6100⁩
13⁨1.9000⁩⁨3.6100⁩
14⁨-1.9000⁩⁨3.6100⁩
Descenso de gradiente interactivo

Exactamente en la frontera. Catorce pasos con una tasa de 1, y acaba en 1.9-1.9: exactamente donde empezó, sin haber hecho nada salvo rebotar. Un empujón más arriba y los rebotes crecen en vez de mantenerse; con 1,2 se sale de la gráfica en cuatro pasos. Una tasa demasiado grande no converge lentamente. No converge.

Ahora la regla general, que sale del mismo argumento. El multiplicador 12η1 - 2\eta era en realidad 1ηf1 - \eta f'', y cerca de un mínimo una pérdida con varios parámetros tiene uno de esos números por dirección: los autovalores de la matriz de segundas derivadas. Todas las direcciones tienen que ser estables a la vez, así que el techo lo fija la mayor:

η<2λmax\eta < \frac{2}{\lambda_{\max}}

Para f(x)=x2f(x) = x^2, f=2f'' = 2, techo 1, que es lo que acabamos de derivar. Para nuestra cinta, la matriz de segundas derivadas es 2nAA\frac{2}{n} A^{\top} A con AA como matriz de entradas de dos columnas, y sus autovalores son 2 y 14,89, así que el techo es 2/14.89=0.134322 / 14.89 = 0.13432. Es una predicción con cinco cifras significativas. Pruébala:

TEXT
lr=0.1343    -> L =      24.5924
lr=0.13431   -> L =      24.5924
lr=0.13432   -> L =       4707.8       BLEW UP
lr=0.13433   -> L =  4.00452e+16       BLEW UP
lr=0.1344    -> L = 1.18229e+107       BLEW UP

Cinco decimales de acuerdo entre una línea de álgebra lineal y cien mil iteraciones de un bucle for.

Y aquí es donde vuelve el Capítulo 1. Todo lo anterior usó las mediciones centradas. Ejecuta el mismo código sobre milímetros y gramos sin procesar y los autovalores son 0,0298 y 998,1 en lugar de 2 y 14,89. El techo se desploma de 0,134 a 0,002004: con la misma exactitud, convergiendo en lr=0.002003 y explotando en lr=0.002004.

Peor que el techo es la razón entre los autovalores. El número de condición mide lo lejos que está el valle de ser redondo: una zanja larga y estrecha obliga a usar una tasa lo bastante pequeña para las paredes empinadas, y entonces el suelo de la zanja se recorre con ese mismo paso de tortuga. El nuestro pasa de 7,44 centrado a 33.452 sin centrar. Con la mejor tasa que puede usar cada versión:

variablesnúmero de condiciónmejor tasapasos hasta estar a un 1% del óptimo
centradas7,440,118410
milímetros y gramos sin centrar33.4520,002003779.513

Mismos datos, mismo código, misma respuesta al final; y ocho mil veces más trabajo, porque nadie restó una media. En el Capítulo 1, la misma omisión le costó al perceptrón un factor de seis mil en épocas, y el diagnóstico allí era geométrico: los datos flotaban lejos del origen. Es la misma geometría aquí con disfraz de optimización, y por eso la normalización de entradas no es un consejo de higiene, sino aritmética.1

Nada de lo anterior necesitaba una librería. Aquí está el optimizador entero.

descent.pyPYTHON
def loss(theta):
    a, b = theta
    return np.mean((a * x + b - y) ** 2)


def grad(theta):
    a, b = theta
    residual = a * x + b - y
    return np.array([np.mean(2 * residual * x), np.mean(2 * residual)])


def descend(theta, lr, steps):
    theta = np.array(theta, dtype=float)
    for _ in range(steps):
        theta = theta - lr * grad(theta)   
    return theta


theta = descend([0.0, 0.0], lr=0.05, steps=60)
print(theta, loss(theta))
TEXT
[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984

La respuesta de mínimos cuadrados en forma cerrada para estos ocho puntos es a=2.100403a = 2.100403, b=0b = 0, con una pérdida de 24.59244924.592449. El bucle la encontró con ocho cifras significativas sin saber que existe una forma cerrada; y eso importa, porque desde el Capítulo 5 en adelante no habrá una.

La trayectoria, porque verla es el punto:

TEXT
   0 a=0.000000 b=0.000000 L=57.437500
   1 a=1.563750 b=0.000000 L=26.736582
   2 a=1.963288 b=-0.000000 L=24.732418
   5 a=2.098116 b=-0.000000 L=24.592488
  10 a=2.100400 b=-0.000000 L=24.592449
  60 a=2.100403 b=-0.000000 L=24.592449

La mayor parte de la distancia se recorre en los dos primeros pasos, porque el gradiente es mayor cuando estás más lejos del fondo y se encoge al acercarte. Gradient descent se ralentiza automáticamente cerca de un mínimo. Eso es una ventaja y también, en el Capítulo 6, un problema.

El argumento hasta ahora tiene un agujero. El paso se detiene cuando L=0\nabla L = \mathbf{0}, y hemos estado llamando a eso «el mínimo». Un punto con gradiente cero es un punto crítico, y ser un mínimo es solo una de las formas de serlo:

  • un mínimo local: subida en todas las direcciones, aunque quizá no sea el punto de ese tipo más bajo en ninguna parte;
  • un máximo local: bajada en todas las direcciones;
  • un punto de silla: subida en algunas direcciones y bajada en otras. La superficie f(x,y)=x2y2f(x,y) = x^2 - y^2 tiene f=(2x,2y)\nabla f = (2x, -2y), que es cero en el origen, donde la función es un mínimo a lo largo del eje xx y un máximo a lo largo del eje yy al mismo tiempo.

Gradient descent no puede distinguirlos, porque solo mira el gradiente, y el gradiente es cero en los tres.

Nuestra recta tiene un único punto crítico y es la respuesta: una pérdida de error cuadrático sobre un modelo lineal es convexa, un único cuenco, y el descenso sobre ella no puede fallar al encontrar el mínimo global. Esa propiedad no sobrevive al contacto con este curso. La pérdida de una red neuronal no es convexa, y desde el Capítulo 5 en adelante «el mínimo» no es una cosa que exista: hay muchos, de distintas profundidades, y el que obtengas depende de dónde empezaste. Eso es una frase y se queda en una frase, porque la teoría es grande y la consecuencia práctica es pequeña.

Puedes ver toda la consecuencia en una sola curva. Toma f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, que tiene dos valles de distintas profundidades:

TEXT
   x =  -1.046681   f(x) =  -0.352386   minimum
   x =   0.101031   f(x) =   0.005026   maximum
   x =   0.945649   f(x) =  -0.152639   minimum

40 pasos, termina en x = 0.9456.

Ver los datos en una tabla
Pasoxf(x)
0⁨0.1100⁩⁨0.0050⁩
1⁨0.1122⁩⁨0.0050⁩
2⁨0.1149⁩⁨0.0049⁩
3⁨0.1182⁩⁨0.0049⁩
4⁨0.1223⁩⁨0.0048⁩
5⁨0.1275⁩⁨0.0047⁩
6⁨0.1338⁩⁨0.0045⁩
7⁨0.1416⁩⁨0.0042⁩
8⁨0.1513⁩⁨0.0038⁩
9⁨0.1633⁩⁨0.0032⁩
10⁨0.1781⁩⁨0.0022⁩
11⁨0.1962⁩⁨0.0007⁩
12⁨0.2183⁩⁨-0.0014⁩
13⁨0.2453⁩⁨-0.0046⁩
14⁨0.2779⁩⁨-0.0093⁩
15⁨0.3170⁩⁨-0.0160⁩
16⁨0.3633⁩⁨-0.0253⁩
17⁨0.4172⁩⁨-0.0377⁩
18⁨0.4783⁩⁨-0.0535⁩
19⁨0.5455⁩⁨-0.0721⁩
20⁨0.6163⁩⁨-0.0922⁩
21⁨0.6869⁩⁨-0.1116⁩
22⁨0.7526⁩⁨-0.1277⁩
23⁨0.8092⁩⁨-0.1393⁩
24⁨0.8540⁩⁨-0.1463⁩
25⁨0.8868⁩⁨-0.1499⁩
26⁨0.9091⁩⁨-0.1516⁩
27⁨0.9236⁩⁨-0.1522⁩
28⁨0.9325⁩⁨-0.1525⁩
29⁨0.9379⁩⁨-0.1526⁩
30⁨0.9411⁩⁨-0.1526⁩
31⁨0.9430⁩⁨-0.1526⁩
32⁨0.9441⁩⁨-0.1526⁩
33⁨0.9448⁩⁨-0.1526⁩
34⁨0.9451⁩⁨-0.1526⁩
35⁨0.9454⁩⁨-0.1526⁩
36⁨0.9455⁩⁨-0.1526⁩
37⁨0.9455⁩⁨-0.1526⁩
38⁨0.9456⁩⁨-0.1526⁩
39⁨0.9456⁩⁨-0.1526⁩
40⁨0.9456⁩⁨-0.1526⁩
Descenso de gradiente interactivo

Cuarenta pasos desde x=0.11x = 0.11, asentándose en 0.94560.9456: el más somero de los dos valles. Ahora mueve el punto de partida una muesca a la izquierda, hasta 0.100.10. Misma tasa, mismos cuarenta pasos, y se asienta en 1.0461-1.0461 en su lugar, donde la pérdida es 0,199747 menor. La divisoria es la loma en 0.1010310.101031, y toda la diferencia entre las dos respuestas es en qué lado de ella te tocó empezar.

Caer en el valle somero es un 56,7% peor en pérdida, y el algoritmo no tiene forma de saberlo, porque desde dentro de un valle todas las direcciones son cuesta arriba. No hay reparación para esto en gradient descent y no va a llegar ninguna. Lo que hay, en la práctica, es el hallazgo de que importa mucho menos de lo que sugiere esta imagen: en las altísimas dimensiones de una red real, la mayoría de los puntos críticos resultan ser sillas y no trampas,2 y el Capítulo 5 mide con qué frecuencia una red pequeña se queda realmente atascada.

Pasos más baratos: estocástico, minibatch, momentum

Enlace a la sección: Pasos más baratos: estocástico, minibatch, momentum

Hay algo en grad arriba que debería molestarte: suma todo el dataset para cada paso. Ocho piezas no son nada. Un millón son un millón de cálculos de gradiente para mover los parámetros una vez.

La vía de escape es que el gradiente es una media, y una media puede estimarse a partir de una muestra. Calcúlalo sobre un puñado aleatorio —un minibatch— y da el paso con eso. La estimación tiene ruido; también es insesgada, y cientos de pasos ruidosos y baratos vencen a uno exacto y caro. Sobre cien mil piezas sintéticas, contando gradientes por ejemplo en vez de pasos:

métodopasos hasta estar a un 0,1% del óptimogradientes por ejemplo
batch completo7700.000
minibatch de 321003.200
un ejemplo cada vez17.58017.580

Doscientas diecinueve veces menos aritmética para llegar al mismo sitio. Y el extremo —un ejemplo cada vez, la aproximación estocástica original de Robbins y Monro3no es el ganador: es cinco veces peor que batches de 32, porque 32 ejemplos no cuestan casi nada más que uno en hardware que multiplica matrices, mientras que el ruido cae con la raíz cuadrada del tamaño del batch. Ese intercambio es la razón por la que todo script de entrenamiento que leas tendrá un batch_size dentro.

Momentum es el otro arreglo barato, y apunta directamente a la zanja. En un valle mal condicionado, los pasos zigzaguean a través de la dirección estrecha mientras avanzan a rastras por la larga. Momentum mantiene una media móvil de gradientes pasados, de modo que los componentes oscilantes se cancelan y el componente consistente se acumula:4

vβv+L(θ),θθηv\mathbf{v} \leftarrow \beta \mathbf{v} + \nabla L(\boldsymbol{\theta}), \qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta \mathbf{v}

Dos líneas extra. Sobre la cinta sin centrar —número de condición 33.452, el peor caso que tenemos—, con la mejor tasa que puede usar el descenso simple:

TEXT
momentum beta=0.0   ->    79,513 steps to 1%
momentum beta=0.9   ->     1,609 steps to 1%
momentum beta=0.99  ->       461 steps to 1%

Un factor de 172 por dos líneas de código. El Capítulo 6 convierte esto en Adam; el mecanismo ya está aquí.

La comprobación que necesitarás en el Capítulo 5

Enlace a la sección: La comprobación que necesitarás en el Capítulo 5

Todos los gradientes de este capítulo se derivaron a mano y, por tanto, podían estar mal. La solución es la tabla de pendientes del principio: medir la derivada numéricamente y comparar. Usa la diferencia central, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, que cancela el término de error principal y es muchísimo más precisa para el mismo hh.

gradcheck.pyPYTHON
def numeric_grad(f, theta, h=1e-5):
    theta = np.asarray(theta, dtype=float)
    out = np.zeros_like(theta)
    for i in range(theta.size):
        bump = np.zeros_like(theta)
        bump[i] = h
        out[i] = (f(theta + bump) - f(theta - bump)) / (2 * h)     
    return out


def gradcheck(f, df, theta, h=1e-5):
    analytic = np.asarray(df(theta), dtype=float)
    numeric = numeric_grad(f, theta, h)
    return np.max(np.abs(analytic - numeric) / np.maximum(1e-8, np.abs(analytic) + np.abs(numeric)))

La forma relativa de la comparación importa: una diferencia absoluta de 10410^{-4} es un desastre en un gradiente de tamaño 10310^{-3} e irrelevante en uno de tamaño 10610^{6}.

TEXT
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744

La primera línea es el gradiente derivado a mano de arriba. La segunda es la misma función con el factor 2 omitido en un componente —una errata de un solo carácter—, y la comprobación la detecta de inmediato. Todo lo que esté por debajo de aproximadamente 10710^{-7} es acuerdo; todo lo que esté por encima de 10410^{-4} es un bug. Guarda esta función: el Capítulo 5 la usa para depurar un motor de diferenciación automática, y es la única razón por la que un gradiente incorrecto puede encontrarse.

Todo este capítulo descansaba sobre una suposición que nunca se enunció: que puedes escribir L/θ\partial L / \partial \theta.

Para una recta con dos parámetros, eso era una línea de álgebra. Deja de serlo casi de inmediato. Pídele a un sistema de álgebra simbólica la derivada de la pérdida de una red respecto a un único peso de la primera capa, para un único ejemplo, y cuenta la aritmética de la respuesta:

redoperaciones en una derivada parcial
cuatro unidades ocultas, una capa40
cuatro unidades ocultas, dos capas301
cuatro unidades ocultas, tres capas1.717

La tercera fila es una red con 57 parámetros —una red tan pequeña que sería una nota al pie en el Capítulo 6—, y escribir su gradiente a mano implica unas 97.869 operaciones para un ejemplo de entrenamiento. No hay notación que rescate esto. Lo rescata la observación de que la regla de la cadena aplicada a una composición tiene una estructura enorme, que las mismas cantidades intermedias aparecen una y otra vez, y que calcularlas en el orden correcto obtiene todas las derivadas por más o menos el precio de una pasada hacia delante. Eso es el Capítulo 5.

Pero antes hay un problema más pequeño, y está esperando justo delante.

Ahora tenemos una máquina que rodará cuesta abajo sobre cualquier pérdida diferenciable. Apúntala a la pregunta original de la cinta —aceptar o rechazar, un objetivo que es 1 o 0—, pon una sigmoide en la salida para que prediga una probabilidad, y minimiza el error cuadrático. Funcionará. También apenas se moverá cuando más equivocada esté, y el gradiente explica por qué:

salida zzpredicciónverdadgradiente con error cuadráticogradiente con entropía cruzada
000.500012.5×1012.5 \times 10^{-1}5.0×1015.0 \times 10^{-1}
2-20.119211.850×1011.850 \times 10^{-1}8.808×1018.808 \times 10^{-1}
6-60.002514.921×1034.921 \times 10^{-3}9.975×1019.975 \times 10^{-1}
10-104.54×1054.54 \times 10^{-5}19.079×1059.079 \times 10^{-5}1.0001.000

Un modelo confiada y catastróficamente equivocado —prediciendo 0,0000454 cuando la respuesta es 1— produce un gradiente de error cuadrático de 9×1059 \times 10^{-5}. No tiene ni idea de que está en problemas. La otra columna, de una pérdida que aún no hemos derivado, informa de 1,0: urgencia máxima, exactamente donde corresponde.

Lo que plantea la pregunta con la que abre el siguiente capítulo. El capítulo anterior dijo que una pérdida es una suposición sobre el ruido, y el error cuadrático asume ruido gaussiano. ¿Qué modelo de ruido tiene una respuesta de sí o no, y qué pérdida sale cuando aplicas la misma derivación sobre él?


El método es más antiguo que todos estos: Cauchy lo describió en una nota a la Académie des Sciences en 1847, como una forma de resolver sistemas de ecuaciones caminando cuesta abajo sobre la suma de sus residuales al cuadrado. También merece la pena leer junto a este capítulo: An overview of gradient descent optimization algorithms, de Sebastian Ruder (arXiv:1609.04747), que cubre momentum hasta Adam en catorce páginas legibles; el capítulo 3 de Numerical Optimization, de Nocedal y Wright (2.ª ed., Springer, 2006), cuyo teorema 3.3 da la tasa de convergencia del descenso más empinado sobre una cuadrática en términos del número de condición —es la teoría que hay detrás de por qué el condicionamiento decide el recuento de pasos, aunque trata la búsqueda de línea en lugar del techo de 2/λmax2/\lambda_{\max} con paso fijo medido arriba—, o §5.8 y §7.1 de Mathematics for Machine Learning, de Deisenroth, Faisal y Ong, para el mismo terreno con menos maquinaria; §6.1 de Understanding Deep Learning, de Prince, y §4.3 de Deep Learning, de Goodfellow, Bengio y Courville; Dive into Deep Learning §12.1–12.3, que tiene el análisis de minibatch con más mediciones de las que caben aquí; y el capítulo 4 de Hands-On Machine Learning, de Géron (3.ª ed.), el tratamiento más práctico de la tasa de aprendizaje como algo que ajustas en vez de derivar. Las notas de MIT 6.390 ponen gradient descent antes de la clasificación, como hace este curso y por la misma razón.

  1. LeCun, Y., Bottou, L., Orr, G. B. y Müller, K.-R. Efficient BackProp, en Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. La sección 4.3 da la recomendación y la sección 5.1 el argumento usado en el recuadro de detalle de arriba: centrar y escalar las entradas cambia los autovalores de la matriz de segundas derivadas y, por tanto, el número de pasos, no solo la comodidad numérica.

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. y Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). El argumento de que en dimensiones altas los puntos críticos son abrumadoramente sillas y no mínimos locales, porque un mínimo exige que todas y cada una de miles de direcciones se curven hacia arriba a la vez.

  3. Robbins, H. y Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). El artículo que estableció que una estimación ruidosa de un gradiente basta, siempre que el tamaño de paso se reduzca de la forma correcta.

  4. Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), pp. 1–17 (1964). El método heavy-ball, que es la actualización con momentum de arriba, veintidós años antes de que backpropagation llegara a este campo.

¿Listo para dejar que elija LIA?

Crea con todos los modelos de IA en un mismo sitio. Empieza gratis hoy.