$$
\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.