ReLU e o genocídio de neurônios: Como uma função de ativação pode criar regiões mortas em uma rede neural | by Alexandre Esposte Santos | May, 2024


A escolha de uma função de ativação no desenvolvimento de uma rede neural é um passo crucial para a obtenção de bons resultados. As funções de ativação são as grandes responsáveis pela incorporação de não linearidades em nossa rede, caso contrário, só seriamos capazes de tratar problemas linearmente separáveis.

Na listagem a seguir, apresentamos algumas funções de ativações mais conhecidas e utilizadas:

  • Sigmoide
  • Tangente hiperbólica
  • Linear
  • ReLU
  • Softmax

vale ressaltar que essas são algumas das funções que mais vemos por ai, no entanto, existem muitas outras que podemos considerar.

Neste artigo, como sugere o título, focaremos nossos esforços em analisar características fundamentais atreladas a função de ativação ReLU (Rectified Linear Unit, ou Unidade Linear Retificada em português).

O primeiro passo consiste em definirmos essa função de ativação, essa parte é essencial para que possamos ir adiante na nossa linha de raciocínio. Desse modo, a função ReLU pode ser expressa da seguinte maneira,

uma segunda forma enunciar essa função é apresentada a seguir

perceba que quando o valor de entrada x for maior do que zero a saída da ReLU será a própria entrada x, caso contrário, se a entrada for um número negativo, a saída será zero. Além da função ReLU, precisamos também da sua derivada,

note que a derivada sempre será um valor constante, 1 para valores positivos e 0 para valores negativos. A seguir, apresentamos uma imagem, onde no gráfico à esquerda está a função ReLU e à direita a sua respectiva derivada.

Fonte: https://towardsdatascience.com/why-rectified-linear-unit-relu-in-deep-learning-and-the-best-practice-to-use-it-with-tensorflow-e9880933b7ef

É comum ficarmos em dúvida sobre qual função de ativação escolher dado um conjunto relativamente extenso de funções a nossa disposição. Neste tópico iremos discutir as diferenças entre a função ReLU e Sigmoide, entendendo assim os prós e contras de cada uma.

Antes de tudo, segue a expressão da função Sigmoide,

Domínio:

O domínio de ambas as funções está no intervalo de menos infinito a infinito, ou seja, essas aceitam qualquer valor como entrada.

Imagem:

A imagem da função sigmoide é limitada no intervalo [0, 1], o que indica que essa função apresenta regiões de saturação em seus extremos. Desse modo, notamos que nessas regiões a derivada é zero, ou pelo menos muito próxima de zero, portanto, essa característica pode ter efeitos negativos no treinamento de uma rede neural profunda.

