Saltar ao contido
3/30Capítulo 3 de 30

Costa abaixo: Gradient Descent e os dous pasos que todo o mundo salta

Calcula o teito exacto dunha taxa de aprendizaxe e mira como unha busca en 3.600 direccións redescobre o gradient.

Nesta páxina

O capítulo anterior remataba cun val.

Non un metafórico: unha curva real, a perda representada fronte a un único parámetro, baixando e volvendo subir. E a perda baixo ela non se escollera porque fose cómoda: derivárase dunha afirmación sobre o ruído nas medicións, e o erro cadrático saíra polo outro extremo como consecuencia, non como convención.

Así que temos unha paisaxe cun fondo, e unha razón para crer que o fondo é o lugar correcto. O que non temos é unha maneira de chegar alí.

Este capítulo constrúe unha, e é o algoritmo que adestra todos os modelos no resto deste curso: todos, sen excepción, ata incluír os que teñen centos de miles de millóns de parámetros. Cabe nunhas vinte liñas. As dúas partes difíciles non están nesas vinte liñas, e son as dúas cousas que case toda explicación salta:

  • Por que o signo menos. A actualización resta o gradient. Todos os titoriais o escriben; moi poucos din por que o gradient é a dirección que vai cara arriba, que é o único feito que converte o signo menos en algo distinto dun acto de fe.
  • Que tamaño de paso. «Demasiado grande diverxe, demasiado pequeno é lento» é certo e inútil. Hai un número exacto, pódese calcular a partir da perda, e este capítulo calcúlao dúas veces: unha para unha parábola de xoguete e outra para os datos reais.

A configuración, e por que non podes simplemente buscar

Ligazón á sección: A configuración, e por que non podes simplemente buscar

Reformulado para que este capítulo se sosteña por si só: as oito pezas da cinta transportadora do Capítulo 1, pero facendo outra pregunta. Non aceptar ou rexeitar —iso volverá máis adiante— senón predicir o peso dunha peza a partir da súa 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

As medicións están centradas, exactamente como no Capítulo 1 e por unha razón que volverá con xuros antes de que remate este capítulo. O modelo é unha recta, y^=ax+b\hat{y} = a x + b, e a perda é o erro cadrático medio que derivou o 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

Dous parámetros. Por que non probar simplemente moitos valores? Fagámolo de verdade: unha grade desde a=0a = 0 ata 55 e desde b=5b = -5 ata 55, en 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 avaliacións para fixar dous números con dúas cifras decimais, e ese segundo é tempo real nunha máquina, así que unha repetición pode caer entre tres e seis; o reconstrutible é o reconto de avaliacións e o mínimo. Gradient descent, ao final deste capítulo, consegue catro cifras decimais en oito pasos e a resposta float64 completa en trinta e seis.

Pero a velocidade non é o argumento, e este é o punto que decide todo o curso. A busca en grade custa kPk^P avaliacións para PP parámetros con kk valores cada un. Con mil valores por eixe:

modeloparámetrosavaliacións da grade
esta recta210610^{6}
a rede XOR do Capítulo 59102710^{27}
unha pequena rede multicapa20.0001060,00010^{60{,}000}

A terceira fila non é un número grande, é un número sen significado: hai aproximadamente 108010^{80} átomos no universo observable. A busca non se volve máis lenta a medida que medran os modelos; deixa de existir. Todo o que segue existe por esa táboa.

Unha derivada é unha medición que podes facer

Ligazón á sección: Unha derivada é unha medición que podes facer

Fixa b=0b = 0 un momento para que haxa un parámetro e unha curva, que é a imaxe coa que te deixou o capítulo anterior. Toma un punto nela, a=1a = 1, e pregunta: se desprazo aa unha pequena cantidade hh, canto se move a perda por unidade dese desprazamento?

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

Esa razón é un incremento vertical sobre incremento horizontal: a pendente da recta que pasa por dous puntos da curva. A medida que hh encolle, os dous puntos xúntanse e a recta convértese na tanxente. A súa pendente é a derivada L(a)L'(a): a taxa á que cambia a perda por unidade de cambio en aa. Non é unha aproximación de nada, nin unha cantidade infinitamente pequena. É un límite de razóns ordinarias.

Paga a pena executalo, porque os números din algo que a definición non di:

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 dúas cousas, e as dúas sosteñen a carga.

