Implementazione della Retropropagazione
Approccio Generale
Nella propagazione in avanti, ogni strato l prende come input le uscite dello strato precedente, al−1, e calcola le proprie uscite. Pertanto, il metodo forward()
della classe Layer
accetta come unico parametro il vettore delle uscite precedenti, mentre le restanti informazioni necessarie sono memorizzate all'interno della classe.
Nella propagazione all'indietro, ogni strato l necessita solo di dal per calcolare i rispettivi gradienti e restituire dal−1, quindi il metodo backward()
prende come parametro il vettore dal. Le altre informazioni richieste sono già memorizzate nella classe Layer
.
Derivate delle Funzioni di Attivazione
Poiché le derivate delle funzioni di attivazione sono necessarie per la backpropagation, funzioni di attivazione come ReLU e sigmoide dovrebbero essere strutturate come classi invece che come funzioni indipendenti. Questo permette di definire sia:
- La funzione di attivazione stessa (implementata tramite il metodo
__call__()
), consentendo di applicarla direttamente nella classeLayer
usandoself.activation(z)
; - La sua derivata (implementata tramite il metodo
derivative()
), abilitando una backpropagation efficiente e utilizzata nella classeLayer
comeself.activation.derivative(z)
.
Strutturando le funzioni di attivazione come oggetti, è possibile passarle facilmente alla classe Layer
e utilizzarle in modo dinamico.
ReLU
La derivata della funzione di attivazione ReLU è la seguente, dove zi è un elemento del vettore delle pre-attivazioni 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)
Sigmoide
La derivata della funzione di attivazione sigmoide è la seguente:
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)
Per entrambe queste funzioni di attivazione, l'applicazione avviene sull'intero vettore z, così come per le loro derivate. NumPy applica internamente l'operazione a ciascun elemento del vettore. Ad esempio, se il vettore z contiene 3 elementi, la derivazione è la seguente:
f′(z)=f′(z1z2z3)=f′(z1)f′(z2)f′(z3)Il metodo backward()
Il metodo backward()
è responsabile del calcolo dei gradienti utilizzando le seguenti formule:
a^{l-1} e zl sono memorizzati rispettivamente come attributi inputs
e outputs
nella classe Layer
. La funzione di attivazione f è memorizzata come attributo activation
.
Una volta calcolati tutti i gradienti necessari, pesi e bias possono essere aggiornati poiché non sono più necessari per ulteriori calcoli:
Wlbl=Wl−α⋅dWl=bl−α⋅dblPertanto, learning_rate
(α) è un altro parametro di questo metodo.
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
L'operatore *
esegue la moltiplicazione elemento per elemento, mentre la funzione np.dot()
esegue il prodotto scalare in NumPy. L'attributo .T
trasporta un array.
Grazie per i tuoi commenti!
Chieda ad AI
Chieda ad AI
Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione
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
Implementazione della Retropropagazione
Scorri per mostrare il menu
Approccio Generale
Nella propagazione in avanti, ogni strato l prende come input le uscite dello strato precedente, al−1, e calcola le proprie uscite. Pertanto, il metodo forward()
della classe Layer
accetta come unico parametro il vettore delle uscite precedenti, mentre le restanti informazioni necessarie sono memorizzate all'interno della classe.
Nella propagazione all'indietro, ogni strato l necessita solo di dal per calcolare i rispettivi gradienti e restituire dal−1, quindi il metodo backward()
prende come parametro il vettore dal. Le altre informazioni richieste sono già memorizzate nella classe Layer
.
Derivate delle Funzioni di Attivazione
Poiché le derivate delle funzioni di attivazione sono necessarie per la backpropagation, funzioni di attivazione come ReLU e sigmoide dovrebbero essere strutturate come classi invece che come funzioni indipendenti. Questo permette di definire sia:
- La funzione di attivazione stessa (implementata tramite il metodo
__call__()
), consentendo di applicarla direttamente nella classeLayer
usandoself.activation(z)
; - La sua derivata (implementata tramite il metodo
derivative()
), abilitando una backpropagation efficiente e utilizzata nella classeLayer
comeself.activation.derivative(z)
.
Strutturando le funzioni di attivazione come oggetti, è possibile passarle facilmente alla classe Layer
e utilizzarle in modo dinamico.
ReLU
La derivata della funzione di attivazione ReLU è la seguente, dove zi è un elemento del vettore delle pre-attivazioni 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)
Sigmoide
La derivata della funzione di attivazione sigmoide è la seguente:
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)
Per entrambe queste funzioni di attivazione, l'applicazione avviene sull'intero vettore z, così come per le loro derivate. NumPy applica internamente l'operazione a ciascun elemento del vettore. Ad esempio, se il vettore z contiene 3 elementi, la derivazione è la seguente:
f′(z)=f′(z1z2z3)=f′(z1)f′(z2)f′(z3)Il metodo backward()
Il metodo backward()
è responsabile del calcolo dei gradienti utilizzando le seguenti formule:
a^{l-1} e zl sono memorizzati rispettivamente come attributi inputs
e outputs
nella classe Layer
. La funzione di attivazione f è memorizzata come attributo activation
.
Una volta calcolati tutti i gradienti necessari, pesi e bias possono essere aggiornati poiché non sono più necessari per ulteriori calcoli:
Wlbl=Wl−α⋅dWl=bl−α⋅dblPertanto, learning_rate
(α) è un altro parametro di questo metodo.
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
L'operatore *
esegue la moltiplicazione elemento per elemento, mentre la funzione np.dot()
esegue il prodotto scalare in NumPy. L'attributo .T
trasporta un array.
Grazie per i tuoi commenti!