Tudo que você já deveria saber sobre otimização de hiperparâmetros em redes neurais — Parte I

Aryadne Guardieiro
Datarisk.io
Published in
8 min readApr 8, 2021

Na hora de modelar, alguns preferem seguir o coração: usam transferência de arquitetura entre problemas de diferentes escopos porque “um dia deu certo”, seguem a intuição quanto ao número de camadas, otimizadores, valor de taxa de aprendizagem e assim por diante. Durante uma fase exploratória, isso pode fazer total sentido. Mas que tal aliar a intuição, teoria e experimentação para aumentar esse AUC?

Esse não é um guia exaustivo sobre o tema, mas é a primeira parte do mínimo que se deve saber para se trabalhar com hiperparâmetros de redes neurais. Mas não se assuste se você não conhece hiperparâmetros muito bem: é apenas um nome bonito para velhos conhecidos.

É comum entender que parâmetros são otimizados durante certas etapas de modelagem, como os pesos dos neurônios durante a fase de treinamento. Porém, diferente desses, os hiperparâmetros são escolhidos antes do treinamento, durante a concepção da estrutura e comportamento do modelo. Dentre eles, se incluem o número de camadas de uma rede, a taxa de aprendizado, o otimizador usado para convergir uma determinada função de perda, o número de épocas, tamanho dos lotes e assim por diante.

Mas antes de sair testando todas as possibilidades de cada um, é importante entender como eles influenciam no resultado do modelo e no processo de modelagem. Neste artigo, estão os hiperparâmetros mais comuns e como suas configurações influenciam o modelo.

Hiperparâmetros mais comuns

É preciso olhar cada uma dessas configurações como componentes de um todo. Muito provavelmente, a alteração de uma pode implicar na necessidade de ajuste de outra. Sendo assim, usar as ferramentas certas de otimização de hiperparâmetros pode poupar muito tempo e dores de cabeça (e é por isso que você não deve perder as próximas partes deste artigo! :D). Os hiperparâmetros abaixo são apresentados em duas seções: configurações de arquitetura e configurações de treino.

Configurações de Arquitetura

A topologia da rede é ditada por hiperparâmetros. A seleção de quantas camadas e número de neurônios em cada camada dizem respeito ao quanto de informação seu modelo consegue “segurar” a cada passagem dos dados. Redes com muitas camadas e neurônios podem levar ao overfitting (rede extremamente especializada nos dados de treino), enquanto redes muito simples podem ter dificuldades em explicar a variabilidade dos padrões dos dados (aumento do bias).

Se seu problema é overfitting, uma opção é otimizar o dropout. Esse parâmetro pode ser entendido como uma probabilidade que será dada a um neurônio de ter seu valor zerado naquela iteração, com o objetivo de que a rede fique melhor em generalizações.

As funções de ativação vão definir o valor de saída de cada neurônio. Existem três tipos mais comuns de funções de ativação: logistica (sigmoide), tangente hiperbólica (tanh) e de ativação linear retificada, em uma abreviação muito livre do inglês, ReLU. As funções sigmoide e tanh comprimem a entrada uma vez que seus valores de saída são limitados (0 a 1 no caso de sigmoides e -1 até 1 no caso da tanh). Se usadas em camadas mais internas de uma rede neural, podem provocar perda de informação, então geralmente são boas opções em redes “rasas”, ou seja, com poucas camadas ou na camada final de resposta da rede, se convir.

Já a função ReLU varia do máximo entre 0 e o valor da entrada, não limitando tanto o resultado da ativação do neurônio, sendo uma opção relevante para ser usada em camadas mais profundas de uma rede, como as de redes convolucionais que tratam imagens, pois permitem que a informação continue a fluir para as outras camadas adiante. A imagem abaixo ilustra o padrão dessas três funções de ativação.

Exemplos comuns de funções de ativação (a) sigmoide, (b) tangente hiperbólica e (c) ReLU. (Fonte)

Configurações de Treino

