Exemplo passo a passo da diferenciação automática no modo reverso

27

Não tenho certeza se essa pergunta pertence aqui, mas está intimamente relacionada aos métodos de gradiente na otimização, que parecem estar no tópico aqui. De qualquer forma, sinta-se à vontade para migrar se você achar que alguma outra comunidade possui uma melhor experiência no tópico.

Em resumo, estou procurando um exemplo passo a passo da diferenciação automática no modo reverso . Não há muita literatura sobre o assunto e a implementação existente (como a do TensorFlow ) é difícil de entender sem conhecer a teoria por trás disso. Assim, eu ficaria muito grato se alguém pudesse mostrar em detalhes o que passar , como podemos processá-lo e o que tirar do gráfico computacional.

Algumas perguntas com as quais tenho mais dificuldade:

  • sementes - por que precisamos delas?
  • regras de diferenciação reversa - eu sei como fazer diferenciação para a frente, mas como vamos para trás? Por exemplo, no exemplo desta seção , como sabemos que ?w2¯=w3¯w1
  • trabalhamos apenas com símbolos ou passamos por valores reais ? Por exemplo , no mesmo exemplo , são os símbolos ou valores e ?wiwi¯
amiga
fonte
"Aprendizado prático da máquina com o Scikit-Learn e o TensorFlow" O apêndice D fornece uma explicação muito boa na minha opinião. Eu recomendo.
Agustin Barrachina 18/07

Respostas:

37

Digamos que temos a expressão e queremos encontrar derivadas e . O AD de modo reverso divide essa tarefa em 2 partes, a saber, passes para frente e para trás.z=x1x2+sin(x1)dzdx1dzdx2

Passar para a frente

Primeiro, decompomos nossa expressão complexa em um conjunto de expressões primitivas, ou seja, expressões que consistem em, no máximo, uma chamada de função. Observe que eu também renomeio variáveis ​​de entrada e saída para obter consistência, embora não seja necessário:

w1=x1
w2=x2
w3=w1w2
w4=sin(w1)
w5=w3+w4
z=w5

A vantagem dessa representação é que as regras de diferenciação para cada expressão separada já são conhecidas. Por exemplo, sabemos que a derivada de é e, portanto, . Usaremos esse fato no passe inverso abaixo.sincosdw4dw1=cos(w1)

Essencialmente, o encaminhamento consiste em avaliar cada uma dessas expressões e salvar os resultados. Digamos, nossas entradas são: e . Então nós temos:x1=2x2=3

w1=x1=2
w2=x2=3
w3=w1w2=6
w4=sin(w1) =0.9
w5=w3+w4=6.9
z=w5=6.9

Passagem reversa

Aqui é onde a mágica começa, e começa com a regra da cadeia . Em sua forma básica, a regra da cadeia declara que, se você tiver a variável que depende de que, por sua vez, depende de , então:t(u(v))uv

dtdv=dtdududv

ou, se depende de através de vários caminhos / variáveis , por exemplo:tvui

u1=f(v)
u2=g(v)
t=h(u1,u2)

então (veja a prova aqui ):

dtdv=idtduiduidv

Em termos de gráfico de expressão, se temos um nó final e nós de entrada , e o caminho de para passa pelos nós intermediários (ou seja, onde ), podemos encontrar derivativos comozwizwiwpz=g(wp)wp=f(wi)dzdwi

dzdwi=pparents(i)dzdwpdwpdwi

Em outras palavras, para calcular a derivada da variável de saída qualquer variável intermediária ou de entrada , precisamos apenas conhecer as derivadas de seus pais e a fórmula para calcular a derivada da expressão primitiva .zwiwp=f(wi)

A passagem reversa começa no final (ou seja, ) e se propaga para trás para todas as dependências. Aqui temos (expressão para "semente"):dzdz

dzdz=1

Isso pode ser lido como "mudança em resulta exatamente na mesma mudança em ", o que é bastante óbvio.zz

Então sabemos que e assim:z=w5

dzdw5=1

w5 depende linearmente de e , então e . Usando a regra da cadeia, encontramos:w3w4dw5dw3=1dw5dw4=1

