Funções de Ativação#

As funções de ativação são componentes matemáticos cruciais em redes neurais artificiais. Elas são responsáveis por introduzir não-linearidade ao modelo, permitindo que a rede aprenda e mapeie relações complexas nos dados. Sem elas, não importa quantas camadas ocultas a rede possua, ela se comportaria apenas como uma gigantesca regressão linear.

Abaixo, documentamos as funções de ativação implementadas na biblioteca Turing.

Rectified Linear Unit (ReLU)#

class ReLU : public Layer#

Implementa a função de ativação Rectified Linear Unit (ReLU).

  • A ReLU é uma função não-linear aplicada elemento a elemento na matriz de entrada. É amplamente utilizada em redes neurais profundas por mitigar o problema do desvanecimento do gradiente e induzir esparsidade nas ativações. A função matemática é definida como:

    \[ f(x) = \max(0, x) \]

Public Functions

ReLU() = default#

Construtor padrão da camada ReLU.

  • Como esta camada não possui parâmetros treináveis (pesos ou vieses), nenhuma inicialização de matriz é necessária.

virtual ~ReLU() = default#

Destrutor padrão.

inline virtual void Forward(const Eigen::MatrixXf &input, Eigen::MatrixXf &output) override#

Executa o forward pass da ativação ReLU.

  • Aplica a função limiar zero a todos os elementos da matriz de entrada.

    \[ Y = \max(0, X) \]

Parameters:
  • input – Referência constante para a matriz de entrada \( X \) de dimensões \( (batch\_size \times input\_size) \).

  • output – Referência para a matriz onde o resultado ativado \( Y \) será armazenado.

inline virtual void Backward(Eigen::MatrixXf &grad_input, const Eigen::MatrixXf &grad_output) override#