Durante a etapa de treino, é importante definir o tamanho do lote, o nosso conhecido batch size. O tamanho do lote geralmente corresponde ao número de padrões que são mostrados a rede antes dos pesos serem atualizados [1]. Para entender o impacto do tamanho do batch, basta pensar na rede neural como uma criança. Se você ensina todo o conteúdo de um ano de uma vez, sem corrigi-la, ela provavelmente não vai reter o conhecimento de forma correta, já que não houve ajustes no entendimento, além de demorar muito para absorver os padrões expostos a cada repetição do conteúdo. Se o tamanho for excessivamente pequeno, provavelmente o aprendizado será desconexo, e ela não vai conseguir assimilar o padrão de uma informação para outra, o que em termos concretos significa que os pesos da rede neural não indicaram coordenadas que fazem sentido no hiperespaço.

Outro hiperparâmetro comum é o número de épocas ou epochs. Esse número descreve quantas vezes todo o dataset de treino será apresentado a rede [1]. E está intimamente atrelado a quanto a rede neural vai se moldar aos dados. Caso o número de épocas seja muito pequeno, a rede pode encontrar dificuldade em assimilar padrões menos comuns nos dados. Caso seja muito grande, pode ocorrer o overfitting.

Dada a função de ativação escolhida na seção anterior, é importante medir o quanto sua rede neural se aproximou ou se afastou do valor real durante o treino para ajuste dos pesos dos neurônios do modelo. Para isso, são utilizadas as funções de perda, ou loss functions. Estas estão intimamente ligadas às funções de ativação e isso deve ser observado na seleção dessa configuração. Geralmente, essas funções são divididas em funções de loss de regressão e de classificação [5]. As de regressão são usadas quando a função de ativação retorna um número contínuo, e as de classificação são usadas quando a saída é binária ou multiclasse.

Entre as funções de loss de regressão mais comuns está o erro médio quadrático (Mean Square Loss — MSE), que computa a média quadrática da diferença entre o valor real e o valor predito (fórmula abaixo). Por ser uma função quadrática, tem seu gradiente descendente mais facilmente computado, o que facilita a localização do seu mínimo. Porém, ela é extremamente sensível a outliers uma vez que eleva ao quadrado a diferença da predição e do valor real.

MSE (Fonte)

Outras funções de perda de regressão são: Mean Absolute Error, que leva em consideração a média do erro absoluto, (cuja função módulo não tem tão boas propriedades matemáticas de cálculo de derivadas) e Mean Bias Error, que calcula a média do erro sem o valor absoluto. Essa função é menos usada, mas pode ajudar a identificar se houve um exagero de erro positivo ou negativo.

Quanto a funções de perda de classificação, temos a função Cross-Entropy Loss, para classificações binárias. Essa função retorna um valor entre 0 e 1 no qual quanto mais próximo de 0, maior a probabilidade da classificação ter sido feita na label correta, e quanto mais próximo de 1, maior a probabilidade de que o modelo está errando (e errando feio). Uma lista mais completa sobre loss functions pode ser encontrada aqui.

Aliados às funções de perda, existem os otimizadores que ajudam a minimizá-las mais rapidamente. Geralmente, isso é feito encontrando-se o gradiente descendente da função, que indica qual “direção” seguir para que seu valor mínimo seja encontrado mais rapidamente. O desenho abaixo ilustra esse contexto para uma função quadrática de loss como o MSE:

O que queremos é chegar no vale mínimo da função e para isso usamos o gradiente descendente como guia (Fonte)

Os otimizadores são usados para acelerar esse processo uma vez que podemos calcular a função de perda para cada estimativa do lote, o que demora muito e é conhecido por batch gradient descent. Um otimizador comum é escolher aleatoriamente um exemplo para termos um erro de referência, que é a estratégia do Stochastic Gradient Descent (SGD). Podemos ainda usar estratégias mais sofisticadas como o Adam, que é um dos mais usados hoje, o Adagrad e o RMSprop. Mais informações sobre esses outros métodos podem ser encontradas nesse link.