O erro non é vagamente proporcional a hh: é exactamente 7.445h7.445\,h. Divide hh por cen, e o erro divídese por cen, con catro cifras significativas cada vez. Esa constante non é decoración: é a metade da segunda derivada da perda, e é a primeira aparición dunha idea que chegará dentro de dúas seccións: que unha curva preto dun punto semella unha recta máis unha corrección proporcional a h2h^2.

E logo o patrón rompe. Por debaixo de h=108h = 10^{-8} a estimación empeora, e en 101410^{-14} xa está mal na segunda cifra. Non pasou nada matemático; pasou a caixa de coma flotante do capítulo anterior. L(a+h)L(a+h) e L(a)L(a) coinciden nos seus dez primeiros díxitos, restalos destrúe eses díxitos, e dividir os restos por un número diminuto amplifica o que queda. Hai un mellor hh —aquí arredor de 10810^{-8}, aproximadamente a raíz cadrada do épsilon da máquina— e facelo máis pequeno non é ser máis coidadoso, é selo menos. Lembra isto: unha función ao final deste capítulo depende diso.

A pendente exacta, desde o cálculo e non desde a medición, é 16.385-16.385. Así que podemos deixar de medir e empezar a derivar.

Aquí está a idea sobre a que se constrúe o resto do curso, dita unha vez e con claridade.

Compoñer dúas funcións é alimentar unha coa outra: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Nada máis.

Unha rede profunda non é como unha composición. É unha. Unha capa é unha función; apilar capas é compoñelas; a «profundidade» é o número de funcións na cadea. Cando o Capítulo 5 constrúe unha rede, está construíndo f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 e nada máis. O que significa que a regra máis importante do cálculo, para os nosos propósitos, é a que diferencia unha composición:

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

As taxas multiplícanse. Se gg cambia tres veces máis rápido ca xx, e ff cambia dúas veces máis rápido ca gg, entón ff cambia seis veces máis rápido ca xx. Ese é todo o contido, e por iso un sinal que pasa cara atrás a través de dez capas se multiplica por dez números, que é a razón pola que o Capítulo 6 dedica unha sección a que pasa cando todos eses números son lixeiramente menores ca un.

Úsaa na nosa perda. Escribe o 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 da función interna axia x_i, cuxa derivada é xix_i. Regra da cadea, termo a termo:

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

Eses símbolos curvos \partial marcan unha derivada parcial: diferencia respecto dunha variable e trata todas as demais como constantes. Non pasa nada novo: é o mesmo límite de antes, tomado ao longo dun eixe. Reúne as parciais nun vector e tes o gradient:

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

No punto (a,b)=(1,4)(a, b) = (1, 4) ese vector é (16.385, 8.0)(-16.385,\ 8.0). Dous números. A pregunta é que significan, e este é o primeiro paso que todo o mundo salta.

O gradient é un vector de pendentes ao longo dos eixes. Iso é todo o que probamos. Non é evidente —non debería selo— que xuntalas nun vector produza algo que apunte a algún sitio en particular.

Así que definamos o que realmente queremos. Escolle un vector unitario u\mathbf{u}, unha dirección. A derivada direccional é a taxa á que cambia a perda cando camiñas nesa 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}

A regra da cadea convérteo en algo calculable. Camiñar ao longo de u\mathbf{u} cambia aa á taxa u1u_1 e bb á taxa u2u_2, e as contribucións súmanse:

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}

A taxa de cambio en calquera dirección é o produto escalar do gradient con esa dirección. E agora o remate, que é unha liña de xeometría. Escribindo o produto escalar co ángulo ϕ\phi entre os 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

xa que u\mathbf{u} ten lonxitude 1. O único que controlas é cosϕ\cos\phi, que é máximo en ϕ=0\phi = 0 e mínimo a media volta, ϕ=180\phi = 180 graos. Así que:

  • O ascenso máis pronunciado é ao longo do propio L\nabla L, e a pendente alí é exactamente L\lVert \nabla L \rVert.
  • O descenso máis pronunciado é ao longo de L-\nabla L, e a pendente alí é L-\lVert \nabla L \rVert.
  • Perpendicular ao gradient, a perda non cambia nada. Por iso as liñas dun mapa de contorno cortan o gradient en ángulos rectos.

Ese é o signo menos. Non unha convención, non unha inversión de signo que alguén escolleu: a dirección de descenso máis rápido é o gradient negativo porque cosϕ\cos\phi se minimiza a media volta, e por ningunha outra razón.

Como isto é unha afirmación sobre todas as direccións, probémola contra todas as direccións. Amostra 3.600 delas, unha por cada décima de grao, e mide cada unha cun pequeno desprazamento:

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