Executa o backward pass da ativação ReLU.

  • A derivada da função ReLU em relação à sua entrada \( x \) é uma função indicadora:

    \[\begin{split} f'(x) = \begin{cases} 1 & \text{se } x > 0 \\ 0 & \text{se } x \le 0 \end{cases} \end{split}\]
    Para propagar o erro, aplicamos a Regra da Cadeia multiplicando o gradiente recebido \( \frac{\partial L}{\partial Y} \) pela derivada local utilizando o Produto de Hadamard ( \( \odot \)):
    \[ \frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \odot f'(X) \]

Parameters:
  • grad_input – Referência para a matriz que armazenará o gradiente calculado \( \frac{\partial L}{\partial X} \).

  • grad_output – Matriz contendo o gradiente recebido da camada seguinte \( \frac{\partial L}{\partial Y} \).

Exemplo de Aplicação#

Abaixo está um exemplo completo de como instanciar e treinar uma rede utilizando a ReLU:

1    Model model;
2
3    // Camada Oculta: 784 entradas (pixels 28x28) para 128 neurônios
4    model.AddLayer(new DenseLayer(784, 128));
5    model.AddLayer(new ReLU());

Sigmoide Logística (Sigmoid)#

class Sigmoid : public Layer#

Implementa a função de ativação Sigmoide Logística.

  • A função sigmoide mapeia qualquer valor real para o intervalo \( (0, 1) \). Historicamente, é muito utilizada para modelar probabilidades e transformar saídas lineares em distribuições. A função matemática é definida como:

    \[ \sigma(x) = \frac{1}{1 + e^{-x}} \]

Public Functions

Sigmoid() = default#

Construtor padrão da camada Sigmoid. Como esta camada não possui parâmetros treináveis (pesos/vieses), a inicialização ocorre sem alocação de matrizes.

virtual ~Sigmoid() = default#

Destrutor padrão.

inline virtual void Forward(const Eigen::MatrixXf &input, Eigen::MatrixXf &output) override#

Executa o forward pass da ativação Sigmoide.

  • Aplica a função logística a cada elemento da matriz de entrada.

    \[ Y = \frac{1}{1 + e^{-X}} \]

Parameters:
  • input – Referência constante para a matriz de entrada \( X \) de dimensões \( (batch\_size \times input\_size) \).

  • output – Referência para a matriz onde o resultado ativado \( Y \) será armazenado.

inline virtual void Backward(Eigen::MatrixXf &grad_input, const Eigen::MatrixXf &grad_output) override#

Executa o backward pass da ativação Sigmoide.

  • Uma das propriedades matemáticas mais elegantes da função sigmoide é que sua derivada pode ser calculada diretamente a partir de sua saída \( Y \):

    \[ \sigma'(X) = Y \odot (1 - Y) \]

  • Para propagar o erro, aplicamos a Regra da Cadeia multiplicando o gradiente recebido \( \frac{\partial L}{\partial Y} \) pela derivada local através do Produto de Hadamard ( \( \odot \)):

    \[ \frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \odot (Y \odot (1 - Y)) \]

Parameters:
  • grad_input – Matriz que armazenará o gradiente calculado \( \frac{\partial L}{\partial X} \).

  • grad_output – Matriz contendo o gradiente recebido da camada seguinte \( \frac{\partial L}{\partial Y} \).

Exemplo de Aplicação#

Abaixo está um exemplo completo de como instanciar e treinar uma rede utilizando a Sigmoide Logística:

1    Model model;
2
3    // Camada Oculta: 784 entradas (pixels 28x28) para 128 neurônios
4    model.AddLayer(new DenseLayer(784, 128));
5    model.AddLayer(new Sigmoid());

Tangente Hiperbólica (Tanh)#

class Tanh : public Layer#

Implementa a função de ativação Tangente Hiperbólica (Tanh).

  • A função Tanh é uma versão reescalonada e deslocada da função Sigmoide. Sua principal vantagem matemática é mapear os valores de entrada para o intervalo \( (-1, 1) \), tornando as ativações centralizadas em zero (zero-centered). Isso reduz as oscilações durante a atualização dos pesos e frequentemente resulta em uma convergência mais rápida. A função matemática é definida como:

    \[ \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} \]

Public Functions

Tanh() = default#

Construtor padrão da camada tanh.

virtual ~Tanh() = default#

Destrutor padrão.

inline virtual void Forward(const Eigen::MatrixXf &input, Eigen::MatrixXf &output) override#

Executa o forward pass da ativação Tangente Hiperbólica.

  • Aplica a função \( \tanh \) a cada elemento da matriz de entrada utilizando a implementação otimizada da biblioteca Eigen.

    \[ Y = \tanh(X) \]

Parameters:
  • input – Referência constante para a matriz de entrada \( X \) de dimensões \( (batch\_size \times input\_size) \).

  • output – Referência para a matriz onde o resultado ativado \( Y \) será armazenado.

inline virtual void Backward(Eigen::MatrixXf &grad_input, const Eigen::MatrixXf &grad_output) override#

Executa o backward pass da ativação Tangente Hiperbólica.

  • A derivada matemática da função Tanh em relação à sua entrada \( X \) é dada por:

    \[ \tanh'(X) = 1 - \tanh^2(X) = 1 - Y^2 \]

  • Para propagar o erro, aplicamos a Regra da Cadeia multiplicando o gradiente recebido \( \frac{\partial L}{\partial Y} \) pela derivada local através do Produto de Hadamard ( \( \odot \)):

    \[ \frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \odot (1 - Y^2) \]

Parameters:
  • grad_input – Matriz que armazenará o gradiente calculado \( \frac{\partial L}{\partial X} \).

  • grad_output – Matriz contendo o gradiente recebido da camada seguinte \( \frac{\partial L}{\partial Y} \).

Exemplo de Aplicação#

Abaixo está um exemplo completo de como instanciar e treinar uma rede utilizando a Tangente Hiperbólica:

1    Model model;
2
3    // Camada Oculta: 784 entradas (pixels 28x28) para 128 neurônios
4    model.AddLayer(new DenseLayer(784, 128));
5    model.AddLayer(new Tanh());

Softmax#

class Softmax : public Layer#

Implementa a função de ativação Softmax.

  • A função Softmax é tipicamente utilizada na última camada de redes neurais de classificação multiclasse. Ela converte um vetor de números reais (logits) em uma distribuição de probabilidade, garantindo que todas as saídas estejam no intervalo \( (0, 1) \) e que a soma das probabilidades de todas as classes seja igual a 1. A função matemática para o i-ésimo elemento é:

    \[ y_i = \frac{e^{x_i}}{\sum_{j=1}^{C} e^{x_j}} \]
    Onde \( C \) é o número total de classes (neurônios na camada).

Public Functions

Softmax() = default#

Construtor padrão da camada Softmax.

virtual ~Softmax() = default#

Destrutor padrão.

inline virtual void Forward(const Eigen::MatrixXf &input, Eigen::MatrixXf &output) override#

Executa o forward pass da ativação Softmax.

  • Aplica a exponenciação a cada elemento e normaliza dividindo pela soma das exponenciais da respectiva amostra (linha).

Parameters:
  • input – Referência constante para a matriz de logits \( X \) de dimensões \( (batch\_size \times num\_classes) \).

  • output – Referência para a matriz onde a distribuição de probabilidade \( Y \) será armazenada.

inline virtual void Backward(Eigen::MatrixXf &grad_input, const Eigen::MatrixXf &grad_output) override#

Executa o backward pass da ativação Softmax.

  • Como a saída \( y_i \) depende de todos os \( x_j \), a derivada real da Softmax é uma matriz Jacobiana. Para evitar o custo de memória de instanciar uma matriz tridimensional para o batch, aplicamos o “Jacobian-vector product” diretamente.

  • A equação vetorizada do gradiente para uma amostra é dada por:

    \[ \frac{\partial L}{\partial X} = Y \odot \left( \frac{\partial L}{\partial Y} - \left( \sum_{j} \frac{\partial L}{\partial Y_j} \cdot Y_j \right) \mathbf{1} \right) \]

Parameters:
  • grad_input – Matriz que armazenará o gradiente calculado \( \frac{\partial L}{\partial X} \).

  • grad_output – Matriz contendo o gradiente recebido da camada seguinte (geralmente a função de perda) \( \frac{\partial L}{\partial Y} \).

Exemplo de Aplicação#

Abaixo está um exemplo completo de como instanciar e treinar uma rede utilizando a Softmax:

1    Model model;
2
3    // Camada Oculta: 784 entradas (pixels 28x28) para 128 neurônios
4    model.AddLayer(new DenseLayer(784, 128));
5    model.AddLayer(new Sigmoid());
6
7    // Camada de Saída: 128 entradas para 10 neurônios (classes de 0 a 9)
8    model.AddLayer(new DenseLayer(128, 10));
9    model.AddLayer(new Softmax());