Articulo de referencia

Diferenciación automática

En matemáticas y álgebra computacional , la diferenciación automática ( autodiferenciación , autodiff o AD ), también llamada diferenciación algorítmica , diferenciación computa...

En matemáticas y álgebra computacional , la diferenciación automática ( autodiferenciación , autodiff o AD ), también llamada diferenciación algorítmica , diferenciación computacional y aritmética de diferenciación [ 1 ] [ 2 ] [ 3 ] [ 4 ], es un conjunto de técnicas para evaluar la derivada parcial de una función especificada por un programa informático. La diferenciación automática permite el cálculo simultáneo de los valores numéricos de funciones arbitrariamente complejas y sus derivadas utilizando únicamente la función misma; no se requiere ninguna derivada simbólica. [ 3 ] [ 4 ] Por lo tanto, la autodiferenciación no es ni numérica ni simbólica, ni una combinación de ambas. A diferencia de los métodos numéricos más tradicionales basados ​​en diferencias finitas , la autodiferenciación es "en teoría" exacta y, en comparación con los algoritmos simbólicos, es computacionalmente económica. [ 5 ] [ 3 ] [ 6 ]

La diferenciación automática aprovecha el hecho de que cada cálculo informático, por complejo que sea, ejecuta una secuencia de operaciones aritméticas elementales (suma, resta, multiplicación, división, etc.) y funciones elementales ( exp , log , sen , cos , etc.). Al aplicar repetidamente la regla de la cadena a estas operaciones, se pueden calcular automáticamente derivadas parciales de orden arbitrario, con una precisión operativa y utilizando, como máximo, un pequeño factor constante de operaciones aritméticas adicionales respecto al programa original.

Diferencias con otros métodos de diferenciación

Figura 1: Cómo se relaciona la diferenciación automática con la diferenciación simbólica

La diferenciación automática se distingue de la diferenciación simbólica y la diferenciación numérica . La diferenciación simbólica presenta la dificultad de convertir un programa informático en una única expresión matemática , lo que puede generar código ineficiente. La diferenciación numérica (el método de diferencias finitas) puede introducir errores de redondeo en el proceso de discretización y cancelación. Estos métodos clásicos pueden tener problemas al calcular derivadas de orden superior, donde la complejidad y los errores aumentan. Finalmente, ambos métodos clásicos pueden ser lentos al calcular derivadas parciales de una función con respecto a múltiples entradas, como se requiere para los algoritmos de optimización basados ​​en gradientes . La diferenciación automática resuelve todos estos problemas.

Aplicaciones

Debido a su eficiencia y precisión en el cálculo de derivadas de primer y orden superior , la autodiferenciación encuentra diversas aplicaciones en computación científica y matemáticas , y existen numerosas implementaciones computacionales de autodiferenciación. Entre ellas, se mencionan INTLAB , Sollya e InCLosure. [ 7 ] [ 8 ] [ 9 ] En la práctica, existen dos tipos (modos) de diferenciación algorítmica: un tipo directo y un tipo inverso. [ 3 ] [ 4 ] Actualmente, los dos tipos están altamente correlacionados y son complementarios, y ambos tienen una amplia variedad de aplicaciones en, por ejemplo, optimización no lineal , análisis de sensibilidad , robótica , aprendizaje automático , gráficos por computadora y visión por computadora . [ 5 ] [ 10 ] [ 3 ] [ 4 ] [ 11 ] [ 12 ] La diferenciación automática es particularmente importante en el campo del aprendizaje automático . Por ejemplo, permite implementar la retropropagación en una red neuronal sin necesidad de calcular manualmente la derivada.

Acumulación hacia adelante y hacia atrás

Regla de la cadena de derivadas parciales de funciones compuestas

Fundamental para la diferenciación automática es la descomposición de diferenciales proporcionada por la regla de la cadena de derivadas parciales de funciones compuestas . Para la composición simple y=F(gramo(h(incógnita)))=F(gramo(h(w0)))=F(gramo(w1))=F(w2)=w3w0=incógnitaw1=h(w0)w2=gramo(w1)w3=F(w2)=y{\displaystyle {\begin{aligned}y&=f(g(h(x)))=f(g(h(w_{0})))=f(g(w_{1}))=f(w_{2})=w_{3}\\w_{0}&=x\\w_{1}&=h(w_{0})\\w_{2}&=g(w_{1})\\w_{3}&=f(w_{2})=y\end{aligned}}} La regla de la cadena da yincógnita=yw2w2w1w1incógnita=F(w2)w2gramo(w1)w1h(w0)incógnita{\displaystyle {\frac {\partial y}{\partial x}}={\frac {\partial y}{\partial w_{2}}}{\frac {\partial w_{2}}{\partial w_{1}}}{\frac {\partial w_{1}}{\partial x}}={\frac {\partial f(w_{2})}{\partial w_{2}}}{\frac {\partial g(w_{1})}{\partial w_{1}}}{\frac {\partial h(w_{0})}{\partial x}}}

Dos tipos de diferenciación automática

Por lo general, se presentan dos modos distintos de diferenciación automática.

  • acumulación hacia adelante (también llamada de abajo hacia arriba , modo hacia adelante o modo tangente )
  • acumulación inversa (también llamada de arriba hacia abajo , modo inverso o modo adjunto )

La acumulación hacia adelante especifica que se recorre la regla de la cadena de adentro hacia afuera (es decir, primero se calculaw1incógnita{\textstyle {\frac {\partial w_ {1}}{\partial x}}}y luegow2w1{\textstyle {\frac {\w parcial_ {2}}{\ w parcial_ {1}}}}y por últimoyw2{\textstyle {\frac {\partial y}{\partial w_{2}}}}), mientras que la acumulación inversa recorre desde afuera hacia adentro (primer cálculoyw2{\textstyle {\frac {\partial y}{\partial w_{2}}}}y luegow2w1{\textstyle {\frac {\w parcial_ {2}}{\ w parcial_ {1}}}}y por últimow1incógnita{\textstyle {\frac {\partial w_ {1}}{\partial x}}}). De forma más concisa,

  • La acumulación hacia adelante calcula la relación recursiva:wiincógnita=wiwi1wi1incógnitacon w3=y,{\displaystyle {\frac {\partial w_{i}}{\partial x}}={\frac {\partial w_{i}}{\partial w_{i-1}}}{\frac {\partial w_{i-1}}{\partial x}}\quad {\text{with }}w_{3}=y,}
  • La acumulación inversa calcula la relación recursiva:ywi=ywi+1wi+1wicon w0=incógnita.{\displaystyle {\frac {\partial y}{\partial w_{i}}}={\frac {\partial y}{\partial w_{i+1}}}{\frac {\partial w_{i+1}}{\partial w_{i}}}\quad {\text{with }}w_{0}=x.}

