Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda Implementação do Backpropagation | Rede Neural do Zero
Introdução às Redes Neurais com Python

bookImplementação do Backpropagation

Abordagem Geral

Na propagação direta, cada camada ll recebe as saídas da camada anterior, al1a^{l-1}, como entradas e calcula suas próprias saídas. Portanto, o método forward() da classe Layer recebe o vetor de saídas anteriores como seu único parâmetro, enquanto o restante das informações necessárias é armazenado dentro da classe.

Na propagação reversa, cada camada ll precisa apenas de dalda^l para calcular os respectivos gradientes e retornar dal1da^{l-1}, então o método backward() recebe o vetor dalda^l como parâmetro. O restante das informações necessárias já está armazenado na classe Layer.

Derivadas das Funções de Ativação

Como as derivadas das funções de ativação são necessárias para a retropropagação, funções de ativação como ReLU e sigmoide devem ser implementadas como classes em vez de funções isoladas. Essa estrutura permite definir claramente ambos os componentes:

  1. A própria função de ativação — implementada usando o método __call__(), podendo ser aplicada diretamente na classe Layer com self.activation(z);
  2. Sua derivada — implementada usando o método derivative(), permitindo o cálculo eficiente durante a retropropagação via self.activation.derivative(z).

Representar funções de ativação como objetos facilita o repasse para diferentes camadas e a aplicação dinâmica durante a propagação direta e reversa.

ReLu

A derivada da função de ativação ReLU é a seguinte, onde ziz_i é um elemento do vetor de pré-ativações zz:

f(zi)={1,zi>00,zi0f'(z_i) = \begin{cases} 1, z_i > 0\\ 0, z_i \le 0 \end{cases}
class ReLU:
    def __call__(self, z):
        return np.maximum(0, z)

    def derivative(self, z):
        return (z > 0).astype(float)

Sigmoid

A derivada da função de ativação sigmoid é a seguinte:

f(zi)=f(zi)(1f(zi))f'(z_i) = f(z_i) \cdot (1 - f(z_i))
class Sigmoid:
    def __call__(self, x):
        return 1 / (1 + np.exp(-z))

    def derivative(self, z):
        sig = self(z)
        return sig * (1 - sig)

Para ambas as funções de ativação, a operação é aplicada a todo o vetor zz, assim como à sua derivada. O NumPy realiza automaticamente o cálculo elemento a elemento, ou seja, cada elemento do vetor é processado de forma independente.

Por exemplo, se o vetor zz contém três elementos, a derivada é calculada como:

f(z)=f([z1z2z3])=[f(z1)f(z2)f(z3)]f'(z) = f'\left( \begin{bmatrix} z_1\\ z_2\\ z_3 \end{bmatrix} \right) = \begin{bmatrix} f'(z_1)\\ f'(z_2)\\ f'(z_3) \end{bmatrix}

O método backward()

O método backward() é responsável por calcular os gradientes utilizando as fórmulas abaixo:

dzl=dalfl(zl)dWl=dzl(al1)Tdbl=dzldal1=(Wl)Tdzl\begin{aligned} dz^l &= da^l \odot f'^l(z^l)\\ dW^l &= dz^l \cdot (a^{l-1})^T\\ db^l &= dz^l\\ da^{l-1} &= (W^l)^T \cdot dz^l \end{aligned}

a^{l-1} e zlz^l são armazenados como os atributos inputs e outputs na classe Layer, respectivamente. A função de ativação ff é armazenada como o atributo activation.

Após todos os gradientes necessários serem calculados, os pesos e biases podem ser atualizados, pois não são mais necessários para cálculos posteriores:

Wl=WlαdWlbl=blαdbl\begin{aligned} W^l &= W^l - \alpha \cdot dW^l\\ b^l &= b^l - \alpha \cdot db^l \end{aligned}

Portanto, learning_rate (α\alpha) é outro parâmetro deste método.

def backward(self, da, learning_rate):
    dz = ...
    d_weights = ...
    d_biases = ...
    da_prev = ...

    self.weights -= learning_rate * d_weights
    self.biases -= learning_rate * d_biases

    return da_prev
Note
Nota

O operador * realiza multiplicação elemento a elemento, enquanto a função np.dot() executa o produto escalar no NumPy. O atributo .T transpõe um array.

question mark

Qual das alternativas a seguir melhor descreve o papel do método backward() na classe Layer durante a retropropagação?

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 2. Capítulo 8

Pergunte à IA

expand

Pergunte à IA

ChatGPT

Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo

bookImplementação do Backpropagation

Deslize para mostrar o menu

Abordagem Geral

Na propagação direta, cada camada ll recebe as saídas da camada anterior, al1a^{l-1}, como entradas e calcula suas próprias saídas. Portanto, o método forward() da classe Layer recebe o vetor de saídas anteriores como seu único parâmetro, enquanto o restante das informações necessárias é armazenado dentro da classe.

Na propagação reversa, cada camada ll precisa apenas de dalda^l para calcular os respectivos gradientes e retornar dal1da^{l-1}, então o método backward() recebe o vetor dalda^l como parâmetro. O restante das informações necessárias já está armazenado na classe Layer.

Derivadas das Funções de Ativação

Como as derivadas das funções de ativação são necessárias para a retropropagação, funções de ativação como ReLU e sigmoide devem ser implementadas como classes em vez de funções isoladas. Essa estrutura permite definir claramente ambos os componentes:

  1. A própria função de ativação — implementada usando o método __call__(), podendo ser aplicada diretamente na classe Layer com self.activation(z);
  2. Sua derivada — implementada usando o método derivative(), permitindo o cálculo eficiente durante a retropropagação via self.activation.derivative(z).

Representar funções de ativação como objetos facilita o repasse para diferentes camadas e a aplicação dinâmica durante a propagação direta e reversa.

ReLu

A derivada da função de ativação ReLU é a seguinte, onde ziz_i é um elemento do vetor de pré-ativações zz:

f(zi)={1,zi>00,zi0f'(z_i) = \begin{cases} 1, z_i > 0\\ 0, z_i \le 0 \end{cases}
class ReLU:
    def __call__(self, z):
        return np.maximum(0, z)

    def derivative(self, z):
        return (z > 0).astype(float)

Sigmoid

A derivada da função de ativação sigmoid é a seguinte:

f(zi)=f(zi)(1f(zi))f'(z_i) = f(z_i) \cdot (1 - f(z_i))
class Sigmoid:
    def __call__(self, x):
        return 1 / (1 + np.exp(-z))

    def derivative(self, z):
        sig = self(z)
        return sig * (1 - sig)

Para ambas as funções de ativação, a operação é aplicada a todo o vetor zz, assim como à sua derivada. O NumPy realiza automaticamente o cálculo elemento a elemento, ou seja, cada elemento do vetor é processado de forma independente.

Por exemplo, se o vetor zz contém três elementos, a derivada é calculada como:

f(z)=f([z1z2z3])=[f(z1)f(z2)f(z3)]f'(z) = f'\left( \begin{bmatrix} z_1\\ z_2\\ z_3 \end{bmatrix} \right) = \begin{bmatrix} f'(z_1)\\ f'(z_2)\\ f'(z_3) \end{bmatrix}

O método backward()

O método backward() é responsável por calcular os gradientes utilizando as fórmulas abaixo:

dzl=dalfl(zl)dWl=dzl(al1)Tdbl=dzldal1=(Wl)Tdzl\begin{aligned} dz^l &= da^l \odot f'^l(z^l)\\ dW^l &= dz^l \cdot (a^{l-1})^T\\ db^l &= dz^l\\ da^{l-1} &= (W^l)^T \cdot dz^l \end{aligned}

a^{l-1} e zlz^l são armazenados como os atributos inputs e outputs na classe Layer, respectivamente. A função de ativação ff é armazenada como o atributo activation.

Após todos os gradientes necessários serem calculados, os pesos e biases podem ser atualizados, pois não são mais necessários para cálculos posteriores:

Wl=WlαdWlbl=blαdbl\begin{aligned} W^l &= W^l - \alpha \cdot dW^l\\ b^l &= b^l - \alpha \cdot db^l \end{aligned}

Portanto, learning_rate (α\alpha) é outro parâmetro deste método.

def backward(self, da, learning_rate):
    dz = ...
    d_weights = ...
    d_biases = ...
    da_prev = ...

    self.weights -= learning_rate * d_weights
    self.biases -= learning_rate * d_biases

    return da_prev
Note
Nota

O operador * realiza multiplicação elemento a elemento, enquanto a função np.dot() executa o produto escalar no NumPy. O atributo .T transpõe um array.

question mark

Qual das alternativas a seguir melhor descreve o papel do método backward() na classe Layer durante a retropropagação?

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 2. Capítulo 8
some-alt