Um jeito de turbinar o otimizador é usando um hiperparâmetro conhecido como impulso (ou momentum em inglês). Esse componente do modelo é uma taxa aplicada ao gradiente descendente da iteração passada que, combinado com o gradiente da iteração atual, ajuda o modelo a convergir mais rapidamente e não ficar preso em mínimos locais, como se ganhasse um empurrãozinho. Pode ser descrito da seguinte forma:

Uso do momentum no cálculo do gradiente descendente (Fonte)

O gif abaixo, extraído do vídeo Neural Network Visualization : Momentum ilustra como esse parâmetro pode ajudar ou atrapalhar seu treinamento. É possível observar que o momentum=0 demora mais a convergir (definir o padrão dos dados), enquanto os momentums 0.5 e 0.9 são mais rápidos em definir o padrão. Já o momentum=0.99 faz o modelo ter uma representatividade dos dados muito pobre.

Dependendo do “empurrãozinho”, o desempenho do modelo pode cair ladeira abaixo (Fonte)

A taxa de aprendizado é um outro parâmetro que pode dar um tombo na sua rede. Usada como uma taxa de correção a cada iteração a cada época, se muito grande pode implicar na falta de convergência (seu modelo vai ficar pulando nas bordas dos mínimos locais sem nunca cair lá dentro), ou se muito pequena pode fazer com que o tempo de treino seja proibitivo, uma vez que os ajustes nos pesos serão muito pequenos levando a uma demora na conversão. Mais sobre o tema aqui.

Conclusão

Como foi dito, essa não é uma lista exaustiva de todos os hiperparâmetros, ou de tudo sobre eles. Mas serve como ponto de partida para repensar o que cada variação na estrutura do seu modelo pode impactar. Dependendo do tipo de rede, a quantidade de hiperparâmetros aumenta, como o caso das redes convolucionais que podem oscilar em quantidade de filtros, canais, funções de pooling entre outras configurações. Mas as descritas aqui ainda fazem parte desse tipo de modelo e continuam valendo.

Por fim, uma dica que se repete entre as referências consultadas e experiência do nosso time é: vale começar com uma arquitetura simples e, uma vez feito o máximo possível de otimizações com ela, partir para estruturas mais complexas como mais camadas ou mais neurônios. Isso geralmente é feito pois aumentar o número de camadas e neurônios tende a aumentar o custo do treinamento, a probabilidade de overfitting e a complexidade de explicabilidade e otimizações.

Como fazer essa evolução gradual dos modelos e as ferramentas mais utilizadas para isso são temas que serão discutidos nos próximos episódios deste artigo. Nos vemos lá :D

Referências no tema que valem a pena conferir:

[1] How to Grid Search Hyperparameters for Deep Learning Models in Python With Keras: https://machinelearningmastery.com/grid-search-hyperparameters-deep-learning-models-python-keras/

[2] A guide to an efficient way to build neural network architectures- Part I: Hyper-parameter selection and tuning for Dense Networks using Hyperas on Fashion-MNIST: https://towardsdatascience.com/a-guide-to-an-efficient-way-to-build-neural-network-architectures-part-i-hyper-parameter-8129009f131b

[3] A guide to an efficient way to build neural network architectures- Part II: Hyper-parameter selection and tuning for Convolutional Neural Networks using Hyperas on Fashion-MNIST: https://towardsdatascience.com/a-guide-to-an-efficient-way-to-build-neural-network-architectures-part-ii-hyper-parameter-42efca01e5d7

[4] Tuning the hyper-parameters of an estimator: https://scikit-learn.org/stable/modules/grid_search.html

[5]What Are Different Loss Functions Used as Optimizers in Neural Networks? https://www.analyticssteps.com/blogs/what-are-different-loss-functions-used-optimizers-neural-networks

--

--

Aryadne Guardieiro
Datarisk.io

Cientista da computação, desenvolvedora e aprendiz dos dados na Datarisk.