El valor de la derivada parcial, llamado semilla , se propaga hacia adelante o hacia atrás y es inicialmenteincógnitaincógnita=1{\textstyle {\frac {\partial x}{\partial x}}=1}oyy=1{\textstyle {\frac {\partial y}{\partial y}}=1}. La acumulación hacia adelante evalúa la función y calcula la derivada con respecto a una variable independiente en una sola pasada. Para cada variable independienteincógnita1,incógnita2,,incógnitanorte{\textstyle x_{1},x_{2},\dots ,x_{n}}Por lo tanto, es necesario un paso separado en el que la derivada con respecto a esa variable independiente se establezca en uno (incógnita1incógnita1=1{\textstyle {\frac {\partial x_{1}}{\partial x_{1}}}=1}) y de todos los demás a cero (incógnita2incógnita1==incógnitanorteincógnita1=0{\textstyle {\frac {\partial x_{2}}{\partial x_{1}}}=\dots ={\frac {\partial x_{n}}{\partial x_{1}}}=0}En cambio, la acumulación inversa requiere las funciones parciales evaluadas para las derivadas parciales. Por lo tanto, la acumulación inversa evalúa primero la función y calcula las derivadas con respecto a todas las variables independientes en una pasada adicional.

El tipo que se debe usar depende del número de iteraciones. La complejidad computacional de una iteración es proporcional a la complejidad del código original.

  • La acumulación hacia adelante es más eficiente que la acumulación inversa para funcionesF:RnorteRmetro{\displaystyle f:\mathbb {R} ^{n}\to \mathbb {R} ^{m}}connortemetro{\displaystyle n\ll m}como solonorte{\displaystyle n}Los barridos son necesarios, en comparación conmetro{\displaystyle m}barridos para acumulación inversa.
  • La acumulación inversa es más eficiente que la acumulación directa para funcionesF:RnorteRmetro{\displaystyle f:\mathbb {R} ^{n}\to \mathbb {R} ^{m}}connortemetro{\displaystyle n\gg m} como solometro{\displaystyle m}Los barridos son necesarios, en comparación connorte{\displaystyle n}barridos para acumulación hacia adelante.

La retropropagación de errores en perceptrones multicapa, una técnica utilizada en el aprendizaje automático , es un caso especial de acumulación inversa. [ 2 ]

La acumulación hacia adelante fue introducida por R.  E. Wengert en 1964. [ 13 ] Según Andreas Griewank, la acumulación inversa se ha sugerido desde finales de la década de 1960, pero se desconoce el inventor. [ 14 ] Seppo Linnainmaa publicó la acumulación inversa en 1976. [ 15 ]

Acumulación anticipada

Acumulación anticipada

En la diferenciación por acumulación hacia adelante, primero se fija la variable independiente con respecto a la cual se realiza la diferenciación y se calcula la derivada de cada subexpresión de forma recursiva. En un cálculo manual, esto implica sustituir repetidamente la derivada de las funciones internas en la regla de la cadena: yincógnita=ywnorte1wnorte1incógnita=ywnorte1(wnorte1wnorte2wnorte2incógnita)=ywnorte1(wnorte1wnorte2(wnorte2wnorte3wnorte3incógnita))={\displaystyle {\begin{aligned}{\frac {\partial y}{\partial x}}&={\frac {\partial y}{\partial w_{n-1}}}{\frac {\partial w_{n-1}}{\partial x}}\\[6pt]&={\frac {\partial y}{\partial w_{n-1}}}\left({\frac {\partial w_{n-1}}{\partial w_{n-2}}}{\frac {\partial w_{n-2}}{\partial x}}\right)\\[6pt]&={\frac {\partial y}{\partial w_{n-1}}}\left({\frac {\partial w_{n-1}}{\partial w_{n-2}}}\left({\frac {\partial w_{n-2}}{\partial w_{n-3}}}{\frac {\partial w_{n-3}}{\partial x}}\right)\right)\\[6pt]&=\cdots \end{aligned}}} Esto se puede generalizar a múltiples variables como un producto matricial de jacobianos .

En comparación con la acumulación inversa, la acumulación directa es natural y fácil de implementar, ya que el flujo de información derivada coincide con el orden de evaluación. Cada variablewi{\displaystyle w_{i}}se ve aumentado con su derivadow˙i{\displaystyle {\dot {w}}_{i}}(almacenado como un valor numérico, no como una expresión simbólica), w˙i=wiincógnita{\displaystyle {\dot {w}}_{i}={\frac {\partial w_{i}}{\partial x}}} como lo indica el punto. Luego, las derivadas se calculan en sincronía con los pasos de evaluación y se combinan con otras derivadas mediante la regla de la cadena.

Usando la regla de la cadena, siwi{\displaystyle w_{i}}tiene predecesores en el grafo computacional: w˙i=j{predecesores de i}wiwjw˙j{\displaystyle {\dot {w}}_{i}=\sum _{j\in \{{\text{predecessors of }}i\}}{\frac {\partial w_{i}}{\partial w_{j}}}{\dot {w}}_{j}}

Figura 2: Ejemplo de acumulación hacia adelante con grafo computacional

Como ejemplo, consideremos la función: y=F(incógnita1,incógnita2)=incógnita1incógnita2+pecadoincógnita1=w1w2+pecadow1=w3+w4=w5{\displaystyle {\begin{aligned}y&=f(x_{1},x_{2})\\&=x_{1}x_{2}+\sin x_{1}\\&=w_{1}w_{2}+\sin w_{1}\\&=w_{3}+w_{4}\\&=w_{5}\end{aligned}}} Para mayor claridad, las subexpresiones individuales se han etiquetado con las variables.wi{\displaystyle w_{i}}.

La elección de la variable independiente sobre la que se realiza la diferenciación afecta a los valores iniciales 1 y 2 . Dado el interés en la derivada de esta función con respecto a x 1 , los valores iniciales deben establecerse en: w˙1=w1incógnita1=incógnita1incógnita1=1w˙2=w2incógnita1=incógnita2incógnita1=0{\displaystyle {\begin{aligned}{\dot {w}}_{1}={\frac {\partial w_{1}}{\partial x_{1}}}={\frac {\partial x_{1}}{\partial x_{1}}}=1\\{\dot {w}}_{2}={\frac {\partial w_{2}}{\partial x_{1}}}={\frac {\partial x_{2}}{\partial x_{1}}}=0\end{aligned}}}

Una vez establecidos los valores iniciales, estos se propagan siguiendo la regla de la cadena, como se muestra en la figura. La figura 2 ilustra este proceso mediante un gráfico computacional.

Para calcular el gradiente de esta función de ejemplo, que requiere no soloyincógnita1{\textstyle {\frac {\partial y}{\partial x_{1}}}}pero tambiényincógnita2{\textstyle {\frac {\partial y}{\partial x_{2}}}}, se realiza un barrido adicional sobre el grafo computacional utilizando los valores semilla.w˙1=0{\textstyle {\dot {w}}_{1}=0};w˙2=1{\textstyle {\dot {w}}_{2}=1}.

Implementación

Pseudocódigo

La acumulación hacia adelante calcula la función y la derivada (pero solo para una variable independiente cada una) en una sola pasada. La llamada al método asociado espera que la expresión Z se derive con respecto a una variable V. El método devuelve un par formado por la función evaluada y su derivada. El método recorre el árbol de expresiones recursivamente hasta alcanzar una variable. Si se solicita la derivada con respecto a esta variable, su derivada es 1; de lo contrario, es 0. A continuación, se evalúan tanto la función parcial como la derivada parcial. [ 16 ]

tupla < float , float > evaluarYderivar ( Expresión Z , Variable V ) { si esVariable ( Z ) si ( Z = V ) devolver { valorDe ( Z ), 1 }; si no devolver { valorDe ( Z ), 0 }; si no si ( Z = A + B ) { a , a ' } = evaluarYderivar ( A , V ); { b , b ' } = evaluarYderivar ( B , V ); devolver { a + b , a ' + b ' }; si no si ( Z = A - B ) { a , a ' } = evaluarYderivar ( A , V ); { b , b ' } = evaluarYderivar ( B , V ); devolver { a - b , a ' - b ' }; si no si ( Z = A * B ) { a , a ' } = evaluarYderivar ( A , V ); { b , b ' } = evaluateAndDerive ( B , V ); return { a * b , b * a ' + a * b ' }; }
C++
#include <iostream> struct ValueAndPartial { float value , partial ; }; struct Variable ; struct Expression { virtual ValueAndPartial evaluateAndDerive ( Variable & variable ) = 0 ; }; struct Variable : public Expression { float value ; Variable ( float value ) : value ( value ) {} ValueAndPartial evaluateAndDerive ( Variable & variable ) { float partial = ( this == & variable ) ? 1.0f : 0.0f ; return { value , partial }; } }; struct Plus : public Expression { Expression & a , & b ; Plus ( Expression & a , Expression & b ) : a ( a ), b ( b ) {} ValueAndPartial evaluateAndDerive ( Variable & variable ) { auto [ valueA , partialA ] = a . evaluateAndDerive ( variable ); auto [ valorB , parcialB ] = b . evaluarYderivar ( variable ); return { valorA + valorB , parcialA + parcialB }; } }; struct Multiplicar : public Expresión { Expresión & a , & b ; Multiplicar ( Expresión & a, Expresión & b ) : a ( a ), b ( b ) {} ValorYPartial evaluarYderivar ( Variable & variable ) { auto [ valorA , parcialA ] = a . evaluarYderivar ( variable ); auto [ valorB , parcialB ] = b . evaluarYderivar ( variable ); return { valorA * valorB , valorB * parcialA + valorA * parcialB }; } }; int main () { // Ejemplo: Encontrar los parciales de z = x * (x + y) + y * y en (x, y) = (2, 3) Variable x ( 2 ), y ( 3 ); Más p1 ( x , y ); Multiplicar m1 ( x , p1 ); Multiplicar m2 ( y , y ); Más z ( m1 , m2 ); float xPartial = z . evaluarYderivar ( x ). parcial ; float yPartial = z . evaluarYderivar ( y ) .partial ; std :: cout << "∂z/∂x = " << xPartial << ", " << "∂z/∂y = " << yPartial << std :: endl ; // Salida: ∂z/∂x = 7, ∂z/∂y = 8 return 0 ; }

Acumulación inversa

Acumulación inversa

En la suma de diferencias de acumulación inversa, la variable dependiente que se va a diferenciar es fija y la derivada se calcula recursivamente con respecto a cada subexpresión . En un cálculo manual, la derivada de las funciones externas se sustituye repetidamente en la regla de la cadena: yincógnita=yw1w1incógnita=(yw2w2w1)w1incógnita=((yw3w3w2)w2w1)w1incógnita={\displaystyle {\begin{aligned}{\frac {\partial y}{\partial x}}&={\frac {\partial y}{\partial w_{1}}}{\frac {\partial w_{1}}{\partial x}}\\[6px]&=\left({\frac {\partial y}{\partial w_{2}}}{\frac {\partial w_{2}}{\partial w_{1}}}\right){\frac {\partial w_{1}}{\partial x}}\\[6px]&=\left(\left({\frac {\partial y}{\partial w_{3}}}{\frac {\partial w_{3}}{\partial w_{2}}}\right){\frac {\partial w_{2}}{\partial w_{1}}}\right){\frac {\partial w_{1}}{\partial x}}\\[6px]&=\cdots \end{aligned}}}

En la acumulación inversa, la cantidad de interés es el adjunto , denotado con una barraw¯i{\displaystyle {\bar {w}}_{i}}; es una derivada de una variable dependiente elegida con respecto a una subexpresiónwi{\displaystyle w_{i}}: w¯i=ywi{\displaystyle {\bar {w}}_{i}={\frac {\partial y}{\partial w_{i}}}}

Usando la regla de la cadena, siwi{\displaystyle w_{i}}tiene sucesores en el grafo computacional: w¯i=j{sucesores de i}w¯jwjwi{\displaystyle {\bar {w}}_{i}=\sum _{j\in \{{\text{successors of }}i\}}{\bar {w}}_{j}{\frac {\partial w_{j}}{\partial w_{i}}}}

La acumulación inversa recorre la regla de la cadena de afuera hacia adentro, o en el caso del grafo computacional de la Figura 3, de arriba hacia abajo. La función de ejemplo es escalar, por lo que solo hay una semilla para el cálculo de la derivada, y solo se necesita un recorrido del grafo computacional para calcular el gradiente (de dos componentes). Esto es solo la mitad del trabajo en comparación con la acumulación directa, pero la acumulación inversa requiere el almacenamiento de las variables intermedias w i, así como las instrucciones que las produjeron en una estructura de datos conocida como "cinta" o lista de Wengert [ 17 ] (sin embargo, Wengert publicó la acumulación directa, no la inversa [ 13 ] ), lo que puede consumir una cantidad significativa de memoria si el grafo computacional es grande. Esto se puede mitigar en cierta medida almacenando solo un subconjunto de las variables intermedias y luego reconstruyendo las variables de trabajo necesarias repitiendo las evaluaciones, una técnica conocida como rematerialización . También se utiliza el punto de control para guardar estados intermedios.

Figura 3: Ejemplo de acumulación inversa con grafo computacional

Consideremos nuevamente la función de ejemploy=F(incógnita1,incógnita2)=incógnita1incógnita2+pecadoincógnita1{\displaystyle y=f(x_{1},x_{2})=x_{1}x_{2}+\sin x_{1}}con subexpresionesw1=incógnita1,{\displaystyle w_{1}=x_{1},}w2=incógnita2,{\displaystyle w_{2}=x_{2},}w3=w1w2,{\displaystyle w_{3}=w_{1}w_{2},}w4=pecado(w1),{\displaystyle w_{4}=\sin(w_{1}),}w5=w3+w4{\displaystyle w_{5}=w_{3}+w_{4}}Las operaciones para calcular los valores adjuntos mediante acumulación inversa se muestran en la tabla siguiente (nótese el orden invertido):

w¯5=1(semilla)w¯4=w¯51w¯3=w¯51w¯2=w¯3w1w¯1=w¯3w2+w¯4porquew1{\displaystyle {\begin{aligned}{\bar {w}}_{5}&=1\quad {\text{(seed)}}\\{\bar {w}}_{4}&={\bar {w}}_{5}\cdot 1\\{\bar {w}}_{3}&={\bar {w}}_{5}\cdot 1\\{\bar {w}}_{2}&={\bar {w}}_{3}\cdot w_{1}\\{\bar {w}}_{1}&={\bar {w}}_{3}\cdot w_{2}+{\bar {w}}_{4}\cdot \cos w_{1}\end{aligned}}}Las derivadas parciales buscadas son entoncesy/incógnita1=w¯1{\displaystyle \partial y/\partial x_{1}={\bar {w}}_{1}}yy/incógnita2=w¯2{\displaystyle \partial y/\partial x_{2}={\bar {w}}_{2}}.

El grafo de flujo de datos de un cálculo puede manipularse para calcular el gradiente de su cálculo original. Esto se hace agregando un nodo adjunto por cada nodo primario, conectados por aristas adjuntas que son paralelas a las aristas primarias pero fluyen en la dirección opuesta. Los nodos en el grafo adjunto representan la multiplicación por las derivadas de las funciones calculadas por los nodos en el primario. Por ejemplo, la suma en el primario produce un fanout en el adjunto; el fanout en el primario produce una suma en el adjunto; [ a ] una función unaria y = f ( x ) en el primario produce = ȳ f '( x ) en el adjunto; etc.

Implementación

Pseudocódigo

La acumulación inversa requiere dos pasadas: En la pasada hacia adelante, la función se evalúa primero y los resultados parciales se almacenan en caché. En la pasada inversa, se calculan las derivadas parciales y el valor derivado previamente se retropropaga. La llamada al método correspondiente espera que la expresión Z se derive y se inicialice con el valor derivado de la expresión padre. Para la expresión superior, Z se diferencia con respecto a Z , que es 1. El método recorre el árbol de expresiones recursivamente hasta que se alcanza una variable y agrega el valor inicial actual a la expresión derivada. [ 18 ] [ 19 ]

void derive ( Expression Z , float seed ) { if isVariable ( Z ) partialDerivativeOf ( Z ) += seed ; else if ( Z = A + B ) derive ( A , seed ); derive ( B , seed ); else if ( Z = A - B ) derive ( A , seed ); derive ( B , -seed ); else if ( Z = A * B ) derive ( A , valueOf ( B ) * seed ); derive ( B , valueOf ( A ) * seed ) ; }
C++
#include <iostream> struct Expression { float value ; virtual void evaluate () = 0 ; virtual void derive ( float seed ) = 0 ; }; struct Variable : public Expression { float partial ; Variable ( float value ) { this -> value = value ; partial = 0.0f ; } void evaluate () {} void derive ( float seed ) { partial += seed ; } }; struct Plus : public Expression { Expression & a , & b ; Plus ( Expression & a , Expression & b ) : a ( a ), b ( b ) {} void evaluate () { a . evaluate (); b . evaluate (); value = a . value + b . value ; } void derive ( float seed ) { a . derive ( seed ); b . derive ( seed ); } }; struct Multiply : public Expression { Expression & a , & b ; Multiplicar ( Expresión & a , Expresión & b ) : a ( a ), b ( b ) {} void evaluar () { a . evaluar(); b . evaluar (); valor = a . valor * b . valor ; } void deriva ( float semilla ) { a . deriva ( b . valor * semilla ); b . deriva ( a . valor * semilla ); } }; int main () { // Ejemplo: Encontrar los parciales de z = x * (x + y) + y * y en (x, y) = (2, 3) Variable x ( 2 ), y ( 3 ); Más p1 ( x , y ); Multiplicar m1 ( x , p1 ); Multiplicar m2 ( y , y ); Más z ( m1 , m2 ); z . evaluar (); std :: cout << "z = " << z . valor << std :: endl ; // Salida: z = 19 z . deriva ( 1 ); std :: cout << "∂z/∂x = " << x . parcial << ", " << "∂z/∂y = " << y . parcial << std :: endl ; // Salida: ∂z/∂x = 7, ∂z/∂y = 8 return 0 ; }

Más allá de la acumulación directa e inversa

La acumulación hacia adelante y hacia atrás son solo dos formas (extremas) de recorrer la regla de la cadena. El problema de calcular un jacobiano completo de f  : ℝ n → ℝ m con un número mínimo de operaciones aritméticas se conoce como el problema de acumulación óptima del jacobiano (OJA), que es NP-completo . [ 20 ] Central para esta demostración es la idea de que pueden existir dependencias algebraicas entre los parciales locales que etiquetan las aristas del grafo. En particular, dos o más etiquetas de arista pueden reconocerse como iguales. La complejidad del problema aún está abierta si se supone que todas las etiquetas de arista son únicas y algebraicamente independientes.

Diferenciación automática mediante números duales

La diferenciación automática en modo directo se logra mediante la ampliación del álgebra de los números reales y la obtención de una nueva aritmética . Se añade un componente adicional a cada número para representar la derivada de una función en dicho número, y todos los operadores aritméticos se extienden para el álgebra ampliada. El álgebra ampliada es el álgebra de los números duales .

Reemplazar cada númeroincógnita{\displaystyle \,x}con el númeroincógnita+incógnitaε{\displaystyle x+x'\varepsilon }, dóndeincógnita{\displaystyle x'}es un número real, peroε{\displaystyle \varepsilon }es un número abstracto con la propiedadε2=0{\displaystyle \varepsilon ^{2}=0}(un infinitesimal ; véase Análisis infinitesimal suave ). Usando solo esto, la aritmética regular da (incógnita+incógnitaε)+(y+yε)=incógnita+y+(incógnita+y)ε(incógnita+incógnitaε)(y+yε)=incógnitay+(incógnitay)ε(incógnita+incógnitaε)(y+yε)=incógnitay+incógnitayε+yincógnitaε+incógnitayε2=incógnitay+(incógnitay+yincógnita)εincógnita+incógnitaεy+yε=incógnitay+incógnitaεy1+yεy=(incógnitay+incógnitaεy)(1yεy)=incógnitay+(incógnitayincógnitayy2)ε{\displaystyle {\begin{aligned}(x+x'\varepsilon )+(y+y'\varepsilon )&=x+y+(x'+y')\varepsilon \\[4px](x+x'\varepsilon )-(y+y'\varepsilon )&=x-y+(x'-y')\varepsilon \\[4px](x+x'\varepsilon )\cdot (y+y'\varepsilon )&=xy+xy'\varepsilon +yx'\varepsilon +x'y'\varepsilon ^{2}=xy+(xy'+yx')\varepsilon \\[8px]{\frac {x+x'\varepsilon }{y+y'\varepsilon }}&={\frac {{\frac {x}{y}}+{\frac {x'\varepsilon }{y}}}{1+{\frac {y'\varepsilon }{y}}}}=\left({\frac {x}{y}}+{\frac {x'\varepsilon }{y}}\right)\cdot \left(1-{\frac {y'\varepsilon }{y}}\right)={\frac {x}{y}}+\left({\frac {x'}{y}}-{\frac {xy'}{y^{2}}}\right)\varepsilon \end{aligned}}} utilizando el hecho de que (1+yεy)(1yεy)=1.{\displaystyle \left(1+{\frac {y'\varepsilon }{y}}\right)\cdot \left(1-{\frac {y'\varepsilon }{y}}\right)=1.}

Ahora, los polinomios se pueden calcular en esta aritmética aumentada. Si PAG(incógnita)=pag0+pag1incógnita+pag2incógnita2++pagnorteincógnitanorte,{\displaystyle P(x)=p_{0}+p_{1}x+p_{2}x^{2}+\cdots +p_{n}x^{n},} entonces PAG(incógnita+incógnitaε)=pag0+pag1(incógnita+incógnitaε)++pagnorte(incógnita+incógnitaε)norte=pag0+pag1incógnita++pagnorteincógnitanorte+pag1incógnitaε+2pag2incógnitaincógnitaε++nortepagnorteincógnitanorte1incógnitaε=PAG(incógnita)+PAG(1)(incógnita)incógnitaε{\displaystyle {\begin{aligned}P(x+x'\varepsilon )&=p_{0}+p_{1}(x+x'\varepsilon )+\cdots +p_{n}(x+x'\varepsilon )^{n}\\&=p_{0}+p_{1}x+\cdots +p_{n}x^{n}+p_{1}x'\varepsilon +2p_{2}xx'\varepsilon +\cdots +np_{n}x^{n-1}x'\varepsilon \\&=P(x)+P^{(1)}(x)x'\varepsilon \end{aligned}}} dóndePAG(1){\displaystyle P^{(1)}}denota la derivada dePAG{\displaystyle P}con respecto a su primer argumento, yincógnita{\displaystyle x'}, llamada semilla , puede elegirse arbitrariamente.

La nueva aritmética consiste en pares ordenados , elementos escritosincógnita,incógnita{\textstyle \langle x,x'\rangle }, con aritmética ordinaria en la primera componente y aritmética de diferenciación de primer orden en la segunda componente, como se describió anteriormente. Extendiendo los resultados anteriores sobre polinomios a funciones analíticas se obtiene una lista de la aritmética básica y algunas funciones estándar para la nueva aritmética: ,+v,v=+v,+v,v,v=v,v,v,v=v,v+v,v,v=v,vvv2(v0)pecado,=pecado(),porque()porque,=porque(),pecado()mi,=mi,miregistro,=registro(),(>0),k=k,kk1(0)|,|=||,sgn(0){\displaystyle {\begin{aligned}\left\langle u,u'\right\rangle +\left\langle v,v'\right\rangle &=\left\langle u+v,u'+v'\right\rangle \\[4px]\left\langle u,u'\right\rangle -\left\langle v,v'\right\rangle &=\left\langle u-v,u'-v'\right\rangle \\[4px]\left\langle u,u'\right\rangle \cdot \left\langle v,v'\right\rangle &=\left\langle uv,u'v+uv'\right\rangle \\[8px]{\frac {\left\langle u,u'\right\rangle }{\left\langle v,v'\right\rangle }}&=\left\langle {\frac {u}{v}},{\frac {u'v-uv'}{v^{2}}}\right\rangle &&(v\neq 0)\\[8px]\sin \left\langle u,u'\right\rangle &=\left\langle \sin(u),u'\cos(u)\right\rangle \\[4px]\cos \left\langle u,u'\right\rangle &=\left\langle \cos(u),-u'\sin(u)\right\rangle \\[4px]e^{\left\langle u,u'\right\rangle }&=\left\langle e^{u},u'e^{u}\right\rangle \\[8px]\log \left\langle u,u'\right\rangle &=\left\langle \log(u),{\frac {u'}{u}}\right\rangle &&(u>0)\\[8px]\left\langle u,u'\right\rangle ^{k}&=\left\langle u^{k},u'ku^{k-1}\right\rangle &&(u\neq 0)\\[8px]\left|\left\langle u,u'\right\rangle \right|&=\left\langle \left|u\right|,u'\operatorname {sgn} u\right\rangle &&(u\neq 0)\end{aligned}}} y en general para la función primitivagramo{\displaystyle g}, gramo(,,v,v)=gramo(,v),gramo(,v)+gramov(,v)v{\displaystyle g(\langle u,u'\rangle ,\langle v,v'\rangle )=\langle g(u,v),g_{u}(u,v)u'+g_{v}(u,v)v'\rangle } dóndegramo{\displaystyle g_{u}}ygramov{\displaystyle g_{v}}son los derivados degramo{\displaystyle g}con respecto a su primer y segundo argumento, respectivamente.

Cuando se aplica una operación aritmética básica binaria a argumentos mixtos, el par,{\textstyle \langle u,u'\rangle }y el número realdo{\displaystyle c}—el número real se eleva primero ado,0{\textstyle \langle c,0\rangle }La derivada de una funciónF:RR{\textstyle f:\mathbb {R} \to \mathbb {R} }en ese puntoincógnita0{\displaystyle x_{0}}ahora se encuentra calculandoF(incógnita0,1){\textstyle f(\langle x_{0},1\rangle )}utilizando la aritmética anterior, que daF(incógnita0),F(incógnita0){\textstyle \langle f(x_{0}),f'(x_{0})\rangle }como resultado.

Implementación

A continuación se muestra un ejemplo de implementación basado en el enfoque de números duales.

Pseudocódigo

Doble más (Doble A, Doble B) { devolver { realPartOf(A) + realPartOf(B), parte infinitesimal(A) + parte infinitesimal(B) }; } Dual menos (Dual A, Dual B) { devolver { realPartOf(A) - realPartOf(B), parte infinitesimal de (A) - parte infinitesimal de (B) }; } Multiplicar dual(dual A, dual B) { devolver { realPartOf(A) * realPartOf(B), ParterealDe(B) * ParteinfinitsimalDe(A) + ParterealDe(A) * ParteinfinitsimalDe(B) }; } X = {x, 0}; Y = {y, 0}; Épsilon = {0, 1}; xPartial = infinitesimalPartOf(f(X + Epsilon, Y)); yPartial = infinitesimalPartOf(f(X, Y + Epsilon));

C++

#include <iostream> struct Dual { float realPart , infinitesimalPart ; Dual ( float realPart , float infinitesimalPart = 0 ) : realPart ( realPart ), infinitesimalPart ( infinitesimalPart ) {} Dual operator + ( Dual other ) { return Dual ( realPart + other . realPart , infinitesimalPart + other . infinitesimalPart ); } Dual operator * ( Dual other ) { return Dual ( realPart * other . realPart , other . realPart * infinitesimalPart + realPart * other . infinitesimalPart ); } }; // Ejemplo: Encontrar las parciales de z = x * (x + y) + y * y en (x, y) = (2, 3) Dual f ( Dual x , Dual y ) { return x * ( x + y ) + y * y ; } int main () { Dual x = Dual ( 2 ); Dual y = Dual ( 3 ); Dual epsilon = Dual ( 0 , 1 ); Dual a = f ( x + epsilon , y ); Dual b = f ( x , y + epsilon ); std :: cout << "∂z/∂x = " << a . infinitesimalPart << ", " << "∂z/∂y = " << b . infinitesimalPart <<std :: endl ; // Salida: ∂z/∂x = 7, ∂z/∂y = 8 return 0 ; }

Argumentos y funciones vectoriales

Las funciones multivariadas pueden manejarse con la misma eficiencia y mecanismos que las funciones univariadas mediante la adopción de un operador de derivada direccional . Es decir, si es suficiente calculary=F(incógnita)incógnita{\textstyle y'=\nabla f(x)\cdot x'}, la derivada direccionalyRmetro{\textstyle y'\in \mathbb {R} ^{m}}deF:RnorteRmetro{\textstyle f:\mathbb {R} ^{n}\to \mathbb {R} ^{m}}enincógnitaRnorte{\textstyle x\in \mathbb {R} ^{n}}en la direcciónincógnitaRnorte{\textstyle x'\in \mathbb {R} ^{n}}puede calcularse como (y1,y1,,ymetro,ymetro)=F(incógnita1,incógnita1,,incógnitanorte,incógnitanorte){\displaystyle (\langle y_{1},y'_{1}\rangle ,\ldots ,\langle y_{m},y'_{m}\rangle )=f(\langle x_{1},x'_{1}\rangle ,\ldots ,\langle x_{n},x'_{n}\rangle )} utilizando la misma aritmética que arriba. Si todos los elementos deF{\displaystyle \nabla f}son deseados, entoncesnorte{\displaystyle n}Se requieren evaluaciones de funciones. Cabe señalar que, en muchas aplicaciones de optimización, la derivada direccional es suficiente.

Alto orden y muchas variables

La aritmética anterior puede generalizarse para calcular derivadas de segundo orden y superiores de funciones multivariables. Sin embargo, las reglas aritméticas se complican rápidamente: la complejidad es cuadrática en el grado de derivada más alto. En su lugar, se puede utilizar el álgebra de polinomios de Taylor truncada . La aritmética resultante, definida sobre números duales generalizados, permite un cálculo eficiente utilizando funciones como si fueran un tipo de dato. Una vez conocido el polinomio de Taylor de una función, las derivadas se extraen fácilmente.

Implementación

La AD en modo directo se implementa mediante una interpretación no estándar del programa en la que los números reales se reemplazan por números duales, las constantes se convierten en números duales con un coeficiente épsilon cero y las primitivas numéricas se adaptan para operar con números duales. Esta interpretación no estándar generalmente se implementa utilizando una de dos estrategias: transformación del código fuente o sobrecarga de operadores .

transformación del código fuente (SCT)

Figura 4: Ejemplo de cómo podría funcionar la transformación del código fuente.

El código fuente de una función se reemplaza por un código fuente generado automáticamente que incluye instrucciones para calcular las derivadas intercaladas con las instrucciones originales.

La transformación del código fuente se puede implementar en todos los lenguajes de programación, y también facilita al compilador la optimización en tiempo de compilación. Sin embargo, la implementación de la herramienta AD en sí es más difícil y el sistema de compilación es más complejo.

Sobrecarga del operador (OO)

Figura 5: Ejemplo de cómo podría funcionar la sobrecarga de operadores.

La sobrecarga de operadores es una posibilidad para el código fuente escrito en un lenguaje que la admita. Los objetos para números reales y operaciones matemáticas elementales deben sobrecargarse para adaptarse a la aritmética aumentada descrita anteriormente. Esto no requiere cambios en la forma ni en la secuencia de operaciones del código fuente original para que la función se diferencie, pero a menudo requiere cambios en los tipos de datos básicos para números y vectores para admitir la sobrecarga, y con frecuencia también implica la inserción de operaciones de marcado especiales. Debido a la sobrecarga inherente de operadores en cada bucle, este enfoque suele presentar un rendimiento de velocidad inferior.

Sobrecarga de operadores y transformación del código fuente

Los operadores sobrecargados pueden utilizarse para extraer el gráfico de valoración, seguido de la generación automática de la versión AD de la función primal en tiempo de ejecución. A diferencia del AAD orientado a objetos clásico , dicha función AD no cambia de una iteración a la siguiente. Por lo tanto, no existe ninguna sobrecarga de tiempo de ejecución de interpretación orientada a objetos o de cinta por muestra Xi.

Al generarse la función AD en tiempo de ejecución, se puede optimizar para tener en cuenta el estado actual del programa y precalcular ciertos valores. Además, se puede generar de forma que utilice de manera consistente la vectorización nativa de la CPU para procesar bloques de datos de usuario de 4(8)-dobles (AVX2/AVX512 acelera de 4 a 8 veces). Con la incorporación del procesamiento multihilo, este enfoque puede conducir a una aceleración final del orden de 8 × #Núcleos en comparación con las herramientas AAD tradicionales. Hay una implementación de referencia disponible en GitHub . [ 21 ]

Véase también

Notas

  1. En términos de matrices de peso, la adjunta es la transpuesta . La suma es el covector.[11]{\displaystyle {\begin{bmatrix}1&\cdots &1\end{bmatrix}}}, desde[11][incógnita1incógnitanorte]=incógnita1++incógnitanorte{\textstyle {\begin{bmatrix}1&\cdots &1\end{bmatrix}}{\begin{bmatrix}x_{1}\\\vdots \\x_{n}\end{bmatrix}}=x_{1}+\cdots +x_{n}}y fanout es el vector[11]{\textstyle {\begin{bmatrix}1\\\vdots \\1\end{bmatrix}}}, desde[11][incógnita]=[incógnitaincógnita]{\textstyle {\begin{bmatrix}1\\\vdots \\1\end{bmatrix}}{\begin{bmatrix}x\end{bmatrix}}={\begin{bmatrix}x\\\vdots \\x\end{bmatrix}}}.

Referencias

  1. Neidinger, Richard D. (2010). "Introducción a la diferenciación automática y la programación orientada a objetos en MATLAB" (PDF) . SIAM Review . 52 (3): 545– 563. CiteSeerX 10.1.1.362.6580 . doi : 10.1137/080743627 . S2CID 17134969 .  
  2. 1 2 Baydin, Atilim Gunes; Pearlmutter, Barak; Radul, Alexey Andreyevich; Siskind, Jeffrey (2018). "Diferenciación automática en aprendizaje automático: una revisión" . Journal of Machine Learning Research . 18 : 1–43 .
  3. 1 2 3 4 5 Hend Dawood y Nefertiti Megahed (2023). Diferenciación automática de incertidumbres: una diferenciación computacional de intervalos para derivadas de primer orden y superiores con implementación. PeerJ Computer Science 9:e1301 https://doi.org/10.7717/peerj-cs.1301 .
  4. 1 2 3 4 Hend Dawood y Nefertiti Megahed (2019). Una axiomatización consistente y categórica de la aritmética de diferenciación aplicable a derivadas de primer orden y de orden superior. Punjab University Journal of Mathematics. 51(11). pp. 77-100. doi: 10.5281/zenodo.3479546. http://doi.org/10.5281/zenodo.3479546 .
  5. 1 2 Este artículo incorpora texto de Dawood y Megahed disponible bajo la licencia CC BY-SA 4.0 . 
  6. Hend Dawood y Yasser Dawood (2022). Búsqueda de raíces de intervalos y polinomios de intervalos: métodos y aplicaciones en ciencia e ingeniería. En S. Chakraverty, editor, Paradigmas polinomiales: tendencias y aplicaciones en ciencia e ingeniería, capítulo 15. IOP Publishing. ISBN 978-0-7503-5065-5. doi: 10.1088/978-0-7503-5067-9ch15. URL https://doi.org/10.1088/978-0-7503-5067-9ch15 .
  7. Siegfried M. Rump (1999). INTLAB–INTerval LABoratory. En T. Csendes, editor, Developments in Reliable Computing, páginas 77–104. Kluwer Academic Publishers, Dordrecht.
  8. S. Chevillard, M. Joldes y C. Lauter. Sollya (2010). Un entorno para el desarrollo de códigos numéricos. En K. Fukuda, J. van der Hoeven, M. Joswig y N. Takayama, editores, Mathematical Software - ICMS 2010, volumen 6327 de Lecture Notes in Computer Science, páginas 28–31, Heidelberg, Alemania. Springer.
  9. Hend Dawood (2022). InCLosure (Interval enCLosure): un lenguaje y entorno para la computación científica fiable. Software informático, versión 4.0. Departamento de Matemáticas. Facultad de Ciencias, Universidad de El Cairo, Giza, Egipto, septiembre de 2022. url: https://doi.org/10.5281/zenodo.2702404 .
  10. Christian P. Fries (2019). Diferenciación automática estocástica: Diferenciación automática para simulaciones de Montecarlo. Quantitative Finance, 19(6):1043–1059. doi: 10.1080/14697688.2018.1556398. url: https://doi.org/10.1080/14697688.2018.1556398 .
  11. Hend Dawood y Yasser Dawood (2020). Intervalos universales: Hacia un álgebra de intervalos que tenga en cuenta las dependencias. En S. Chakraverty, editor, Métodos matemáticos en ciencias interdisciplinarias. Capítulo 10, páginas 167–214. John Wiley & Sons, Hoboken, Nueva Jersey. ISBN 978-1-119-58550-3. doi: 10.1002/9781119585640.ch10. url: https://doi.org/10.1002/9781119585640.ch10 .
  12. Hend Dawood (2014). Las matemáticas de intervalo como arma potencial contra la incertidumbre. En S. Chakraverty, editor, Matemáticas del modelado de la incertidumbre en el análisis de problemas de ingeniería y ciencias. Capítulo 1, páginas 1–38. IGI Global, Hershey, PA. ISBN 978-1-4666-4991-0.
  13. 1 2 R.E. Wengert (1964). "Un programa sencillo de evaluación automática de derivadas" . Comm. ACM . 7 (8): 463– 464. doi : 10.1145/355586.364791 . S2CID 24039274 . 
  14. Griewank, Andreas (2012). "¿Quién inventó el modo inverso de diferenciación?" (PDF) . Optimization Stories . Documenta Mathematica Series. Vol. 6. pp. 389–400 . doi : 10.4171/dms/6/38 . ISBN   978-3-936609-58-5.
  15. Linnainmaa, Seppo (1976). "Expansión de Taylor del error de redondeo acumulado". BIT Numerical Mathematics . 16 (2): 146– 160. doi : 10.1007/BF01931367 . S2CID 122357351 . 
  16. Maximilian E. Schüle, Maximilian Springer, Alfons Kemper , Thomas Neumann (2022). «Optimización de código LLVM para la diferenciación automática». Actas del Sexto Taller sobre Gestión de Datos para el Aprendizaje Automático Integral . págs. 1–4 . doi : 10.1145/3533028.3533302 . ISBN  9781450393751. S2CID 248853034 . {{cite book}}: CS1 maint: varios nombres: lista de autores ( enlace )
  17. Bartholomew-Biggs, Michael; Brown, Steven; Christianson, Bruce; Dixon, Laurence (2000). "Diferenciación automática de algoritmos". Journal of Computational and Applied Mathematics . 124 ( 1– 2): 171– 190. Bibcode : 2000JCoAM.124..171B . doi : 10.1016/S0377-0427(00)00422-2 . hdl : 2299/3010 .
  18. Maximilian E. Schüle, Harald Lang, Maximilian Springer, Alfons Kemper , Thomas Neumann , Stephan Günnemann (2021). «Aprendizaje automático en bases de datos con SQL en GPU». 33.ª Conferencia Internacional sobre Gestión de Bases de Datos Científicas y Estadísticas . pp. 25–36 . doi : 10.1145/3468791.3468840 . ISBN  9781450384131. S2CID 235386969 . {{cite book}}: CS1 maint: varios nombres: lista de autores ( enlace )
  19. Maximilian E. Schüle, Harald Lang, Maximilian Springer, Alfons Kemper , Thomas Neumann , Stephan Günnemann (2022). "SQL recursivo y soporte de GPU para aprendizaje automático en bases de datos" . Bases de datos distribuidas y paralelas . 40 ( 2–3 ): 205–259 . doi : 10.1007/s10619-022-07417-7 . S2CID 250412395 . {{cite journal}}: CS1 maint: varios nombres: lista de autores ( enlace )
  20. Naumann, Uwe (abril de 2008). "La acumulación jacobiana óptima es NP-completa". Mathematical Programming . 112 (2): 427– 441. CiteSeerX 10.1.1.320.5665 . doi : 10.1007/s10107-006-0042-z . S2CID 30219572 .  
  21. "Biblioteca de prototipos AADC" . 22 de junio de 2022 vía GitHub.

Lecturas adicionales

  • Rall, Louis B. (1981). Diferenciación automática: técnicas y aplicaciones . Lecture Notes in Computer Science. Vol.  120. Springer . ISBN 978-3-540-10861-0.
  • Griewank, Andreas; Walther, Andrea (2008). Evaluación de derivadas: Principios y técnicas de diferenciación algorítmica . Otros títulos en matemáticas aplicadas. Vol.  105 (2.ª  ed.). SIAM . doi : 10.1137/1.9780898717761 . ISBN 978-0-89871-659-7.
  • Neidinger, Richard (2010). "Introducción a la diferenciación automática y la programación orientada a objetos en MATLAB" (PDF) . SIAM Review . 52 (3): 545– 563. CiteSeerX 10.1.1.362.6580 . doi : 10.1137/080743627 . S2CID 17134969. Recuperado el 15 de marzo de 2013 .  
  • Naumann, Uwe (2012). El arte de diferenciar programas informáticos . Software-Environments-tools. SIAM . ISBN 978-1-611972-06-1.
  • Henrard, Marc (2017). Diferenciación algorítmica en finanzas explicada . Ingeniería financiera explicada. Palgrave Macmillan . ISBN 978-3-319-53978-2.
  • www.autodiff.org , un "sitio web de entrada a todo lo que quieres saber sobre la diferenciación automática".
  • Diferenciación automática de programas OpenMP paralelos
  • Diferenciación automática, plantillas C++ y fotogrametría
  • Diferenciación automática, enfoque de sobrecarga del operador
  • Calcula derivadas analíticas de cualquier programa Fortran77, Fortran95 o C a través de una interfaz web. Diferenciación automática de programas Fortran.
  • Descripción y código de ejemplo para la diferenciación automática hacia adelante en Scala. Archivado el 3 de agosto de 2016 en Wayback Machine.
  • finmath-lib diferenciación automática estocástica , diferenciación automática para variables aleatorias (implementación en Java de la diferenciación automática estocástica).
  • Diferenciación algorítmica adjunta: calibración y teorema de la función implícita
  • Artículo e implementación sobre diferenciación automática basada en plantillas de C++
  • Derivados depurables de código fuente a código fuente de Tangent
  • Griegas exactas de primer y segundo orden mediante diferenciación algorítmica.
  • Diferenciación algorítmica adjunta de una aplicación acelerada por GPU
  • Métodos adjuntos en finanzas computacionales: soporte de herramientas de software para la diferenciación algorítmica.
  • Aceleración de más de mil veces en los cálculos de precios de xVA con los procesadores Intel Xeon Scalable.
  • Implementación de la serie de Taylor truncada dispersa con ejemplo VBIC95 para derivadas de orden superior.