Demonstração de retropropagação
$$ \definecolor{input}{RGB}{66, 133, 244} \definecolor{output}{RGB}{219, 68, 55} \definecolor{dinput}{RGB}{244, 180, 0} \definecolor{doutput}{RGB}{15, 157, 88} \definecolor{dweight}{RGB}{102, 0, 255} $$

Algoritmo de retropropagação

O algoritmo de retropropagação é essencial para treinar rapidamente grandes redes neurais. Neste artigo, explicamos como o algoritmo funciona.

Role para baixo...

Rede neural simples

À direita, você vê uma rede neural com uma entrada, um nó de saída e duas camadas ocultas de dois nós.

Os nós em camadas vizinhas são conectados com pesos \(w_{ij}\), que são os parâmetros de rede.

Função de ativação

Cada nó tem uma entrada total \(\color{input}x\), uma função de ativação \(f(\color{input}x\color{black})\)e uma saída \(\color{output}y\color{black}=f(\color{input}x\color{black})\). \(f(\color{input}x\color{black})\) precisa ser uma função não linear. Caso contrário, a rede neural só poderá aprender modelos lineares.

Uma função de ativação usada com frequência é a função sigmoide: \(f(\color{input}x\color{black}) = \frac{1}{1+e^{-\color{input}x}}\).

Função erro

A meta é aprender os pesos da rede automaticamente a partir dos dados para que a saída prevista \(\color{output}y_{output}\) esteja próxima do destino \(\color{output}y_{target}\) para todas as entradas \(\color{input}x_{input}\).

Para medir a distância da meta, usamos uma função de erro \(E\). Uma função de erro usada com frequência é \(E(\color{output}y_{output}\color{black},\color{output}y_{target}\color{black}) = \frac{1}{2}(\color{output}y_{output}\color{black} - \color{output}y_{target}\color{black})^2 \).

Propagação para frente

Começamos com um exemplo de entrada \((\color{input}x_{input}\color{black},\color{output}y_{target}\color{black})\) e atualizamos a camada de entrada da rede.

Para manter a consistência, consideramos a entrada como qualquer outro nó, mas sem uma função de ativação. Portanto, a saída é igual à entrada, ou seja, \( \color{output}y_1 \color{black} = \color{input} x_{input} \).

Propagação para frente

Agora, atualizamos a primeira camada escondida. Usamos a saída \(\color{output}y\) dos nós na camada anterior e usamos os pesos para calcular a entrada \(\color{input}x\) dos nós na camada seguinte.
$$ \color{input} x_j \color{black} = $$$$ \sum_{i\in in(j)} w_{ij}\color{output} y_i\color{black} +b_j$$

Propagação para frente

Depois atualizamos a saída dos nós na primeira camada escondida. Para isso, usamos a função de ativação, \( f(x) \).
$$ \color{output} y \color{black} = f(\color{input} x \color{black})$$

Propagação para frente

Usando essas duas fórmulas, propagamos o resto da rede e recebemos a saída final.
$$ \color{output} y \color{black} = f(\color{input} x \color{black})$$
$$ \color{input} x_j \color{black} = $$$$ \sum_{i\in in(j)} w_{ij}\color{output} y_i \color{black} + b_j$$

Derivado ao erro

O algoritmo de retropropagação decide quanto atualizar cada peso da rede após comparar a saída prevista com a saída desejada para um exemplo específico. Para isso, precisamos calcular como o erro muda em relação a cada peso \(\color{dweight}\frac{dE}{dw_{ij}}\).
Depois de usar os derivados de erros, podemos atualizar os pesos usando uma regra de atualização simples:
$$w_{ij} = w_{ij} - \alpha \color{dweight}\frac{dE}{dw_{ij}}$$
em que \(\alpha\) é uma constante positiva, chamada de taxa de aprendizado, que precisamos ajustar empiricamente.

[Observação] A regra de atualização é muito simples: se o erro diminuir quando o peso aumentar (\(\color{dweight}\frac{dE}{dw_{ij}}\color{black} < 0\)), aumente o peso. Caso contrário, se o erro aumentar quando o peso aumentar (\(\color{dweight}\frac{dE}{dw_{ij}} \color{black} > 0\)), então diminua o peso.

Derivados adicionais

Para ajudar a calcular \(\color{dweight}\frac{dE}{dw_{ij}}\), também armazenamos para cada nó mais dois derivados: como o erro muda com:
  • a entrada total do nó \(\color{dinput}\frac{dE}{dx}\) e
  • a saída do nó \(\color{doutput}\frac{dE}{dy}\).

Propagação de retorno

Vamos começar a propagar a retropropagação dos derivados de erros. Como temos a saída prevista desse exemplo de entrada específica, podemos calcular como o erro muda com essa saída. Considerando nossa função de erro \(E = \frac{1}{2}(\color{output}y_{output}\color{black} - \color{output}y_{target}\color{black})^2\) :
$$ \color{doutput} \frac{\partial E}{\partial y_{output}} \color{black} = \color{output} y_{output} \color{black} - \color{output} y_{target}$$

Propagação de retorno

Agora que temos \(\color{doutput} \frac{dE}{dy}\) podemos \(\color{dinput}\frac{dE}{dx}\) usar a regra da cadeia.
$$\color{dinput} \frac{\partial E}{\partial x} \color{black} = \frac{dy}{dx}\color{doutput}\frac{\partial E}{\partial y} \color{black} = \frac{d}{dx}f(\color{input}x\color{black})\color{doutput}\frac{\partial E}{\partial y}$$
em que \(\frac{d}{dx}f(\color{input}x\color{black}) = f(\color{input}x\color{black})(1 - f(\color{input}x\color{black}))\) quando \(f(\color{input}x\color{black})\) é a função de ativação Sigmoid.

Propagação de retorno

Assim que tivermos o derivado de erro em relação à entrada total de um nó, poderemos ver o derivado de erro em relação aos pesos que chegam a esse nó.
$$\color{dweight} \frac{\partial E}{\partial w_{ij}} \color{black} = \frac{\partial x_j}{\partial w_{ij}} \color{dinput}\frac{\partial E}{\partial x_j} \color{black} = \color{output}y_i \color{dinput} \frac{\partial E}{\partial x_j}$$

Propagação de retorno

Usando a regra de cadeia, também é possível conseguir \(\frac{dE}{dy}\) da camada anterior. Fizemos um círculo completo.
$$ \color{doutput} \frac{\partial E}{\partial y_i} \color{black} = \sum_{j\in out(i)} \frac{\partial x_j}{\partial y_i} \color{dinput} \frac{\partial E}{\partial x_j} \color{black} = \sum_{j\in out(i)} w_{ij} \color{dinput} \frac{\partial E}{\partial x_j}$$

Propagação de retorno

Só falta repetir as três fórmulas anteriores até calcularmos todos os derivados de erros.

Fim.

Computando...