Unha busca que non sabe nada de gradients, sobre 3.600 direccións, atopa a súa subida máis pronunciada en 154,0 graos: a dirección propia do gradient, dentro da resolución de 0,1 graos da busca. E a pendente que atopa alí, 18,2337, é a lonxitude do gradient con seis cifras. O teorema non é unha historia sobre o que significan os gradients; é un feito medible, e esa é a medición.

Por que un pequeno paso costa abaixo realmente axuda

Ligazón á sección: Por que un pequeno paso costa abaixo realmente axuda

Agora o segundo paso saltado. Sabemos que dirección baixa. Diso non se segue que camiñar nesa dirección reduza a perda, porque «abaixo» é unha afirmación sobre un desprazamento infinitesimal e un paso non é infinitesimal.

A ponte é a linealización. Preto dun punto, unha función suave é a súa tanxente máis unha 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 é a expansión de Taylor de primeira orde. O O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) descartado é a curvatura: o mesmo termo que facía que a estimación da táboa de pendentes fose errónea exactamente en 7.445h7.445\,h. Introduce o 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

A perda baixa en ηL2\eta \lVert \nabla L \rVert^2. Cada parte diso é non negativa, así que a promesa é real —para un η\eta suficientemente pequeno—, porque o termo ignorado medra como η2\eta^2 e acaba coméndoo. Esa é toda a teoría. Aquí está a promesa cumpríndose, e logo rompendo:

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

Leo desde abaixo. A medida que η\eta encolle, a caída entregada converxe coa prometida —razón 0,99938, logo 0,99994—, que é o teorema de Taylor sendo correcto. Leo desde arriba e en η=0.2\eta = 0.2 a «caída» entregada é menos dezaseis. O paso foi costa abaixo e a perda subiu.

Así que a regra de actualización é

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

e vén cunha condición que ninguén enuncia: que η\eta sexa suficientemente pequeno. Suficientemente pequeno comparado con que, exactamente, é a seguinte sección.

A learning rate ten un teito, e pódese calcular

Ligazón á sección: A learning rate ten un teito, e pódese calcular

Comeza co val máis simple que existe, f(x)=x2f(x) = x^2, onde f(x)=2xf'(x) = 2x. Un paso de gradient descent é

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

A posición multiplícase por (12η)(1 - 2\eta) en cada paso. Iso é unha sucesión xeométrica, e as sucesións xeométricas teñen exactamente unha regra: encollen cando o multiplicador é menor ca 1 en valor absoluto e medran no caso contrario. Así que 12η<1\lvert 1 - 2\eta \rvert < 1, que é 0<η<10 < \eta < 1.

A fronteira está exactamente en η=1\eta = 1. Non «arredor de 1», nin «1 adoita ser demasiado grande». En η=1\eta = 1 o multiplicador é 1-1 e o punto rebota entre xx e x-x para sempre, sen achegarse nin escapar. Por debaixo, converxe; por riba, diverxe. O intervalo divídese de novo en η=0.5\eta = 0.5, onde o multiplicador cambia de signo: por debaixo a aproximación é monótona, por riba o punto pásase e alterna de lado, e exactamente en 0.50.5 o multiplicador é 0 e un único paso cae no mínimo.

Catro réximes, a partir de catro liñas de álxebra. Vai e cruza ti mesmo as fronteiras:

14 pasos, rematando en x = -0.0836.

Ver os datos como táboa
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 cunha taxa de 0,1, desde x=1.9x = -1.9, rematando en 0.0836-0.0836. Sobe a taxa a 0,5 e o primeiro paso cae no fondo. Sobe a 0,9 e remata no mesmo 0.0836-0.0836 ca 0,1 —mesma distancia, estilo oposto, porque 12η\lvert 1 - 2\eta \rvert é 0,8 para ambas—, pero chega alí facendo zigzag a través do val en vez de baixar por un lado.

E agora o interesante:

14 pasos, rematando en x = -1.9000.

Ver os datos como táboa
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 na fronteira. Catorce pasos cunha taxa de 1, e remata en 1.9-1.9: precisamente onde empezou, sen facer nada máis que rebotar. Un empurrón máis arriba e o rebote medra en vez de manterse; en 1,2 sae da gráfica en catro pasos. Unha taxa demasiado grande non converxe lentamente. Non converxe.

