Implementazione della Retropropagazione
Approccio Generale
Nella propagazione in avanti, ogni livello l prende come input le uscite del livello 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 altre informazioni necessarie sono memorizzate all'interno della classe.
Nella propagazione all'indietro, ogni livello l necessita solo di dal per calcolare i rispettivi gradienti e restituire dal−1, quindi il metodo backward()
prende come parametro il vettore dal. Il resto delle informazioni richieste è già memorizzato nella classe Layer
.
Derivate delle Funzioni di Attivazione
Poiché le derivate delle funzioni di attivazione sono necessarie per la retropropagazione, le funzioni di attivazione come ReLU e sigmoide dovrebbero essere strutturate come classi invece che come funzioni standalone. Questo consente di definire sia:
- La funzione di attivazione stessa (implementata tramite il metodo
__call__()
), permettendo di applicarla direttamente nella classeLayer
usandoself.activation(z)
; - La sua derivata (implementata tramite il metodo
derivative()
), abilitando una retropropagazione 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 (traspone) 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 livello l prende come input le uscite del livello 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 altre informazioni necessarie sono memorizzate all'interno della classe.
Nella propagazione all'indietro, ogni livello l necessita solo di dal per calcolare i rispettivi gradienti e restituire dal−1, quindi il metodo backward()
prende come parametro il vettore dal. Il resto delle informazioni richieste è già memorizzato nella classe Layer
.
Derivate delle Funzioni di Attivazione
Poiché le derivate delle funzioni di attivazione sono necessarie per la retropropagazione, le funzioni di attivazione come ReLU e sigmoide dovrebbero essere strutturate come classi invece che come funzioni standalone. Questo consente di definire sia:
- La funzione di attivazione stessa (implementata tramite il metodo
__call__()
), permettendo di applicarla direttamente nella classeLayer
usandoself.activation(z)
; - La sua derivata (implementata tramite il metodo
derivative()
), abilitando una retropropagazione 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 (traspone) un array.
Grazie per i tuoi commenti!