Por outro lado, a função ReLU tem sua imagem no intervalo [0, infinito[ . Logo, há saturação para valores negativos, mas para valores positivos não existe essa limitação.

Custo computacional:

Um fator muito importante quando tratamos de funções de ativação corresponde ao seu custo computacional. Vamos discutir brevemente quais são as principais diferenças entre ambas as funções no contexto de custo computacional e como isso impacta no treinamento de uma rede neural.

Neste ponto, devemos notar as operações necessárias para computar essas funções. Observe que na função ReLU é necessário apenas uma operação condicional, isto é, uma comparação do valor de entrada para decidirmos se a saída será x ou se será 0. Por outro lado, na função sigmoide é necessário realizar uma série de operações matemáticas para obtermos a sua saída, o que configura um maior custo computacional.

Agora devemos analisar as operações contidas na função Sigmoide:

  1. A função sigmoide requer o cálculo da função exponencial, o que computacionalmente é uma tarefa mais dispendiosa. O calculo da exponencial não ocorre de maneira direta, ou seja, é necessário uma série de outros cálculos para que se chegue em um valor preciso. Desse modo, o simples fato de calcular uma exponencial já demanda mais do seu processador.
  2. É necessário realizar a adição presente no denominador (1 + exp), entretanto, o mais custoso consiste no cálculo da função exponencial, como discutimos no item 1.
  3. Realizar a divisão do numerador com o denominador. Algumas operações efetuadas no processador são mais performáticas do que outras. Devemos notar que operações de adição e multiplicação são mais performáticas que operações de divisão. Desse modo, é sempre preferível tratar uma divisão como uma multiplicação, entretanto, não é possível fazer isso na função sigmoide.

Portanto, podemos concluir que a função ReLU é computacionalmente mais barata e rápida de se computar quando comparada a função Sigmoide, onde essa demanda mais recursos computacionais e tempo. Isso pode ser notável ao treinarmos redes com algumas centenas de neurônios por camada.

Para fins de comparação, desenvolvi um simples código no Colab para testar como ambas as funções se comportam em um cenário onde é necessário realizar o cálculo de 500 milhões de entradas aleatórias. Os resultados de tempo estão expostos na imagem a seguir

vale mencionar que os cálculos realizados foram feitos com o auxílio do pacote numpy, desse modo, essas operações já foram efetuadas com certa otimização.

Há algumas décadas, observava-se problemas denominados por Vanishing/Exploding Gradients. Na tabela a seguir, apresento a comparação entre esses dois casos

Fonte: https://chatgpt.com/

admito que nessa imagem eu me rendi ao chatgpt, a comparação no formato tabular exprime muito melhor e de maneira mais compacta do que eu conseguiria expor por meio de um texto.

Devemos nos lembrar que uma rede neural é treinada por meio do algoritmo backpropagation, onde esse utiliza do gradiente descendente para realizar as alterações dos pesos da rede, ou seja, é necessário o cálculo do gradiente da função de custo referente a cada peso estabelecido durante a construção do modelo. Portanto, como pontuado pela tabela apresentada, há dois problemas em todo esse processo, que consiste na diminuição gradativa do gradiente conforme a propagação pela rede ou onde o gradiente cresce de acordo com essa propagação. Ambos os fenômenos são prejudiciais para o treinamento da rede neural, pois não nos permite alcançar uma solução ótima.

Em meados de 2010, alguns pesquisadores notaram que a maneira como os pesos da rede eram inicializados poderia ter a sua contribuição nesses problemas, além disso, notaram também que a utilização da função sigmoide, muito utilizada na época, também apresentava seu grau de contribuição nessa questão, devido ao seu comportamento saturado nas extremidades.

Possíveis soluções para esse problema consiste em alterar a inicialização da rede e também a utilização de outras funções de ativação. Podemos utilizar as seguintes inicializações de pesos:

. Glorot

. He

. LeCun

Além disso, podemos trocar a função sigmoide pela função ReLU, onde essa última resolve o “problema” de saturação presente na sigmoide.

Até o momento só discutimos aspectos positivos da função ReLU, entretanto, nem tudo são flores. Essa função apresenta uma característica/fenômeno expresso por “dying ReLUs” ou “ReLUs mortas”.

Vimos que a saída da ReLU será zero dado para valores negativos na entrada, portanto, durante o treinamento, existe a possibilidade de alguns neurônios apresentarem suas respectivas saídas iguais a zero independente da entrada, ou seja, dizemos que esses neurônios estão mortos.

Um neurônio “morre” quando a combinação linear de seus pesos com as suas respectivas entradas sempre resultam em um valor negativo, ou seja, sua saída sempre será zero. Agora devemos lembrar que esses pesos são ajustados através do gradiente descendente. Dessa maneira, os pesos do neurônio não serão mais atualizados, uma vez que o gradiente descendente não terá mais efeito sobre o ajuste dos pesos. Portanto, os neurônios entram em um estado de limbo do qual não conseguem sair, tornando-se inativos ou ‘mortos’. Isso resulta em um modelo subótimo.

O problema em questão advém do fato de que a derivada da função ReLU é igual a zero para valores negativos. Portanto, a solução é simples: podemos modificar a função ReLU para que sua derivada não seja igual a zero para valores negativos. Dessa forma, o neurônio mantém seus pesos sendo ajustados durante o treinamento.

A alteração dessa função resultou em uma gama de outras funções. A seguir, listo algumas dessas variantes:

  1. Leaky ReLU: Introduz um pequeno valor de inclinação 𝛼 para as entradas negativas, permitindo que a função tenha uma pequena derivada diferente de zero.
  2. RReLU (Randomized leaky ReLU): A inclinação α é escolhida aleatoriamente dentro de um intervalo específico durante o treinamento e fixa durante a inferência.
  3. PReLU (Parametric leake ReLU): Similar à Leaky ReLU, mas o valor de α é um parâmetro aprendido durante o treinamento.
  4. ELU (Exponential linear unit): Introduz uma exponencial para valores negativos, o que ajuda a suavizar a saída.
  5. SELU (Scaled Exponential Linear Unit): Uma variante da ELU, escalada para manter a normalização interna de forma autônoma durante o treinamento.

Não estarei discutindo sobre essas funções neste artigo, pois acredito que ele já está extenso. Entretanto, futuramente, pretendo trazer uma parte 2 abrangendo todas essas variantes.

Com essas variantes da ReLU, somos capazes de evitar, ou ao menos, mitigar a morte dos nossos neurônios, permitindo-nos assim treinar as nossas redes sem nos preocupar com esse problema.

  1. GÉRON, Aurélien. Hands-on machine learning with Scikit-Learn, Keras, and TensorFlow. “ O’Reilly Media, Inc.”, 2022.
  2. PACHECO, Peter. An introduction to parallel programming. Elsevier, 2011.

3. https://www.ime.usp.br/~leo/mac2166/2017-1/line_introducao_eficiencia_algoritmos.html



Source link

Be the first to comment

Leave a Reply

Your email address will not be published.


*