Agora a regra xeral, que sae do mesmo argumento. O multiplicador 12η1 - 2\eta era en realidade 1ηf1 - \eta f'', e preto dun mínimo unha perda con varios parámetros ten un número así por dirección: os autovalores da matriz de segundas derivadas. Todas as direccións teñen que ser estables á vez, así que o teito márcao a maior:

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

Para f(x)=x2f(x) = x^2, f=2f'' = 2, teito 1, que é o que acabamos de derivar. Para a nosa cinta, a matriz de segundas derivadas é 2nAA\frac{2}{n} A^{\top} A con AA a matriz de entradas de dúas columnas, e os seus autovalores son 2 e 14,89, así que o teito é 2/14.89=0.134322 / 14.89 = 0.13432. É unha predición con cinco cifras significativas. Próbaa:

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 decimais de acordo entre unha liña de álxebra lineal e cen mil iteracións dun bucle for.

E aquí é onde volve o Capítulo 1. Todo o anterior usou as medicións centradas. Executa o mesmo código sobre milímetros e gramos en bruto e os autovalores son 0,0298 e 998,1 en vez de 2 e 14,89. O teito colapsa de 0,134 a 0,002004 —coa mesma exactitude, converxendo en lr=0.002003 e estoupando en lr=0.002004.

Peor ca o teito é a razón entre os autovalores. O número de condición mide canto se afasta o val de ser redondo: unha trincheira longa e estreita obriga a usar unha taxa o bastante pequena para as paredes empinadas, e entón o chan da trincheira percorrese a ese mesmo paso de tartaruga. O noso pasa de 7,44 centrado a 33.452 en bruto. Coa mellor taxa que pode usar cada versión:

característicasnúmero de condiciónmellor taxapasos ata quedar a menos do 1% do óptimo
centradas7,440,118410
milímetros e gramos en bruto33.4520,002003779.513

Mesmos datos, mesmo código, mesma resposta ao final, e oito mil veces máis traballo porque ninguén restou unha media. No Capítulo 1 a mesma omisión custoulle ao perceptrón un factor de seis mil en épocas, e o diagnóstico alí era xeométrico: os datos flotaban lonxe da orixe. É a mesma xeometría aquí disfrazada de optimización, e por iso a normalización das entradas non é un consello de hixiene senón aritmética.1

Nada do anterior necesitaba unha biblioteca. Aquí está o optimizador enteiro.

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

A resposta pechada de mínimos cadrados para estes oito puntos é a=2.100403a = 2.100403, b=0b = 0, cunha perda de 24.59244924.592449. O bucle atopouna con oito cifras significativas sen saber que existe unha forma pechada, o cal importa, porque do Capítulo 5 en diante xa non haberá unha.

A traxectoria, porque vela é o 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

A maior parte da distancia cúbrese nos dous primeiros pasos, porque o gradient é máis grande cando estás máis lonxe do fondo e encolle a medida que te achegas. Gradient descent desacelera automaticamente preto dun mínimo. Iso é unha vantaxe e tamén, no Capítulo 6, un problema.

O argumento ata agora ten un burato. O paso detense cando L=0\nabla L = \mathbf{0}, e estivemos chamándolle a iso «o mínimo». Un punto con gradient cero é un punto crítico, e ser un mínimo é só unha das maneiras de selo:

  • un mínimo local: costa arriba en todas as direccións, pero quizais non o punto máis baixo dese tipo en ningures;
  • un máximo local: costa abaixo en todas as direccións;
  • un punto de sela: costa arriba nalgunhas direccións e costa abaixo noutras. A superficie f(x,y)=x2y2f(x,y) = x^2 - y^2 ten f=(2x,2y)\nabla f = (2x, -2y), que é cero na orixe, onde a función é un mínimo ao longo do eixe xx e un máximo ao longo do eixe yy ao mesmo tempo.

Gradient descent non pode distinguilos, porque só mira o gradient, e o gradient é cero nos tres.

A nosa recta ten un único punto crítico e é a resposta: unha perda de erro cadrático sobre un modelo lineal é convexa, unha soa cunca, e o descenso nela non pode deixar de atopar o mínimo global. Esa propiedade non sobrevive ao contacto con este curso. A perda dunha rede neuronal non é convexa, e do Capítulo 5 en diante «o mínimo» non é algo que exista: hai moitos, de profundidades distintas, e cal obteñas depende de onde empezaches. Iso é unha frase e queda nunha frase, porque a teoría é grande e a consecuencia práctica é pequena.

