Introdução teórica a Neural Network — Deep Learning — Parte 2

Arthur Lamblet Vaz
Data Hackers
Published in
7 min readSep 1, 2018

No post anterior vimos o funcionamento de uma Rede Neural (Neural Network ou, somente, NN), agora devemos entender como gerar um output preciso. Para ajudar a entender essa segunda parte, iremos exemplificar em um clássico problema de reconhecimento de números.

O exemplo abaixo retrata uma imagem com 14 pixel do número 1 e será aplicado NN para classificar.

Imagem da direita o nosso exemplo, na direita como é visualizado no NN

Mas até chegar a imagem da direita, como o NN interpretou a imagem da esquerda? Vamos lá…

Essa é uma imagem de 14x14, que resulta em um total de 196 pixel, como mostra a imagem da direita, com uma escala de tonalidade do branco ao preto que varia entre 0 e 1. Então, esse seria nossos input's e para as próximas camadas (Hidden Layers), os pesos e bias irão determinar os neurônios que serão ativados ou não. Como visto no post parte 1, o bias é uma constante que determina quando se deve considerar ou melhorar a significância das somas dos pesos vezes os input's.

Mas supondo que trabalhemos com 2 camadas escondidas e 1 de saída. Sendo que cada camada tem 16 neurônios e a camada de saída 10 (0,1,2,3,4,5,6,7,8,9). Portanto, teremos no total 196*16+16*16+16*10 = 3.552 ajustes de pesos e bias teremos mais 16+16+10. Por fim, um total de 3.594 combinações para determinar o valor final de uma interação. Em suma, a função de NN terá 196 pixeis de INPUT'S, com 10 OUTPUTS e 3.594 PARÂMETROS.

Então claramente entendemos que ajustando os pesos e os bias, é a maneira de refinar o resultado. Na primeira iteração os pesos e bias são inicializados de maneira aleatória e devido a isso, resultará uma função de custo alto.

Vou usar um outro exemplo para demonstrar como funciona, imagine que estamos querendo classificar o número 3. Abaixo mostra o resultado do output do modelo comparado com o real, calculando assim o resultado da função de custo do modelo, que será a soma de cada quadrado da diferença entre o resultado do modelo com o real dos outputs.

Resultado do modelo versus o real de cada output possível

E cada interação terá sua função de custo, portanto devemos considerar a função de custo médio de todos o treinamentos que o modelo irá fazer. Usando novamente o primeiro exemplo a função de custo será definido por 3.594 INPUT'S, 1 OUTPUT e os PARÂMETROS dependerá de quantos exemplos de treino o modelo terá, quanto mais, melhor!

Agora, tão esperado momento…

como diminuiremos o valor da função custo?

A galera de exatas irá dizer DERIVANDO, o que não está errado se for um gráfico simples como uma hipérbole. Mas normalmente o gráfico da função é composto por altos e baixos com n dimensões. Para isso precisamos de um vetor nos direcionando para o menor valor, em outras palavras o negativo do vetor gradiente!

Em linhas gerais, como funciona o vetor gradiente?

Ilustração do vetor gradiente

Será inicializado de maneira aleatória no gráfico da função. Em seguida o modelo deve escolher que direção deve seguir, o slope da função(tangente) irá ter esse papel. O vetor gradiente é apenas um vetor e o que dirá a ele quanto deve-se avançar é conhecido como step size. Se o step size for proporcional ao tamanho do slope e estiver no ponto flat, perpendicular ao eixo y (em torno do ponto mínimo), o step size será cada vez menor evitando assim o overshooting (passar do mínimo local).

Fazendo repetidamente, usando o slope como guia, certamente irá chegar ao mínimo local. O vetor gradiente dirá a direção para o maior valor a partir do ponto, então devemos considerar o negativo para que possamos enxergar a direção do menor valor possível.

Entendeu? Vamos entrar no “matematiquês"

Na figura abaixo, mostra um simples exemplo de vetor gradiente de uma função da circunferência.O ponto P(2,5) — está em amarelo, foi escolhido de forma arbitrária e será usado para achar a curva de nível do problema aplicando a função objetiva — está em vermelho, resultando o valor da curva de nível de 29 — está em rosa. Para quem não lembra, a curva de nível é o desenho do contorno da função objetiva e serve para representar a área graficamente.