dzdw3=dzdw5dw5dw3=1×1=1
dzdw4=dzdw5dw5dw4=1×1=1

A partir da definição e das regras de derivadas parciais, descobrimos que . Portanto:w3=w1w2dw3dw2=w1

dzdw2=dzdw3dw3dw2=1×w1=w1

Que, como já sabemos do passe para frente, é:

dzdw2=w1=2

Finalmente, contribui para via e . Mais uma vez, pelas regras das derivadas parciais, sabemos que e . Portanto:w1zw3w4dw3dw1=w2dw4dw1=cos(w1)

dzdw1=dzdw3dw3dw1+dzdw4dw4dw1=w2+cos(w1)

E, novamente, dadas as entradas conhecidas, podemos calculá-lo:

dzdw1=w2+cos(w1)=3+cos(2) =2.58

Como e são apenas aliases para e , obtemos nossa resposta:w1w2x1x2

dzdx1=2.58
dzdx2=2

E é isso!


Esta descrição refere-se apenas a entradas escalares, ou seja, números, mas na verdade também pode ser aplicada a matrizes multidimensionais, como vetores e matrizes. Duas coisas que devemos ter em mente ao diferenciar expressões com esses objetos:

  1. Os derivados podem ter uma dimensionalidade muito maior do que as entradas ou saídas, por exemplo, derivada do vetor wrt vetor é uma matriz e derivada da matriz wrt é uma matriz quadridimensional (às vezes chamada de tensor). Em muitos casos, esses derivados são muito escassos.
  2. Cada componente na matriz de saída é uma função independente de 1 ou mais componentes da (s) matriz (s) de entrada. Por exemplo, se e ambos e são vectores, não depende , mas apenas no subconjunto de . Em particular, isso significa que encontrar a derivada resume a rastrear como depende de .y=f(x)xyyiyjxkdyidxjyixj

O poder da diferenciação automática é que ele pode lidar com estruturas complicadas de linguagens de programação como condições e loops. No entanto, se tudo o que você precisa são expressões algébricas e você possui uma estrutura boa o suficiente para trabalhar com representações simbólicas, é possível construir expressões totalmente simbólicas. De fato, neste exemplo, podemos produzir a expressão e calcular essa derivada para quaisquer entradas que desejemos.dzdw1=w2+cos(w1)=x2+cos(x1)

amiga
fonte
1
Pergunta / resposta muito útil. Obrigado. Apenas uma crítica litte: você parece mover-se sobre uma estrutura de árvore sem explicar (que é quando você começar a falar sobre os pais, etc ..)
MadHatter
1
Também não vai doer esclarecer por que precisamos de sementes.
MadHatter 28/05
@ MadHatter obrigado pelo comentário. Tentei reformular alguns parágrafos (estes que se referem aos pais) para enfatizar uma estrutura gráfica. Também adicionei "seed" ao texto, embora esse nome em si possa ser enganador na minha opinião: no AD seed sempre há uma expressão fixa - , não algo que você possa escolher ou gerar. dzdz=1
Ffriend
Obrigado! Notei que quando você precisa definir mais de uma "semente", geralmente escolhe 1 e 0. Gostaria de saber o porquê. Quero dizer, toma-se o "quociente" de um diferencial diferenciado, de modo que "1" é pelo menos intuitivamente justificado. Mas e quanto a 0? E se for preciso colher mais de 2 sementes?
MadHatter 30/05
1
Tanto quanto eu entendo, mais de uma semente é usada apenas no AD de modo avançado. Nesse caso, você define a semente como 1 para uma variável de entrada que deseja diferenciar em relação a e define a semente como 0 para todas as outras variáveis ​​de entrada, para que elas não contribuam para o valor de saída. No modo reverso, você define a semente como uma variável de saída e normalmente possui apenas uma variável de saída. Eu acho que você pode construir um pipeline de AD de modo reverso com várias variáveis ​​de saída e definir todas elas, exceto uma para 0, para obter o mesmo efeito que no modo de avanço, mas nunca investiguei essa opção.
Ffriend