El gradiente que se desvanece
28 min de lectura
La lección anterior, sobre backpropagation through time (BPTT), cerró con un gradiente exacto y un cabo suelto. Exacto porque el sondeo numérico lo confirmó casilla por casilla; suelto porque la forma de calcularlo repite un mismo gesto sin que nadie mirara sus consecuencias: para arrastrar el error de una posición a la de más atrás hay que multiplicarlo por la matriz recurrente , y en una secuencia de cuarenta tokens ese producto se repite casi cuarenta veces. Esta lección mide qué le hace al gradiente repetir la misma multiplicación tantas veces seguidas, y encuentra el número de que le pone techo al resultado.
Y no es una curiosidad numérica: en juego está la única razón para preferir una RNN a la red del principio del bloque. Toma una reseña que arranca con Dudo que a alguien le sirva y, treinta palabras más tarde, cierra con su valoración. Ese Dudo inicial invierte el signo de todo lo que viene después, pero la red solo puede aprender esa conexión si el gradiente calculado en la última palabra consigue volver hasta la primera. Si por el camino se apaga, la red jamás registrará que el principio importaba, y la memoria que la definía se quedará, en la práctica, en memoria de lo reciente.
Antes de mirar la matriz entera, quítale coordenadas hasta que quede una sola. Con el estado es un número, es un número , y arrastrar el error un paso hacia atrás es multiplicarlo por . Repetir el paso veces lo multiplica por .
Y no tiene término medio. Si se hunde: con , cuarenta pasos dejan el error en , doce órdenes de magnitud por debajo de donde salió. Si se dispara: . Solo mantiene el error en su tamaño, y es un filo de un solo punto: basta desviarse un pelo para caer a un lado o al otro. Con más de una coordenada, pasa a ser una matriz y su papel se reparte entre dos números que la formalización separa enseguida: uno le pone techo al gradiente paso a paso, máscara incluida; el otro marca el ritmo de la matriz multiplicada por sí misma, a la larga. El explorable enseña el segundo, el radio espectral, en una recurrencia sin máscara (todas las pendientes de la en ). Deslízalo a un lado y a otro de y verás las dos mitades del fenómeno: por debajo, la curva se desploma; por encima, se va por arriba.
El producto que conecta dos posiciones
Volvamos a la recurrencia hacia atrás de la lección anterior y sigámosla más de un paso. Allí, el error de un paso salía del siguiente con dos gestos (transportar y enmascarar):
El con la pendiente de la es multiplicar por una matriz diagonal (, con esas pendientes en la diagonal y ceros fuera), así que un paso de la vuelta multiplica por dos matrices: primero , luego esa diagonal. Encadena la recurrencia desde el último paso hasta uno lejano y las dos se repiten, una pareja por posición:
El producto tiene parejas, una por cada paso entre y (la distancia que el error recorre). Y dentro de cada pareja, la misma que en todas las demás: esto no es una capa distinta por posición, como lo eran los bloques de la primera capa concatenada de la lección sobre por qué el MLP falla con secuencias, sino una sola matriz elevada, en la práctica, a la potencia de la distancia. Ese es el gesto que la lección anterior repetía sin mirarlo.
Por qué decae o explota
Un producto de matrices es difícil de mirar; su tamaño, no. Toma normas a los dos lados y usa que la norma de un producto no supera el producto de las normas:
Ese ya no mide un vector: mide una matriz. Para una matriz cualquiera se define como
el mayor factor por el que puede estirar la longitud de un vector, tomando el peor caso sobre todas las direcciones posibles. Esa definición es la que deja cierta para cualquier (multiplica la definición por ) y, aplicada dos veces seguidas, la que da : estira como mucho , y después estira ese resultado como mucho más.
Los dos factores de cada pareja tienen un techo. Para una matriz diagonal ese máximo es directo: estira cada coordenada por su propia entrada, así que el peor caso es alinear con la mayor en valor absoluto, y . Aquí cada entrada de la diagonal es una pendiente de la (una por coordenada de ): nunca pasa de , y en cuanto esa coordenada se satura se acerca a . Son las bandas rojas que el explorador de activaciones de la lección sobre funciones de activación ya te dejó ver. La mayor de todas esas pendientes, en todos los pasos de a , es por tanto también ; llámala . El de es la misma definición sin la comodidad de la diagonal: el único hecho nuevo que necesitas aquí es que una matriz estira la longitud de un vector como mucho por su mayor valor singular , así que multiplicar por ella veces la estira, a lo sumo, por . Junta las dos cotas sobre las parejas:
Ahí está el exponente, y con él la única condición que se sostiene pase lo que pase por el camino. El error que llega a la posición es, como mucho, el del final multiplicado por elevado a la distancia. Si , el gradiente se desvanece sin remedio: el techo cae en picado con la distancia y el gradiente no puede pasar por encima de él, valgan lo que valgan las máscaras. Como , ya lo garantiza por sí solo, y la saturación solo añade margen. Lo contrario no se cumple. Si , el techo deja de sujetar el gradiente y este puede explotar, pero nada lo obliga: esa desigualdad es necesaria para la explosión, no suficiente.
La cota, además, suele sobrar: un vector concreto rara vez se estira el máximo en cada paso. Lo que hace de verdad al multiplicarse por sí misma, a la larga, lo mide otro número, su radio espectral (el mayor de los módulos de sus valores propios), que nunca pasa de . En una recurrencia sin máscara, con todas las pendientes en , el producto es una potencia de , y ahí sí decide: por debajo de el gradiente se hunde, por encima se dispara, y entre las dos cosas no hay más que ese filo. Es el número que el explorable te dejó mover.
Con las máscaras de por medio, deja de ser una garantía: el producto alterna con diagonales y ya no es la potencia de una sola matriz. Lo que se sostiene entonces es , y por eso es esa la condición de verdad. No necesitas calcular valores propios en esta lección; lo que sí hace falta es saber que existe, que marca el ritmo de la matriz sola y que nunca supera a .
Ver por qué el radio espectral deja de decidir cuando entran las máscaras
En una recurrencia sin máscara el producto es , y lo que se cumple a la larga es que tiende al radio espectral cuando crece. Por eso ahí garantiza el desvanecimiento y hace crecer sin freno la norma de la potencia, aunque para una distancia corta la cota con vaya por delante del ritmo real.
Con la , lo que se repite ya no es sino cada pareja, y una máscara puede apagar justo la coordenada que frenaba el crecimiento. Toma . Su cuadrado es la matriz nula, así que y su potencia muere en dos pasos. Pero si la máscara vale en cada paso, cada pareja es , cuyo radio espectral es , y el producto crece como . La condición real no se contradice: aquí y , y un nunca prometió que el gradiente se hundiera.
Compruébalo con una matriz de verdad, sin reducirla a una sola coordenada: numpy.linalg trae svd
para y eig para los valores propios que dan .
W = np.array([[0.5, 4.0],
[0.0, 0.5]])
sigma_max = np.linalg.svd(W, compute_uv=False)[0] # mayor valor singular
rho = np.max(np.abs(np.linalg.eig(W)[0])) # radio espectral
print("sigma_max = %.4f" % sigma_max)
print("rho = %.4f" % rho)
print()
print(" d ||W^d|| ||W^d||**(1/d)")
for d in [1, 5, 10, 20, 40]:
Wd = np.linalg.matrix_power(W, d)
norm_d = np.linalg.svd(Wd, compute_uv=False)[0]
print("%2d %9.2e %14.4f" % (d, norm_d, norm_d ** (1 / d)))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador y se reutiliza en todas las lecciones.
Para esta , pero : la cota con el valor singular sobrevalora el ritmo de la potencia por un factor de ocho. La tabla lo confirma. En la norma cruda ya vale (coincide por construcción), y en sigue por encima de (la potencia puede crecer durante varios pasos aunque garantice que, a la larga, se hunda). Para la norma cruda ya es minúscula y baja de , cada vez más cerca de : confirmando con números lo que el razonamiento de arriba solo afirmaba. Y los dos números responden a preguntas distintas: la potencia sola se hunde, pero con esta no garantiza nada en cuanto entran las máscaras.
Verlo y recortarlo en NumPy
La primera celda reduce todo al caso de una coordenada y mira el exponente crudo; la segunda ataca la mitad del problema que sí tiene un parche barato.
El caso escalar no es un juguete: es la fórmula de arriba con , donde , y son el mismo número. Ejecútala y lee la fila del medio () contra las otras dos.
# atras lo multiplica por w**d. La mascara del tanh (pendiente <= 1) solo lo
# encoge mas, asi que w = 1 ya es el caso mas favorable.
print(" w d=10 d=20 d=40")
for w in [0.5, 1.0, 1.5]:
print("%.1f %9.2e %9.2e %9.2e" % (w, w ** 10, w ** 20, w ** 40))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador y se reutiliza en todas las lecciones.
La primera fila se hunde, la última se dispara y la del medio no se mueve, tal como decía la intuición. Con el error llega a los cuarenta pasos convertido en de lo que era, y ningún descenso de gradiente aprende de una señal tan pequeña. Y esto es el mejor caso: la máscara del , en cuanto el estado se satura, lo empuja todavía más abajo.
La explosión, en cambio, tiene una respuesta directa. Si el problema es que la norma del gradiente crece sin freno, ponle un techo: fija un umbral y, cuando lo pase, reescala el gradiente para devolverlo justo a ese tamaño. Es el recorte del gradiente (gradient clipping):
El con es la clave: si el gradiente ya es pequeño, el factor vale y no lo toca; solo actúa sobre el que se ha pasado, y lo hace multiplicándolo por un número positivo, que cambia su longitud pero no su dirección. Compruébalo.
rng = np.random.default_rng(1)
kappa = 5.0 # el techo que le ponemos a la norma
def recorta(g):
n = np.linalg.norm(g)
return g * min(1.0, kappa / n), n # factor <= 1: encoge o deja igual, no gira
for nombre, g in [("normal ", rng.normal(size=100) * 0.3),
("explotado", rng.normal(size=100) * 3.0)]:
g_rec, n = recorta(g)
coseno = float(g @ g_rec) / (n * np.linalg.norm(g_rec))
print("%s ||g|| = %6.2f -> ||recortado|| = %.2f (coseno con g: %.4f)"
% (nombre, n, np.linalg.norm(g_rec), coseno))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador y se reutiliza en todas las lecciones.
El gradiente normal pasa intacto; el explotado, con norma casi , sale recortado a exacto, y el coseno con el original es (misma dirección, otro tamaño). El recorte salva el entrenamiento cuando el gradiente explota y no cuesta casi nada. Lo que no hace (y conviene decirlo en voz alta) es nada por el desvanecimiento: un gradiente que ya llegó valiendo tiene norma minúscula, el vale , y se queda como estaba.
Comprueba tu intuición
Cuatro preguntas: qué desvanece o explota el gradiente, qué arregla el recorte y qué no, si la máscara de la puede salvar la situación y qué se pierde cuando no.
¿Qué hace que el gradiente de una RNN se desvanezca o explote al retroceder por una secuencia larga?
El recorte del gradiente reescala cuando su norma pasa de un umbral . ¿Qué problema resuelve y cuál no?
La máscara que aparece en cada paso de la vuelta es la pendiente de la . Si el mayor valor singular de cumple , el gradiente se desvanece. ¿Puede la máscara rescatarlo?
¿Qué pierde una RNN cuando el gradiente que conecta el final de la secuencia con el principio se ha desvanecido?
El recorte le pone un techo a la explosión, pero deja el otro lado del problema donde estaba. Un gradiente que se ha multiplicado camino a cero no se recupera multiplicándolo por , y ninguna elección de arregla las dos cosas a la vez: bajar por debajo de para que no pueda explotar es justo lo que garantiza que se desvanezca. El problema no está en los pesos, sino en el camino: cualquier ruta que multiplique por la misma matriz en cada paso acaba en uno de los dos extremos.
La salida es cambiar el camino. Hace falta una vía por la que el estado avance de un paso al siguiente sumando, en lugar de volver a pasar por : sobre una suma el gradiente fluye sin multiplicarse una vez por posición, y así ni se hunde ni se dispara. Esa vía aditiva es el estado de celda de la LSTM (long short-term memory), y construirla (una compuerta cada vez, cada una arreglando un fallo concreto de la RNN vanilla) es la siguiente lección, sobre la LSTM: memoria con compuertas.
Para profundizar2 fuentes · 1 paper, 1 libro
De dónde sale lo de esta lección, y dónde seguir si quieres más. Nada de aquí hace falta para continuar el curso.
- On the difficulty of training Recurrent Neural Networks
De aquí salen las dos piezas de la lección: la cota con la pendiente γ que garantiza el desvanecimiento (el radio espectral solo decide en el caso lineal) y el recorte de la norma del gradiente a un umbral. No toca las compuertas.
- Gradient Flow in Recurrent Nets: the Difficulty of Learning Long-Term Dependencies
Su §2 demuestra lo que aquí ves en una gráfica: el error que retrocede por la secuencia decae o crece de forma exponencial en el número de pasos. Reúne las pruebas de 1991 y 1994; su §4 solo repasa arreglos, la LSTM entre ellos.