Redes Neurais, Perceptron Multicamadas e o Algoritmo Backpropagation
Você já se perguntou como funcionam os sistemas de reconhecimento de imagem? Como um aplicativo do seu celular faz para detectar rostos, ou um teclado inteligente sugere a próxima palavra? As chamadas Redes Neurais tem sido amplamente usadas para tarefas como essas, mas mostraram-se úteis também em outras áreas, como aproximação de funções, previsão de séries temporais e processamento de linguagem natural.
Neste artigo, explico como funciona um tipo básico de Rede Neural, o Perceptron Multicamadas, e um fascinante algoritmo responsável pelo aprendizado da rede, o backpropagation. Tal modelo de rede serviu de base para os modelos mais complexos hoje existentes, como as Redes Convolucionais, que são o estado da arte para classificação de imagens.
Se você estudou Cálculo na universidade e ainda não se esqueceu, não terá dificuldade em entender a parte do texto que está escrita em Matematiquês…Caso contrário, não se sinta incomodado pelas fórmulas, apenas foque na ideia principal; algumas analogias ao mundo real que procurei estabelecer irão ajudá-lo a entender também.
1. Modelo computacional de um neurônio
Inspirando-se no funcionamento dos neurônios biológicos do sistema nervoso dos animais, estabeleceu-se na área da Inteligência Artificial um modelo computacional de um neurônio conforme ilustrado a seguir:
Os sinais da entrada no neurônio são representados pelo vetor x = [x1, x2, x3, …, xN], podendo corresponder aos pixels de uma imagem, por exemplo. Ao chegarem ao neurônio, são multiplicados pelos respectivos pesos sinápticos, que são os elementos do vetor w = [w1, w2, w3, …, wN], gerando o valor z, comumente denominado potencial de ativação, de acordo com a expressão:
O termo adicional b provê um grau de liberdade a mais, que não é afetado pela entrada nessa expressão, correspondendo tipicamente ao “bias” (viés). O valor z passa então por uma função matemática de ativação σ, com a característica de ser não linear, responsável por limitar tal valor a um certo intervalo, produzindo o valor final de saída y do neurônio. Algumas funções de ativação usadas são a degrau, sigmoide, tangente hiperbólica, softmax e ReLU (Rectified Linear Unit).
2. Combinando neurônios em camadas
Com apenas um neurônio não se pode fazer muita coisa, mas podemos combiná-los em uma estrutura em camadas, cada uma com número diferente de neurônios, formando uma rede neural denominada Perceptron Multicamadas (“Multi Layer Perceptron — MLP”). O vetor de valores de entrada x passa pela camada inicial, cujos valores de saída são ligados às entradas da camada seguinte, e assim por diante, até a rede fornecer como resultado os valores de saída da última camada. Pode-se arranjar a rede em várias camadas, tornando-a profunda e capaz de aprender relações cada vez mais complexas.
3. Treinamento de um MLP
Para que uma rede dessas funcione, é preciso treiná-la. É como ensinar a uma criança o beabá. O treinamento de uma rede MLP insere-se no contexto de aprendizado de máquina supervisionado, em que cada amostra de dados utilizada apresenta um rótulo informando a que classificação ela se encaixa. Por exemplo, uma imagem de um cachorro contém um rótulo informando que aquilo é um cachorro. Assim, a ideia geral é fazer com que a rede aprenda os padrões referentes a cada tipo de coisa (cada classe), assim, quando uma amostra desconhecida for fornecida à rede, ela seja capaz de estabelecer a qual classe tal amostra pertence. Como isso pode ser feito?
Backpropagation
“Não é errando que se aprende?”
A ideia do algoritmo backpropagation é, com base no cálculo do erro ocorrido na camada de saída da rede neural, recalcular o valor dos pesos do vetor w da camada última camada de neurônios e assim proceder para as camadas anteriores, de trás para a frente, ou seja, atualizar todos os pesos w das camadas a partir da última até atingir a camada de entrada da rede, para isso realizando a retropropagação o erro obtido pela rede. Em outras palavras, calcula-se o erro entre o que a rede achou que era e o que de fato era (era um gato e ela achou que era um cachorro — temos aí um erro!), então recalculamos o valor de todos os pesos, começando da última camada e indo até a primeira, sempre tendo em vista diminuir esse erro.
Traduzindo essa ideia para o matematiquês, o backpropagation consiste nas seguintes etapas:
- Inicializar todos os pesos da rede com pequenos valores aleatórios.
- Fornecer dados de entrada à rede e calcular o valor da função de erro obtida, ao comparar com o valor de saída esperado. Lembre-se de que como o aprendizado é supervisionado, já se sabe de antemão qual deveria ser a resposta correta. É importante que a função de erro seja diferenciável.
- Na tentativa de minimizar o valor da função de erro, calculam-se os valores dos gradientes para cada peso da rede. Do Cálculo, sabemos que o vetor gradiente fornece a direção de maior crescimento de uma função; aqui, como queremos caminhar com os pesos na direção de maior decréscimo da função de erro, basta tomarmos o sentido contrário ao do gradiente e…voilà! Já temos um excelente caminho por onde andar.
- Uma vez que temos o vetor gradiente calculado, atualizamos cada peso de modo iterativo, sempre recalculando os gradientes em cada passo de iteração, até o erro diminuir e chegar abaixo de algum limiar preestabelecido, ou o número de iterações atingir um valor máximo, quando enfim o algoritmo termina e a rede está treinada.
Assim, a fórmula geral de atualização dos pesos na iteração fica:
Ou seja, o valor do peso na iteração atual será o valor do peso na iteração anterior, corrigido de valor proporcional ao gradiente. O sinal negativo indica que estamos indo na direção contrária à do gradiente, conforme mencionado. O parâmetro η representa a taxa de aprendizado da rede neural, controlando a tamanho do passo que tomamos na correção do peso.
Fazendo uma analogia, imagine que você está numa região montanhosa e escura, desejando descer o mais rápido possível à procura de um vale, na esperança de encontrar um lago de águas límpidas. É possível andar em várias direções, mas você tenta achar a melhor ao sentir o solo ao redor e tomar aquela com maior declive. Em termos matemáticos, você está indo na direção contrária à do vetor gradiente. Considere também que você pode controlar o tamanho do passo que pode dar, mas note que passos muito largos podem fazê-lo passar do vale, caindo na outra montanha mais adiante, e outro passo largo o trará de volta à primeira montanha, fazendo-o pular pra lá e pra cá sem nunca alcançar o vale; por outro lado, passos muito curtos o farão demorar muito a descer, podendo morrer de sede do meio do caminho.
Voltando ao matematiquês, o conceito-chave da equação anterior é o cálculo da expressão ∂E /∂w, consistindo em computar as derivadas parciais da função de erro E em relação a cada peso do vetor w. Para nosso auxílio, vamos considerar a figura a seguir, que ilustra uma rede MLP com duas camadas e servirá de base para a explicação do backpropagation. Uma conexão entre um neurônio j e um neurônio i da camada seguinte possui peso w[j,i]. Repare que os números sobrescritos, entre parênteses, indicam o número da camada à qual a variável em questão pertence, podendo, neste exemplo, valer (1) ou (2).
Sinta-se à vontade para voltar à figura anterior conforme for acompanhando os passos a seguir. Sendo y a saída esperada e ŷ a saída obtida pela rede, definimos a função de erro como sendo:
Ou seja, aqui estamos simplesmente calculando a somatória dos quadrados das diferenças entre os elementos dos dois vetores. Agora, calculamos a derivada parcial do erro em relação à camada de saída, ŷ:
Seguindo a estrutura da rede para a esquerda, prosseguimos no cálculo da derivada do erro em relação ao potencial de ativação z da camada (2), usando a Regra da Cadeia.
Este valor é o gradiente local em relação ao i-ésimo neurônio da camada (2) e, para não tornar as fórmulas excessivamente extensas, será simplesmente indicado como δ:
Finalmente, usando a Regra da Cadeia mais uma vez, computamos a derivada parcial do erro em função de um peso w[j,i] da camada (2):
O cálculo em relação ao bias é análogo, resultando em:
Com esses resultados em mãos, é possível aplicar a fórmula geral de atualização dos pesos dos neurônios da última camada:
E a fórmula de atualização do bias:
Pronto, já temos a fórmula mágica para atualizar os pesos dos neurônios da última camada. Agora temos que calcular esta expressão para os pesos dos neurônios da camada anterior; logo, precisamos calcular a derivada parcial em relação à saída y[i] da camada (1). A sacada aqui é perceber que tal valor y[i] interfere em todos os neurônios da camada seguinte, assim, temos que considerar a somatória dos erros que ele propaga:
Mas:
Resultando em:
Prosseguindo, temos:
Esse δ a que igualamos o resultado segue a mesma ideia anterior: é o gradiente local em relação ao i-ésimo neurônio da camada (1). Finalmente, vem:
Substituindo na fórmula de atualização dos pesos:
Novamente, as fórmulas para o bias são análogas e ficam como exercício.
É isso! Caso existissem mais camadas na rede, o procedimento continuaria, seguindo sempre esse mesmo padrão de calcular as derivadas parciais, retropropagando os erros até a camada de entrada da rede.
O backpropagation é um algoritmo elegante e engenhoso. Os atuais modelos deep learning como Redes Neurais Convolucionais, embora mais refinados que o MLP, têm se mostrado muito superiores em tarefas como classificação de imagens e também utilizam como método de aprendizado o backpropagation, assim como as chamadas Redes Neurais Recorrentes, em processamento de linguagem natural, também fazem uso desse algoritmo. O mais incrível é que tais modelos conseguem encontrar padrões inobserváveis e obscuros para nós, humanos, o que é fascinante e permite considerar que em breve estaremos contando com a ajuda do deep learning para resolver muitos dos principais problemas que afligem a humanidade, como a cura do câncer.