Podes ver toda a consecuencia nunha soa curva. Toma f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, que ten dous vales de profundidades distintas:

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, rematando en x = 0.9456.

Ver os datos como táboa
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

Corenta pasos desde x=0.11x = 0.11, estabilizándose en 0.94560.9456: o máis superficial dos dous vales. Agora move o punto inicial un chisco á esquerda, a 0.100.10. Mesma taxa, mesmos corenta pasos, e estabilízase en 1.0461-1.0461 no seu lugar, onde a perda é 0.199747 menor. A divisoria de augas é a lomba en 0.1010310.101031, e toda a diferenza entre as dúas respostas é en que lado dela che cadrou empezar.

Caer no val superficial é un 56,7% peor en perda, e o algoritmo non ten xeito de sabelo, porque desde dentro dun val todas as direccións son costa arriba. Non hai reparación para isto en gradient descent e non vai chegar ningunha. O que si hai, na práctica, é o achado de que importa moito menos do que suxire esta imaxe: nas dimensións moi altas dunha rede real a maioría dos puntos críticos resultan ser selas máis ca trampas,2 e o Capítulo 5 mide con que frecuencia unha rede pequena queda realmente atrapada.

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

Ligazón á sección: Pasos máis baratos: estocástico, minibatch, momentum

Hai algo en grad de arriba que debería molestarte: suma sobre todo o dataset en cada paso. Oito pezas non son nada. Un millón son un millón de cálculos de gradient para mover os parámetros unha vez.

A saída é que o gradient é unha media, e unha media pódese estimar a partir dunha mostra. Calcúlao nun pequeno conxunto aleatorio —un minibatch— e dá o paso con iso. A estimación é ruidosa; tamén é non sesgada, e centos de pasos ruidosos baratos gañan a un paso exacto caro. En cen mil pezas sintéticas, contando gradients por exemplo en vez de pasos:

métodopasos ata quedar a menos do 0,1% do óptimogradients por exemplo
full batch7700.000
minibatch de 321003.200
un exemplo cada vez17.58017.580

Dúascentas dezanove veces menos aritmética para chegar ao mesmo sitio. E o extremo —un exemplo cada vez, a aproximación estocástica orixinal de Robbins e Monro3non é o gañador: é cinco veces peor ca batches de 32, porque 32 exemplos case non custan máis ca un en hardware que multiplica matrices, mentres que o ruído cae coa raíz cadrada do tamaño do batch. Ese equilibrio é a razón pola que todo script de adestramento que leas terá un batch_size nel.

Momentum é a outra corrección barata, e apunta directamente á trincheira. Nun val mal condicionado os pasos fan zigzag a través da dirección estreita mentres se arrastran pola longa. Momentum mantén unha media móbil dos gradients pasados, de modo que os compoñentes oscilantes se cancelan e o 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}

Dúas liñas extra. Na cinta en bruto sen centrar —número de condición 33.452, o peor caso que temos—, coa mellor taxa que pode usar o 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 dúas liñas de código. O Capítulo 6 convérteo en Adam; o mecanismo xa está aquí.

A comprobación que necesitarás no Capítulo 5

Ligazón á sección: A comprobación que necesitarás no Capítulo 5

Todos os gradients deste capítulo deriváronse á man e, polo tanto, poderían estar mal. A corrección é a táboa de pendentes do principio: medir a derivada numericamente e comparar. Usa a diferenza central, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, que cancela o termo de erro principal e é moitísimo máis precisa para o mesmo 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)))

A forma relativa da comparación importa: unha diferenza absoluta de 10410^{-4} é un desastre nun gradient de tamaño 10310^{-3} e irrelevante nun de tamaño 10610^{6}.

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

A primeira liña é o gradient derivado á man arriba. A segunda é a mesma función co factor 2 omitido nun compoñente —un erro tipográfico dun só carácter—, e a comprobación detéctao inmediatamente. Todo por debaixo de arredor de 10710^{-7} é acordo; todo por riba de 10410^{-4} é un bug. Garda esta función: o Capítulo 5 úsaa para depurar un motor de diferenciación automática, e é a única razón pola que un gradient incorrecto é atopable.

Todo neste capítulo descansaba nunha suposición que nunca se enunciou: que podes escribir L/θ\partial L / \partial \theta.

Para unha recta con dous parámetros, iso era unha liña de álxebra. Deixa de selo case de inmediato. Pídelle a un sistema de álxebra simbólica a derivada da perda dunha rede respecto dun único peso da primeira capa, para un único exemplo, e conta a aritmética da resposta:

