Implementação do Backpropagation
Abordagem Geral
Na propagação direta, cada camada l recebe as saídas da camada anterior, al−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 l precisa apenas de dal para calcular os respectivos gradientes e retornar dal−1, então o método backward()
recebe o vetor dal 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:
- A própria função de ativação (implementada via o método
__call__()
), permitindo que seja aplicada diretamente na classeLayer
usandoself.activation(z)
; - Sua derivada (implementada via o método
derivative()
), possibilitando uma retropropagação eficiente e utilizada na classeLayer
comoself.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 zi é um elemento do vetor de pré-ativações z:
f′(zi)={1,zi>00,zi≤0class 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)⋅(1−f(zi))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 z, assim como suas derivadas. O NumPy aplica internamente a operação a cada elemento do vetor. Por exemplo, se o vetor z contém 3 elementos, a derivação é a seguinte:
f′(z)=f′(z1z2z3)=f′(z1)f′(z2)f′(z3)O método backward()
O método backward()
é responsável por calcular os gradientes utilizando as fórmulas abaixo:
a^{l-1} e zl são armazenados como os atributos inputs
e outputs
na classe Layer
, respectivamente. A função de ativação f é 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:
Wlbl=Wl−α⋅dWl=bl−α⋅dblPortanto, learning_rate
(α) é 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
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.
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
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
Implementação do Backpropagation
Deslize para mostrar o menu
Abordagem Geral
Na propagação direta, cada camada l recebe as saídas da camada anterior, al−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 l precisa apenas de dal para calcular os respectivos gradientes e retornar dal−1, então o método backward()
recebe o vetor dal 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:
- A própria função de ativação (implementada via o método
__call__()
), permitindo que seja aplicada diretamente na classeLayer
usandoself.activation(z)
; - Sua derivada (implementada via o método
derivative()
), possibilitando uma retropropagação eficiente e utilizada na classeLayer
comoself.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 zi é um elemento do vetor de pré-ativações z:
f′(zi)={1,zi>00,zi≤0class 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)⋅(1−f(zi))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 z, assim como suas derivadas. O NumPy aplica internamente a operação a cada elemento do vetor. Por exemplo, se o vetor z contém 3 elementos, a derivação é a seguinte:
f′(z)=f′(z1z2z3)=f′(z1)f′(z2)f′(z3)O método backward()
O método backward()
é responsável por calcular os gradientes utilizando as fórmulas abaixo:
a^{l-1} e zl são armazenados como os atributos inputs
e outputs
na classe Layer
, respectivamente. A função de ativação f é 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:
Wlbl=Wl−α⋅dWl=bl−α⋅dblPortanto, learning_rate
(α) é 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
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.
Obrigado pelo seu feedback!