<jupyter_start><jupyter_text>1.4 Funções de ativaçãoComo já vimos redes neurais precisam de funções de ativação para não colapsarem em uma simples multiplicação de matrizes. Porém a decisão de qual função de ativação usar é longe de trivial, por isso nessa seção vamos mostrar um conjunto de funções de ativação e suas propriedades  Dependências<jupyter_code># Gráficos
import matplotlib.pyplot as plt

# Matemática + manipulação de vetores
import math
import numpy as np

# "Fixar" números aleatórios gerados
np.random.seed(0)

# Utilidades
import utils

# Recarregar automaticamente dependências caso elas mudem
%load_ext autoreload
%autoreload 2

# Ignorar warnings
import warnings
warnings.filterwarnings('ignore')

np.seterr(all='raise')
x = np.linspace(-10, 10)
def plot_line(ax, x, y, *args, **kwargs):
    ax.plot(x, y, *args, **kwargs)
    
def plot_activation_vis(f, x, name):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True)
    
    
    # função de activação
    y = f(x)
    ax1.set_title('Funcao de ativacao: ' + name)
    plot_line(ax1, x, y)
    
    # derivada
    dy = f(x, derivative=True)
    ax2.set_title('Derivada da Funcao de ativacao: ' + name)
    plot_line(ax2, x, dy)
    
    plt.show()<jupyter_output><empty_output><jupyter_text>Sem ativação (Linear)... e sem comentários<jupyter_code>def linear(x, derivative=False):
    if derivative:
        return np.ones(len(x))
    return x

plot_activation_vis(linear, x, 'linear')<jupyter_output><empty_output><jupyter_text>ReLUA ReLU é atualmente a função de ativação mais comumente usada para as camadas internas de redes neurais. Ela tem uma série de propriedades úteis, como ser barata de computar e ter um gradiente constante para valores positivos.![](https://cdn-images-1.medium.com/max/1600/1*ZD5kma5J-6UabfEwERv_dQ.png)<jupyter_code>def relu(x, derivative=False):
    if derivative:
        return np.where(relu(x) > 0, 1, 0)
    return np.where(x > 0, x, 0)

plot_activation_vis(relu, x, 'relu')<jupyter_output><empty_output><jupyter_text>SELUSELU é Scaled Exponential Linear Function, é uma função que aproxima a ReLU, porém ela não tem um gradiente nulo nos números negativos, evitando o problema da "ReLU morta". ![](https://cdn-images-1.medium.com/max/1600/1*Q_lez8e2mP7MdSZf-O5bKw.png)<jupyter_code>def selu(x, alpha=3, derivative=False):
    if derivative:
        return np.where(selu(x) > 0, 1, alpha * np.exp(x))
    return np.where(x > 0, x, alpha * np.exp(x) - alpha)

plot_activation_vis(selu, x, 'SeLU')<jupyter_output><empty_output><jupyter_text>SigmoidA função sigmoíde costumava ser a mais usada para as camadas internas antigamente, em especial por ter interpretações matemáticas e biológicas. Hoje em dia ainda é usada na camada final para classificação bináriaPorém a sigmóide tem um sério problema com seus gradientes, eles são extremamente pequenos fora do intervalo [-2, 2], e mesmo nesse intervalos não são muito altos, assim o treinamento de redes neurais profundas se torna muito lento ou até mesmo impossível, o "vanishing gradient problem".<jupyter_code>def sigmoid(x, derivative=False):
    if derivative:
        return sigmoid(x) * (1 - sigmoid(x))
    return 1 / (1 + np.e ** (-x))

plot_activation_vis(sigmoid, x, 'Sigmoid')<jupyter_output><empty_output><jupyter_text>SoftmaxSoftmax é uma função multivariada, ou seja ela recebe vários argumentos de entrada. O propósito dela é gerar uma distribuição de probabilidade a partir de um conjunto de valores. Desse modo ela é usada na camada final de uma rede neural quando queremos fazer classificação multiclasse.Para transformarmos os valores em probabilidades fazemos os seguintes passos:1. Exponenciamos os valores, garantindo que todos se tornem positivos (e que se $x_i > x_j$, $\exp(x_i) > \exp(x_j)$ ). 2. Depois dividimos cada exponencial pela soma das exponenciais, garantindo que a soma dos termos finais seja 1.Às vezes usam um parâmetro chamado temperatura, que suaviza a diferença entre as probabilidades.<jupyter_code>def softmax(x, temperature=1):
    exps = np.exp(x/temperature)
    return exps/np.sum(exps)

x = np.arange(5) - 2
print(x)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True)
y = softmax(x)
ax1.set_title('Temperatura igual a 1')
ax1.bar(x, y)

y = softmax(x, 2)
ax2.set_title('Temperatura igual a 2')
ax2.bar(x, y)
plt.show()<jupyter_output>[-2 -1  0  1  2]<jupyter_text>TanhTem um formato e propriedades bastante semelhantes à sigmóide, porém seus valores variam entre -1 e 1, e seu gradiente é significativamente maior<jupyter_code>def tanh(x, derivative=False): #ela já está implementada no numpy
    if derivative:
        return 1 - tanh(x)**2
    return (np.exp(x) - np.exp(-x))/(np.exp(x) + np.exp(-x))
plot_activation_vis(tanh, x, 'Hyperbolic Tangent')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09944180277031217, 'fraction_numerical': 0.01674591689063469, 'mean_word_length': 4.7254437869822485, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 34, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '398164', 'license': 'Apache-2.0', 'n_tokens_mistral': 1862, 'n_tokens_neox': 1773, 'n_words': 538, 'path': '_notebooks/2021-01-21-1.4.funcoes_de_ativacao.ipynb', 'repo_name': 'hereismari/codando-redes-neurais', 'size': '4837'}