Agora, deve-se calcular a derivada parcial em função de x e y — está em azul. Depois de ter calculado a derivada parcial em função de x e y, deve-se aplicar na fórmula padrão do vetor gradiente. Resultará uma nova função que será aplicado novamente o ponto escolhido de forma aleatória para achar a direção do vetor — em verde. No gráfico — em preto, mostra a posição do ponto escolhido no eixo x=2 e no y=5. A parti deste ponto, será projetado o vetor gradiente da seguinte maneira: como o o vetor resultante foi <4,10>, deve somar mais quatro no eixo x e em cima dessa projeção, somar mais 10 no eixo y.

A sua diagonal será o vetor gradiente da função.

Exemplo do cálculo do vetor gradiente

Voltando a RNN…

O vetor gradiente deve ser incrementado ao vetor de peso e bias para resultar o novo vetor resultado. Esse método é aplicado em todas amostras de treino já que devemos minimizar a função de custo que nada mais é a média da função de custo de cada treinamento (iteração). O algoritmo Backpropagation que computa de forma eficiente os gradientes, além de ser o terceiro pulo do gato de redes neurais porque com ele você entenderá como funciona o processo de aprendizagem de máquina — não se preocupe que esse tópico será explicado com mais detalhe em um próximo post.

Ao final de um modelo com uma acurácia acima de 96%, posso dizer que meu modelo saber identificar um número? Bem… desculpe ser franco, mas NÃO e calma vou explicar melhor.

Em cada camada, cada neurônio será um padrão dos conjuntos de pixel e quase podemos dizer que não existe um padrão muito claro. Só podemos afirmar que dentro de 3.594 dimensões de espaços possíveis de pesos e bias, o modelo achou um mínimo local suficiente para classificar os números. Agora se colocássemos um imagem aleatória (imagem de um gato) o modelo iria fornecer um resultado? Sim, ele pode resultar um número e isso ajuda a provar que o modelo não é capaz de desenhar o número que está classificando… logo eles não são tão inteligentes assim né?!

E outra, esse método não parece mas é velho… só que para facilitar o entendimento dos métodos Rede Neural Convolucional e LSTM as Redes Neurais Multicamadas (MLP) podem ser uma boa opção.

Voltandooo… o gradiente descedente aumenta as chances de convergir para o mínimo local fornecendo um alto número de épocas (epochs em inglês ou iterações), já que ele treina toda a população para encontrar mínimo local de cada iteração. Todavia, é um processo lento e custoso computacionalmente. Devido a esse fato, desenvolveram o Gradiente Descendente Estocástico (GDE), Mini-Batch Gradiente Descendente, Momentum, Adagrad, Adadelta, Rmsprop, Adam.

Gradiente Descendente

Esse método considera apenas amostra aleatória N=1 ao invés de pegar todos inputs e calcular todos outputs como no tradicional Gradiente Descendente. O Mini-Batch Gradiente Descendente nada mais é do que uma variação do GDE com uma amostra maior que 1, onde os Batch serão divididos em partes para serem embaralhados e esse processo será repetido para cada iteração. Abaixo segue um gráfico para ajudar o entendimento, o Full Batch se refere ao método padrão do Gradiente Descendente.

Ilustração dos três otimizadores Gradientes

Ficou confuso? Talvez em código ajuda a clarear a diferença. Segue um simples script em python.

for i in range(nb_interacao):
params_grad = evaluate_gradient(fun_custo, data, params)
params = params - params_grad * learning_rate

Agora o Gradiente Descendente Estocástico são escritos da seguinte maneira:

for i in range(nb_interacao):
np.random.shuffle(data)
for example in data:

params_grad = evaluate_gradient(fun_custo, example, params)
params = params - params_grad * learning_rate

Por fim, o Mini-Batch Gradiente Descendente:

for i in range(nb_interacao):
np.random.shuffle(data)
for batch in get_batches(data, batch_size=50):

params_grad = evaluate_gradient(fun_custo, batch, params)
params = params - params_grad * learning_rate

O learning_rate serve para apenas diminuir o valor do erro para não causar overshooting, geralmente é um valor pequeno como 0,01.

Dos vetores gradientes descendente, Mini Batch acaba sendo a melhor opção por pegar o melhor dos dois mundos.

--

--

Arthur Lamblet Vaz
Data Hackers

Surfista, natureba e engenheiro de produção com ênfase em Data Science🌱🌍♻️