Redes Neurais | Teoria #2

Um mergulho mais profundo no aprendizado das Redes Neurais.

Caio Deberaldini
Turing Talks
6 min readOct 13, 2019

--

Imagem por Clint Adai em Unsplash

1. Introdução

Fala pessoal, ficaram ansiosos para lerem e aprenderem mais ainda sobre as nossas queridas Redes Neurais? Bom, seguindo o último post a respeito do assunto — se você não viu, não perca aqui — , daremos continuidade nos nossos estudos a respeito do modelo que está na crista da onda.

2. Função de Custo

Assim como os demais modelos de Aprendizado Supervisionado os quais exploramos, ao final de todo o processo o que queremos é otimizar a nossa função de hipótese, de tal forma que o valor esperado pelo modelo e o valor real sejam o mais próximo possível, ou seja, o erro entre eles seja o mínimo possível.

Desse modo, lembrando que, em nosso contexto, usaremos Redes Neurais para classificarmos se determinado exemplo pertence a uma classe em específico — todavia, podemos usá-las para uma regressão também — , temos a nossa função de custo definida da mesma forma que na Regressão Logística, com o porém de que, para a nossa análise ser generalizada, podemos trabalhar com K classes de análise. A equação abaixo é a nossa função de custo, a qual buscaremos minimizar para obtermos os parâmetros (representados por Θ) “ótimos” do nosso modelo — ou seja, aqueles que fazem a nossa predição tornar-se o mais próximo possível do valor real esperado:

Função de Custo de uma Rede Neural.

3. Feedforward Algorithm

Ok, então queremos calcular a função de erro da nossa Rede Neural. Mas, para isso, precisamos calcular o valor esperado para a i-ésima entrada do nosso conjunto de dados. Ou seja, precisamos passar a nossa entrada pelas camadas, fazendo-se a ativação dos neurônios e obtendo funções cada vez mais complexas (e não-lineares) ao longo da rede — como explicado no nosso 1º post de Redes Neurais. O algoritmo utilizado neste caso é conhecido como Feedforward Algorithm. Vejamos a seguinte arquitetura, também mostrado no 1º post:

Ativação dos neurônios. Fonte: “Coursera Machine Learning — Stanford University”, Andrew Ng.

Dada uma camada L, cada neurônio da camada L+1 é calculado como sendo a composição da função de ativação g sobre a combinação linear dos neurônios da camada anterior, ponderados com os parâmetros θ correspondentes. Por exemplo, para a²⁾, tomamos a 1ª linha da matriz de parâmetros θ⁽¹⁾ e multiplicamos pelos correspondentes neurônios da camada anterior, ou seja, o valor da 1ª coluna, da 1ª linha de θ multiplica o valor da bias unit da camada anterior, o da 2ª coluna multiplica o próximo neurônio e assim sucessivamente. De tal forma que, para a arquitetura acima, temos o seguinte algoritmo:

Onde a multiplicação ali representada é a multiplicação matricial entre a matriz θ⁽ⁱ⁾ e o vetor coluna dos neurônios a⁽ⁱ⁾. Além disso, sempre temos que nos lembrar de adicionar a unidade de viés (bias unit) na 1ª camada e nas camadas que compõem a Hidden Layer — dúvidas, leiam novamente o nosso primeiro post de Redes Neurais e não esqueçam de deixar seus claps rsrs.

4. Backward Propagation Algorithm (Backprop)

O algoritmo de Backprop serve para que consigamos calcular a derivada da nossa função de custo em cada uma das nossas camadas (com exceção da camada de entrada). Para cada uma das camadas escondidas e para a camada de saída, calculamos uma hipótese para cada neurônio, ativando-o com uma função determinada — no caso, explicamos sobre a função sigmóide — , compondo essa função com a combinação linear dos neurônios anteriores, parametrizados por θ. A imagem de ativação dos neurônios acima mostra como, matematicamente, é isso.

