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

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 estruturadas como classes em vez de funções isoladas. Isso permite definir tanto:

  1. A própria função de ativação (implementada via o método __call__()), permitindo que seja aplicada diretamente na classe Layer usando self.activation(z);
  2. Sua derivada (implementada via o método derivative()), possibilitando uma retropropagação eficiente e utilizada na classe Layer como self.activation.derivative(z).

Ao estruturar as funções de ativação como objetos, é possível passá-las facilmente para a classe Layer e utilizá-las de forma dinâmica.

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, elas são aplicadas ao vetor inteiro zz, assim como suas derivadas. O NumPy aplica internamente a operação a cada elemento do vetor. Por exemplo, se o vetor zz contém 3 elementos, a derivação é a seguinte:

f(z)=f([z1z2z3])=[f(z1)f(z2)f(z3)]f'(z) = f'( \begin{bmatrix} z_1\\ z_2\\ z_3 \end{bmatrix} ) = \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 a 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

Suggested prompts:

Can you explain how the backward() method uses the stored attributes in the Layer class?

What is the purpose of the activation function's derivative in backpropagation?

Could you provide an example of how to use these activation function classes in a neural network layer?

Awesome!

Completion rate improved to 4

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 estruturadas como classes em vez de funções isoladas. Isso permite definir tanto:

  1. A própria função de ativação (implementada via o método __call__()), permitindo que seja aplicada diretamente na classe Layer usando self.activation(z);
  2. Sua derivada (implementada via o método derivative()), possibilitando uma retropropagação eficiente e utilizada na classe Layer como self.activation.derivative(z).

Ao estruturar as funções de ativação como objetos, é possível passá-las facilmente para a classe Layer e utilizá-las de forma dinâmica.

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, elas são aplicadas ao vetor inteiro zz, assim como suas derivadas. O NumPy aplica internamente a operação a cada elemento do vetor. Por exemplo, se o vetor zz contém 3 elementos, a derivação é a seguinte:

f(z)=f([z1z2z3])=[f(z1)f(z2)f(z3)]f'(z) = f'( \begin{bmatrix} z_1\\ z_2\\ z_3 \end{bmatrix} ) = \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 a 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