redeoperacións nunha derivada parcial
catro unidades ocultas, unha capa40
catro unidades ocultas, dúas capas301
catro unidades ocultas, tres capas1.717

A terceira fila é unha rede con 57 parámetros —unha rede tan pequena que sería unha nota ao pé no Capítulo 6— e escribir o seu gradient á man significa unhas 97.869 operacións para un exemplo de adestramento. Non hai notación que rescate isto. O que o rescata é a observación de que a regra da cadea aplicada a unha composición ten unha estrutura enorme, que as mesmas cantidades intermedias aparecen unha e outra vez, e que calculalas na orde correcta dá todas as derivadas por aproximadamente o prezo dunha pasada cara adiante. Iso é o Capítulo 5.

Pero primeiro hai un problema máis pequeno, e agarda xusto diante.

Agora temos unha máquina que baixará costa abaixo sobre calquera perda diferenciable. Apúntaa á pregunta orixinal da cinta —aceptar ou rexeitar, un obxectivo que é 1 ou 0—, pon unha sigmoide na saída para que prediga unha probabilidade, e minimiza o erro cadrático. Executarase. Tamén se moverá moi pouco cando máis equivocada estea, e o gradient di por que:

saída zzprediciónverdadegradient con erro cadráticogradient 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 que está confiado e catastroficamente equivocado —predicindo 0,0000454 cando a resposta é 1— produce un gradient de erro cadrático de 9×1059 \times 10^{-5}. Non ten nin idea de que está en apuros. A outra columna, dunha perda que aínda non derivamos, informa 1,0: urxencia máxima, exactamente onde se merece.

O que suscita a pregunta coa que abre o seguinte capítulo. O capítulo anterior dixo que unha perda é unha suposición sobre o ruído, e o erro cadrático supón ruído gaussiano. Que modelo de ruído ten unha resposta de si ou non, e que perda sae cando lle aplicas a mesma derivación?


O método é máis antigo ca todos estes: Cauchy describiuno nunha nota á Académie des Sciences en 1847, como unha maneira de resolver sistemas de ecuacións camiñando costa abaixo sobre a suma dos seus residuais cadráticos. Tamén paga a pena ler xunto con este capítulo: An overview of gradient descent optimization algorithms de Sebastian Ruder (arXiv:1609.04747), que cobre momentum ata Adam en catorce páxinas lexibles; o capítulo 3 de Numerical Optimization de Nocedal e Wright (2.ª ed., Springer, 2006), cuxo teorema 3.3 dá a taxa de converxencia do descenso máis pronunciado nunha cuadrática en termos do número de condición —é a teoría detrás de por que o condicionamento decide o reconto de pasos, aínda que trata a busca en liña en vez do teito de 2/λmax2/\lambda_{\max} con paso fixo medido arriba—, ou §5.8 e §7.1 de Mathematics for Machine Learning de Deisenroth, Faisal e Ong para o mesmo terreo con menos maquinaria; §6.1 de Understanding Deep Learning de Prince e §4.3 de Deep Learning de Goodfellow, Bengio e Courville; Dive into Deep Learning §12.1–12.3, que ten a análise de minibatch con máis medicións das que caben aquí; e o capítulo 4 de Hands-On Machine Learning de Géron (3.ª ed.), o tratamento máis práctico da learning rate como algo que axustas máis ca derivar. As notas MIT 6.390 poñen gradient descent antes da clasificación, como fai este curso e pola mesma razón.

  1. LeCun, Y., Bottou, L., Orr, G. B. e Müller, K.-R. Efficient BackProp, en Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. A sección 4.3 dá a recomendación e a sección 5.1 o argumento usado na caixa de detalle de arriba: centrar e escalar as entradas cambia os autovalores da matriz de segundas derivadas e, polo tanto, o número de pasos, non só a comodidade numérica.

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. e Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). O argumento de que en dimensións altas os puntos críticos son esmagadoramente selas máis ca mínimos locais, xa que un mínimo require que cada unha de miles de direccións curve cara arriba á vez.

  3. Robbins, H. e Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). O artigo que estableceu que unha estimación ruidosa dun gradient abonda, dado un tamaño de paso que encolle do xeito correcto.

  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). O método heavy-ball, que é a actualización de momentum de arriba, vinte e dous anos antes de que backpropagation chegase a este campo.

Listo para deixar que LIA escolla por ti?

Crea con todos os modelos de IA nun só sitio: empeza gratis hoxe mesmo.