Bom, então para cada vez que ativamos nossos neurônios, teremos um erro no valor esperado associado a ele. Podemos ter uma intuição melhor em nossa última camada, pois sabemos que o resultado em cada neurônio desta será a probabilidade para que, dada uma entrada, ela pertença à classe associada àquele neurônio. Portanto, como temos para cada exemplo do nosso conjunto de treino um vetor de labels associando-o a sua classe, para a Output Layer não temos grandes problemas para calcular o erro. Basta pegarmos o valor previsto e fazermos a subtração com o vetor de labels do exemplo. A equação seria então, para o i-ésimo exemplo do nosso conjunto de treino:

Porém, nosso objetivo é obtermos os parâmetros que pesamos nossos neurônios de uma camada para as camadas consecutivas. Como não necessitamos nos aprofundar tanto na matemática do algoritmo, deixo para os amantes da matemática (como eu) e curiosos este artigo, super interessante, que demonstra como a partir do erro relativo a função de custo devido a ativação do neurônio e do erro relativo a ativação do neurônio devido aos parâmetros considerados, chega-se à derivada da função de custo devido aos parâmetros considerados para uma camada escondida. A ideia do algoritmo é brincar com derivadas parciais para, a partir dos erros das camadas subsequentes, obtermos a derivada da função de custo com respeito aos parâmetros das camadas anteriores. Por isso o nome Backpropagation! Com essa intuição bastante abstrata, têm-se as equações do algoritmo Backprop:

Onde (*) é a multiplicação matricial, (.*) a multiplicação elemento a elemento e g’(z) é a derivada da função de ativação — no nosso caso, função sigmóide — para a camada L. Vale ressaltar que começamos o cálculo pela camada de saída e terminamos na camada que sucede a camada de entrada.

Para a nossa função de ativação, sua derivada, a qual pode ser obtida pela regra da cadeia (os mais familiarizados com Cálculo Diferencial e Integral a conhecem muito bem), é dada pela seguinte equação:

Depois de calcularmos o erro da função de custo com respeito à ativação dos neurônios em cada camada (como descrito pelas fórmulas acima), calculamos a derivada da função de custo com respeito aos pesos — representada por D⁽ᴸ⁾ — , para cada camada, como sendo a seguinte expressão:

Onde m é o número de exemplos do nosso conjunto de treinamento.

5. Algoritmo Gradiente Descendente

Por fim, precisamos atualizar nossos parâmetros a cada iteração do Backprop. Assim como apresentado no modelo de Regressão Linear, aplicamos o algoritmo do Gradiente Descendente para obtermos novos parâmetros, ponderando o seu gradiente pela taxa de aprendizado α:

6. Resumo do pipeline de uma Rede Neural

Em síntese, o fluxo dos dados em uma Rede Neural, como visto, segue o pipeline: aplica-se o Feedforward para calcularmos o valor dos neurônios e o valor esperado de nossos exemplos de treinamento; retornamos na rede, por meio do Backward Propagation, e calculamos a derivada da nossa função de custo com respeito a cada matriz de parâmetros θ; atualiza-se os valores dos parâmetros, por exemplo, pelo Gradiente Descendente; o processo é iterado repetidas vezes, até que o resultado de saída da rede seja satisfatório, de acordo com alguma métrica.

Conclusão

Eu sei, o caminho até aqui foi árduo e, muito provavelmente, seja necessária mais de uma leitura a respeito deste e do post anterior dessas belezinhas conhecidas como Redes Neurais. Porém, não se preocupem. Todo esse esforço será recompensado em nossa 3ª parte, onde, finalmente, colocaremos as mãos na massa e iremos implementar nossa primeira Rede Neural, juntos, treinando e testando-a para que ela classifique números escritos a mão (sim, exatamente isso que vocês ouviram! — ou leram — ).

Esperamos que tenham gostado e até a próxima!

Curtiu? Então não deixe de criar sua conta no Medium e seguir a gente para continuar aprendendo mais modelos e outros assuntos de IA. Dê uma olhada em nossos outros posts e acompanhe o Grupo Turing no Facebook, Instagram e no LinkedIn, temos várias oportunidades em IA por lá também.

Até!

--

--