La matriz que aparece en la ecuación adjunta es AT.
Por eso, aunque el problema directo se resuelve hacia adelante en el tiempo, el problema adjunto se resuelve hacia atrás.
Si se usa un factor 1/2 delante de la norma cuadrática, la derivada queda sin el factor 2.
Equivalentemente, ese factor puede absorberse en los pesos wj.
Con esa convención, el término fuente de la ecuación adjunta es
Esta ecuación se resuelve en modo reverso.
Primero se calcula y se guarda la trayectoria directa u0,u1,…,uM.
Luego se calcula λM.
Finalmente se propaga λj hacia atrás.
El adjunto discreto evita calcular una sensibilidad distinta para cada parámetro.
Esto es especialmente útil cuando hay muchos parámetros y una única función de costo escalar.
Aunque la ecuación diferencial original sea no lineal, la ecuación para λ es lineal en λ.
La linealidad aparece porque el adjunto se obtiene al linearizar alrededor de la trayectoria directa ya calculada.
En muchos casos, el método del adjunto discreto es equivalente a hacer backpropagation sobre el solver numérico.
Por eso se dice que el adjunto se resuelve en modo reverso.
sujeta a la condición inicial u(t0)=u0, donde u∈Rn es el vector solución desconocido de la ODE, f:Rn×Rp×R→Rn es una función que depende de: el estado u, θ∈Rp es un vector de parámetros, y t∈[t0,t1] se refiere al tiempo. Aquí, n denota el tamaño de la ODE y p el número de parámetros. Resolver la ODE implica obtener u(t), que depende de θ. En general no es posible obtener una solución explícita de u (salvo en casos lineales o muy particulares), por lo que debemos resolverla numéricamente.
Recordemos que queremos obtener θ (donde θ pueden ser los parámetros de una red o, en problemas inversos, coeficientes de ecuaciones diferenciales). De esta forma, nos interesa generalmente dθdL.
Para ello, veamos qué es L. Podemos escribir el término de la loss de forma general como una integral[1]:
En (26) notamos que aparece un VJP[2]∂u∂hs(t), con sensibilidads(t)=∂θ∂u∈Rn×p[3]. Como en el método discreto, la idea del método adjunto consiste en aprovechar esta estructura para introducir una nueva variable (adjuntoλ) que nos permita evitar calcular el jacobiano s(t).
¿Por qué es costoso s(t)? Es una matriz de n×p y, como veremos, satisface su propia ODE. Cuando p es grande (por ejemplo, los parámetros de una red), esto se vuelve prohibitivo, y de ahí la motivación del método adjunto.
Notemos que la sensibilidad tiene su ecuación diferencial asociada:
Recordemos que el objetivo ahora es eliminar la sensibilidad s(t). Primero vamos a usar integración por partes sobre λ⊤dtds para trasladar la derivada temporal de s hacia λ, y luego reemplazamos en (29).
0=∫t0t1[λ⊤dtdsaplicamos partes−λ⊤∂u∂fs−λ⊤∂θ∂f]dτ=partesλ⊤s∣∣t0t1−∫t0t1dtdλ⊤sdτ−∫t0t1λ⊤∂u∂fsdτ−∫t0t1λ⊤∂θ∂fdτ=frontera con s(t0)=0λ(t1)⊤s(t1)+∫t0t1coeficiente de s−dtdλ⊤−λ⊤∂u∂fsdτ−∫t0t1λ⊤∂θ∂fdτ∀λ(t)
Notar que λ⊤s∣∣t0t1=λ(t1)⊤s(t1)−λ(t0)⊤s(t0). En general, la condición inicial no depende de θ, por lo que dθdu0=0. Luego, s(t0)=dθdu0=0⇒λ⊤s∣∣t0t1=λ(t1)⊤s(t1).
Notemos que (33) es una ecuación diferencial para λ(τ); necesitamos una condición para resolverla. Tomemos λ(t1)=0 como condición final. Sustituyendo ambas elecciones en (31):
A=dtd−∂u∂f, un operador lineal (dada una función devuelve una función, y además es lineal). Por la ecuación de sensibilidad, As=b, con b=∂f/∂θ.
Se puede derivar que el operador adjunto es A∗λ=−dτdλ−(∂u∂f)⊤λ. El operador adjunto A∗ se define como aquel que cumple ∫t0t1(Av)⊤wdt=∫t0t1v⊤(A∗w)dt; puede verse como una generalización de la transpuesta, ⟨Au,v⟩=⟨u,A⊤v⟩, con A matriz y u,v vectores.
De esta forma, obtenemos el siguiente método para computar el gradiente dL/dθ:
Resolver la ODE original (forward):dtdu=f(u,θ,t),u(t0)=u0. Se guardan los valores de u(t) o se usan técnicas como checkpointing.
Resolver la ecuación adjunta (backward):dtdλ=−(∂u∂f)⊤λ−(∂u∂h)⊤,λ(t1)=0. La condición final λ(t1)=0 significa que la ODE adjunta se resuelve hacia atrás en el tiempo (de t1 a t0).
Calcular el gradiente:dθdL=∫t0t1(λ⊤∂θ∂f+∂θ∂h)dt.
Es una técnica para balancear el uso de memoria y el tiempo de cómputo en métodos que requieren almacenar activaciones intermedias (como Reverse AD y el método del adjunto). Consiste en guardar solo algunos puntos intermedios en memoria y recomputar los demás según sea necesario, intercambiando memoria por cómputo.
Una alternativa al checkpointing es no almacenar la trayectoria u(t), sino reconstruirla resolviendo la ODE hacia atrás junto con la del adjunto. Primero invertimos la variable temporal y definimos un estado final, en vez de un estado inicial:
Bajo el cambio t→−t, todo lado derecho cambia de signo. Aplicándolo también a la ecuación adjunta (cuya forma estándar es dtdλ=−(∂f/∂u)⊤λ−(∂h/∂u)⊤), podemos resolver el sistema acoplado hacia atrás, en modo reverse:
Esto se denomina backsolve. Su ventaja es que evita almacenar la trayectoria completa (poca memoria); su desventaja es que, en ciertos casos, reconstruir u hacia atrás puede acumular error numérico. Por eso puede combinarse con checkpointing para reanclar la solución en puntos guardados e ir corrigiendo dichos errores.
Veamos el ejemplo del caso discreto, donde la loss es una suma ponderada ∑iwi∥u(ti,θ)−uiobs∥22 sobre instantes de observación ti. Podemos escribir el integrando como
Notar que ∂h/∂u es de tamaño 1×n (ya que h es una función escalar y u∈Rn, su gradiente es un vector fila de n componentes), de modo que el producto ∂u∂hs(t) es un vector de 1×p.
s(t) define qué tanto cambia mi solución u(t)∈Rn con respecto de θ: ∂θ∂u. Notemos que tiene su ecuación diferencial asociada. Diferenciemos (24) con respecto de θ:
y si u0 no depende de θ, entonces s(t0)=0. De esta forma, la ecuación de sensibilidad me dice cómo un cambio de los parámetros afecta a mi solución del sistema en el tiempo.