Neural Network: Backpropagation nell'Output Layer

Parte (2/4)

Potrebbe interessarti:

Il progetto Python, che comprende gli script mostrati in questo post, e quelli relativi alla creazione dei grafici Matplotlib e Plotly, è disponibile assieme al corso Introduzione al Machine Learning.

Dimmi di Più

Backpropagation

La Backpropagation, abbreviazione di “backward propagation of errors” (propagazione all’indietro degli errori), è il procedimento utilizzato per calcolare come la funzione di costo varia rispetto ai diversi parametri di una Rete Neurale. I gradienti così ottenuti possono quindi essere utilizzati da un algoritmo di ottimizzazione, come la Discesa del Gradiente, per aggiornare pesi e bias e ridurre progressivamente l’errore del modello.

Nella fase di Forward Propagation, i dati di input attraversano la rete procedendo da un layer al successivo. In ciascun layer successivo a quello di input, ogni nodo calcola una combinazione lineare dei valori ricevuti, moltiplicandoli per i rispettivi pesi e aggiungendo un termine di bias. Al risultato viene quindi applicata una funzione di attivazione, come la Sigmoide. Considerando l’architettura già vista di una rete con un singolo hidden layer, ogni nodo $a^h_j$ produce una trasformazione non-lineare degli input, che possiamo interpretare intuitivamente come una funzione “base” $\phi_j$.

Le attivazioni dell’hidden layer costituiscono quindi nuove feature apprese dal modello e definiscono una rappresentazione dei dati in un nuovo spazio delle feature latenti. Immaginaniamo questa trasformazione come un’operazione che “piega” o “deforma” lo spazio delle feature originale. Se la trasformazione appresa è adatta al problema, osservazioni difficili da separare nello spazio originale possono diventare più facilmente separabili nel nuovo spazio.

Le feature prodotte dall’hidden layer vengono quindi combinate linearmente dai nodi dell’output layer. Nel caso di un singolo nodo di output $a^o_1$, utilizzando una funzione Sigmoide, il valore prodotto può essere interpretato come la probabilità stimata che l’osservazione appartenga alla classe positiva, $1$. Durante l’addestramento, questa previsione viene confrontata con il valore reale $y$ mediante una funzione di Loss, come la Logistic Loss, che misura quanto la previsione del modello differisce dal risultato desiderato.

A questo punto entra in gioco la fase di Backpropagation. Partendo dall’output layer, calcoliamo come la funzione di costo varia rispetto a ciascun parametro della rete. In altre parole, vogliamo determinare quanto una piccola variazione di ogni peso o bias influenzi il valore della funzione di costo. Matematicamente, questo processo viene effettuato utilizzando la chain rule del calcolo differenziale, che permette di calcolare la derivata di una composizione di funzioni attraverso una sequenza di derivate concatenate. La Backpropagation consente quindi di calcolare le derivate parziali della funzione di costo rispetto a ciascun peso e bias del modello. Il calcolo procede a ritroso attraverso la rete: dall’output layer verso gli hidden layer precedenti, fino al primo layer contenente parametri apprendibili. Una volta calcolati tutti i gradienti, questi possono essere utilizzati per aggiornare i parametri del modello con un algoritmo di ottimizzazione come la Discesa del Gradiente.

Per un generico parametro $w_j$, la derivata:

$$ \frac{\partial J}{\partial w_j} $$

indica quanto e in quale direzione la funzione di costo $J$ aumenta al variare di quel parametro. La Discesa del Gradiente modifica quindi il parametro nella direzione opposta:

$$ w_j \leftarrow w_j - \eta \frac{\partial J}{\partial w_j} $$

dove $\eta$ rappresenta il learning rate.

Il gradiente della funzione di costo indica infatti la direzione di massima crescita locale; il suo opposto, il gradiente negativo, indica invece la direzione di discesa più ripida. Aggiornando progressivamente tutti i parametri in questa direzione, cerchiamo quindi di ridurre il valore della funzione di costo.

Semplificando, è possibile immaginare una Rete Neurale che commette un errore come un apparecchio il cui funzionamento dipende dalla regolazione di molti componenti. I gradienti ci indicano come una piccola modifica di ciascun componente influenzi l’errore complessivo. Il processo di ottimizzazione usa queste informazioni per regolare progressivamente ogni parametro nella direzione che riduce l’errore del modello.

Chain Rule

La Chain Rule (regola della catena) permette di calcolare la derivata di una funzione ottenuta dalla composizione di altre funzioni. In particolare, afferma che la derivata di una composizione di funzioni può essere calcolata moltiplicando tra loro le derivate delle funzioni che compongono tale relazione. Più precisamente, utilizzando la notazione di Leibniz, se $f$ e $g$ sono funzioni differenziabili, allora:

$$ \require{physics} \begin{align} \dv{f(g(x))}{x} &= \dv{f(g(x))}{g(x)} \cdot \dv{g(x)}{x} \label{chain-1}\tag{1}\\[6pt] \end{align} $$

Indicando semplicemente con $g$ il valore intermedio $g(x)$, possiamo scrivere la stessa relazione in forma più compatta:

$$ \begin{align} \dv{f}{x} &= \dv{f}{g} \cdot \dv{g}{x} \label{chain-2}\tag{2}\\[6pt] \end{align} $$

La Chain Rule può essere applicata anche a composizioni arbitrariamente lunghe. Ad esempio, se:

$$ f=f(g), \qquad g=g(h), \qquad h=h(u), \qquad u=u(v), \qquad v=v(x) $$

allora:

$$ \begin{align} \dv{f}{x} &= \dv{f}{g} \cdot \dv{g}{h} \cdot \dv{h}{u} \cdot \dv{u}{v} \cdot \dv{v}{x} \label{chain-3}\tag{3}\\[6pt] \end{align} $$

Consideriamo, ad esempio, la funzione:

$$ \begin{align} h(x) &= 3(2x^2)^5 \end{align} $$

Possiamo rappresentarla come la composizione di due funzioni più semplici:

$$ \begin{align} h(x) & = f(g(x))\\[6pt] f(x) & = 3g(x)^5\\[6pt] g(x) & = 2x^2\\[6pt] \end{align} $$

Applicando la Chain Rule:

$$ \begin{align} \dv{h(x)}{x} &= \dv{f(g(x))}{x}\\[6pt] &= \dv{f(g(x))}{g(x)} \cdot \dv{g(x)}{x}\\[6pt] &= \dv{3g(x)^5}{g(x)} \cdot \dv{2x^2}{x}\\[6pt] &= 15g(x)^4 \cdot 4x \end{align} $$

e, sostituendo nuovamente $g(x)=2x^2$:

$$ \dv{h(x)}{x} = 15\left(2x^2\right)^4 \cdot 4x = 60x(2x^2)^4 = 960x^9 $$

Chain Rule Multivariabile

Quando una funzione dipende da più variabili, la Chain Rule deve tenere conto di tutti i percorsi attraverso i quali la variabile rispetto alla quale stiamo derivando può influenzare il risultato. In questo caso utilizziamo le derivate parziali, perché vogliamo misurare come varia una funzione rispetto a una determinata variabile, mantenendo costanti le altre. Consideriamo, ad esempio:

$$ f\left(g\left(y,h(x,z)\right)\right) $$

e supponiamo che $y$ e $z$ siano indipendenti da $x$. Esiste quindi un unico percorso attraverso il quale $x$ può influenzare il valore finale:

$$ x \longrightarrow h \longrightarrow g \longrightarrow f $$

Applicando la Chain Rule:

$$ \begin{align} \pdv{f}{x} &= \pdv{f}{g} \cdot \pdv{g}{h} \cdot \pdv{h}{x} \end{align} $$

oppure, esplicitando completamente le funzioni:

$$ \begin{align} \pdv{f(g(y,h(x,z)))}{x} &= \pdv{f(g(y,h(x,z)))}{g(y,h(x,z))} \cdot \pdv{g(y,h(x,z))}{h(x,z)} \cdot \pdv{h(x,z)}{x} \end{align} $$

Nel calcolo della derivata rispetto a $x$, il percorso che passa attraverso $y$ non produce alcun contributo, perché abbiamo assunto che $y$ sia indipendente da $x$:

$$ \pdv{y}{x}=0 $$

Lo stesso vale per $z$, se anch’essa è indipendente da $x$. Esiste però un caso ancora più importante. Supponiamo che una variabile $x$ influenzi una funzione finale $f$ attraverso due percorsi differenti:

$$ \begin{align} x & \longrightarrow g\longrightarrow f\\ x & \longrightarrow h\longrightarrow f \end{align} $$

Se:

$$ f=f(g(x),h(x)) $$

allora la derivata totale rispetto a $x$ è:

$$ \begin{align} \dv{f}{x} &= \pdv{f}{g} \cdot \dv{g}{x} + \pdv{f}{h} \cdot \dv{h}{x} \end{align} $$

I contributi provenienti dai diversi percorsi vengono quindi sommati. Questo principio sarà particolarmente importante nella Backpropagation: un neurone può infatti contribuire all’output della rete attraverso più connessioni e più percorsi differenti.

Chain Rule e Backpropagation

Nei diagrammi seguenti mostriamo la composizione di funzioni utilizzata durante la Forward Propagation e le derivate parziali calcolate durante la Backpropagation. Per semplicità consideriamo inizialmente soltanto i pesi $w$, tralasciando temporaneamente i bias $b$. All’interno di ogni nodo distinguiamo le due operazioni fondamentali:

  • la combinazione lineare degli input, $z$;
  • la funzione di attivazione, $a=\sigma(z)$.

Ripensando alla fase di Forward Propagation, abbiamo visto come l’output della rete sia il risultato di una composizione di funzioni. Considerando il semplice percorso mostrato nello schema seguente:

Diagramma della fase di Forward Propagation
Figura 1: Fase di Forward Propagation

possiamo scrivere:

$$ \begin{align} \text{out} &= a^o_1\left( z^o_1\left( a^h_1\left( z^h_1\left(x_1,w^h_{1,1}\right) \right), w^o_{1,1} \right) \right)\\[6pt] \text{loss} &= L\left(y,\text{out}\right)\\[6pt] &= L\left( y, a^o_1\left( z^o_1\left( a^h_1\left( z^h_1\left(x_1,w^h_{1,1}\right) \right), w^o_{1,1} \right) \right) \right) \end{align} $$

Nell’output layer, per calcolare la derivata parziale della Loss $L$ rispetto al parametro $w^o_{1,1}$, applichiamo quindi la Chain Rule:

Diagramma della fase di Backpropagation, considerando l'Output Layer
Figura 2: Fase di Backpropagation #1
$$ \begin{align} \pdv{L}{w^o_{1,1}} &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{w^o_{1,1}} \end{align} $$

Per calcolare invece la derivata parziale della Loss rispetto al parametro $w^h_{1,1}$ dell’hidden layer, dobbiamo seguire l’intero percorso che collega tale parametro alla Loss:

Diagramma della fase di Backpropagation, considerando l'Output e l'Hidden Layer
Figura 3: Fase di Backpropagation #2
$$ \begin{align} \pdv{L}{w^h_{1,1}} &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{a^h_1} \cdot \pdv{a^h_1}{z^h_1} \cdot \pdv{z^h_1}{w^h_{1,1}} \end{align} $$

La Chain Rule permette quindi di scomporre il calcolo della derivata rispetto a ciascun parametro della rete in una sequenza di derivate locali, molto più semplici da calcolare rispetto alla derivazione diretta dell’intera funzione composta. La Backpropagation sfrutta questa struttura procedendo dalla Loss verso i layer precedenti e, soprattutto, riutilizzando i risultati intermedi già calcolati. In questo modo evita di ripetere molte volte gli stessi calcoli e rende efficiente la determinazione dei gradienti anche in reti formate da molti layer e molti parametri. Vediamo quindi come calcolare i gradienti della funzione di costo nell’output layer e nell’hidden layer.

In questa semplice rete esiste un unico percorso tra $w^h_{1,1}$ e l’output. In una rete con più neuroni, invece, l’attivazione di un nodo può influenzare diversi nodi del layer successivo. In questo caso la derivata deve includere la somma dei contributi provenienti da tutti i percorsi che collegano quel parametro alla Loss.

Backpropagation nell’Output Layer

Calcolo del Gradiente Rispetto ai Pesi

Consideriamo inizialmente una rete con un singolo hidden layer, formato da due nodi, e un output layer anch’esso formato da due nodi. Vogliamo calcolare le derivate parziali della Loss $L$ rispetto a ciascun peso $w^o_{t,s}$ dell’output layer. Lo stesso procedimento verrà successivamente applicato ai parametri di bias $b^o_t$. In seguito esprimeremo le equazioni ottenute in forma vettorizzata, utilizzando vettori e matrici, e le generalizzeremo a layer con un numero arbitrario di nodi.

Calcolo della Derivata Parziale di L rispetto a $w^o_{1,1}$
Figura 4: Derivata Parziale di L rispetto a $w^o_{1,1}$
$$ \begin{align} \pdv{L}{w^o_{1,1}} &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{w^o_{1,1}} \end{align} $$
Calcolo della Derivata Parziale di L rispetto a $w^o_{1,2}$
Figura 5: Derivata Parziale di L rispetto a $w^o_{1,2}$
$$ \begin{align} \pdv{L}{w^o_{1,2}} &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{w^o_{1,2}} \end{align} $$
Calcolo della Derivata Parziale di L rispetto a $w^o_{2,1}$
Figura 6: Derivata Parziale di L rispetto a $w^o_{2,1}$
$$ \begin{align} \pdv{L}{w^o_{2,1}} &= \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2} \cdot \pdv{z^o_2}{w^o_{2,1}} \end{align} $$
Calcolo della Derivata Parziale di L rispetto a $w^o_{2,2}$
Figura 7: Derivata Parziale di L rispetto a $w^o_{2,2}$
$$ \begin{align} \pdv{L}{w^o_{2,2}} &= \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2} \cdot \pdv{z^o_2}{w^o_{2,2}} \end{align} $$

Riassumendo:

$$ \begin{align} \pdv{L}{w^o_{1,1}} &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{w^o_{1,1}}\\[6pt] \pdv{L}{w^o_{1,2}} &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{w^o_{1,2}}\\[6pt] \pdv{L}{w^o_{2,1}} &= \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2} \cdot \pdv{z^o_2}{w^o_{2,1}}\\[6pt] \pdv{L}{w^o_{2,2}} &= \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2} \cdot \pdv{z^o_2}{w^o_{2,2}} \end{align} $$

Possiamo rendere queste espressioni più compatte introducendo un termine $d^o_j$ per ciascun nodo dell’output layer:

$$ \begin{align} d^o_j &= \pdv{L}{z^o_j} = \pdv{L}{a^o_j} \cdot \pdv{a^o_j}{z^o_j} \end{align} $$

Il termine $d^o_j$ rappresenta la derivata della Loss rispetto alla combinazione lineare $z^o_j$ del $j$-esimo nodo dell’output layer. Nel nostro esempio:

$$ \begin{align} d^o_1 &= \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1}\\[6pt] d^o_2 &= \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2} \end{align} $$

Quindi:

$$ \begin{align} \pdv{L}{w^o_{1,1}} &= d^o_1 \cdot \pdv{z^o_1}{w^o_{1,1}}\\[6pt] \pdv{L}{w^o_{1,2}} &= d^o_1 \cdot \pdv{z^o_1}{w^o_{1,2}}\\[6pt] \pdv{L}{w^o_{2,1}} &= d^o_2 \cdot \pdv{z^o_2}{w^o_{2,1}}\\[6pt] \pdv{L}{w^o_{2,2}} &= d^o_2 \cdot \pdv{z^o_2}{w^o_{2,2}} \end{align} $$

Ricordando che:

$$ \begin{align} z^o_1 &= w^o_{1,1}a^h_1 + w^o_{1,2}a^h_2 + b^o_1\\[6pt] z^o_2 &= w^o_{2,1}a^h_1 + w^o_{2,2}a^h_2 + b^o_2 \end{align} $$

otteniamo:

$$ \begin{align} \pdv{z^o_1}{w^o_{1,1}} &= a^h_1 & \pdv{z^o_1}{w^o_{1,2}} &= a^h_2\\[6pt] \pdv{z^o_2}{w^o_{2,1}} &= a^h_1 & \pdv{z^o_2}{w^o_{2,2}} &= a^h_2. \end{align} $$

Di conseguenza:

$$ \begin{align} \pdv{L}{w^o_{1,1}} &= d^o_1 a^h_1\\[6pt] \pdv{L}{w^o_{1,2}} &= d^o_1 a^h_2\\[6pt] \pdv{L}{w^o_{2,1}} &= d^o_2 a^h_1\\[6pt] \pdv{L}{w^o_{2,2}} &= d^o_2 a^h_2 \end{align} $$

E’ possibile esprimere queste equazioni in forma vettoriale:

$$ \begin{align} \mathbf{d}^o & = \begin{bmatrix} d^o_1\\[6pt] d^o_2\\[6pt] \end{bmatrix} = \begin{bmatrix} \partial{L} / \partial{a^o_1}\\[6pt] \partial{L} / \partial{a^o_2}\\[6pt] \end{bmatrix} \odot \begin{bmatrix} \partial{a^o_1} / \partial{z^o_1}\\[6pt] \partial{a^o_2} / \partial{z^o_2}\\[6pt] \end{bmatrix}\\[6pt] \pdv{L}{\mathbf{W}^o} & = \begin{bmatrix} d^o_1\\[6pt] d^o_2\\[6pt] \end{bmatrix} \cdot \begin{bmatrix} a^h_1 & a^h_2\\[6pt] \end{bmatrix} \end{align} $$

dove $\odot$ rappresenta il prodotto di Hadamard, cioè la moltiplicazione elemento per elemento tra vettori o matrici della stessa dimensione.

Generalizzazione

Consideriamo ora una singola osservazione $\mathbf{x}^{(i)}$, un output layer formato da $n_o$ nodi e un hidden layer formato da $n_h$ nodi. Definiamo:

$$ \begin{align} \mathbf{d}^{o(i)} &= \begin{bmatrix} d^{o(i)}_1\\[6pt] d^{o(i)}_2\\[6pt] \vdots\\[6pt] d^{o(i)}_{n_o} \end{bmatrix} = \begin{bmatrix} \partial{L^{(i)}} / \partial{a^{o(i)}_1}\\[6pt] \partial{L^{(i)}} / \partial{a^{o(i)}_2}\\[6pt] \vdots\\[6pt] \partial{L^{(i)}} / \partial{a^{o(i)}_{n_o}}\\[6pt] \end{bmatrix} \odot \begin{bmatrix} \partial{a^{o(i)}_1} / {z^{o(i)}_1}\\[6pt] \partial{a^{o(i)}_2} / {z^{o(i)}_2}\\[6pt] \vdots\\[6pt] \partial{a^{o(i)}_{n_o}} / {z^o_{n_o}}\\[6pt] \end{bmatrix} = \pdv{L^{(i)}}{\mathbf{a}^{o(i)}} \odot \pdv{\mathbf{a}^{o(i)}}{\mathbf{z}^{o(i)}} = \pdv{L^{(i)}}{\mathbf{a}^{o(i)}} \odot \sigma'\left(\mathbf{z}^{o(i)}\right) \\[12pt] \pdv{L^{(i)}}{\mathbf{W}^o} & = \begin{bmatrix} d^{o(i)}_1\\[6pt] d^{o(i)}_2\\[6pt] \vdots\\[6pt] d^{o(i)}_{n_o} \end{bmatrix} \cdot \begin{bmatrix} a^{h(i)}_1 & a^{h(i)}_2 & \cdots & a^{h(i)}_{n_h}\\[6pt] \end{bmatrix} = \mathbf{d}^{o(i)} \cdot \left(\mathbf{a}^{h(i)}\right)^\intercal \end{align} $$

Notiamo che la derivata parziale di $L^{(i)}$ rispetto a $\mathbf{W}^o$ ha dimensione $(n_o\times1)(1\times n_h) = (n_o\times n_h)$, che corrisponde esattamente alla dimensione della matrice $\mathbf W^o$.

Consideriamo ora un dataset composto da $m$ osservazioni multiple. Per ciascuna osservazione $i$, definiamo:

$$ d^{o(i)}_j = \pdv{L^{(i)}}{z^{o(i)}_j} = \pdv{L^{(i)}}{a^{o(i)}_j} \cdot \pdv{a^{o(i)}_j}{z^{o(i)}_j} $$

Raccogliendo questi valori per tutte le osservazioni, otteniamo la matrice $\mathbf{D^o}$, di dimensione $m\times n_o$:

$$ \mathbf D^o = \begin{bmatrix} d^{o(1)}_1 & d^{o(1)}_2 & \dots & d^{o(1)}_{n_o}\\[6pt] d^{o(2)}_1 & d^{o(2)}_2 & \dots & d^{o(2)}_{n_o}\\[6pt] \vdots & \vdots & \ddots & \vdots\\[6pt] d^{o(m)}_1 & d^{o(m)}_2 & \dots & d^{o(m)}_{n_o} \end{bmatrix} $$

Analogamente, la matrice delle attivazioni dell’hidden layer $\mathbf{A^h}$ ha dimensione $m\times n_h$:

$$ \mathbf A^h = \begin{bmatrix} a^{h(1)}_1 & a^{h(1)}_2 & \dots & a^{h(1)}_{n_h}\\[6pt] a^{h(2)}_1 & a^{h(2)}_2 & \dots & a^{h(2)}_{n_h}\\[6pt] \vdots & \vdots & \ddots & \vdots\\[6pt] a^{h(m)}_1 & a^{h(m)}_2 & \dots & a^{h(m)}_{n_h} \end{bmatrix} $$

Definiamo ora la funzione di costo come la media delle Loss calcolate sulle $m$ osservazioni:

$$ J = \frac{1}{m} \sum_{i=1}^{m}L^{(i)} $$

Il gradiente della funzione di costo rispetto ai pesi dell’output layer è una matrice di dimensione $(n_o\times m)(m\times n_h) = (n_o\times n_h)$:

$$ \begin{align} \pdv{J}{\mathbf W^o} &= \frac{1}{m} \begin{bmatrix} d^{o(1)}_1 & d^{o(2)}_1 & \cdots & d^{o(m)}_1\\[6pt] d^{o(1)}_2 & d^{o(2)}_2 & \cdots & d^{o(m)}_2\\[6pt] \vdots & \vdots & \ddots & \vdots\\[6pt] d^{o(1)}_{n_o} & d^{o(2)}_{n_o} & \cdots & d^{o(m)}_{n_o} \end{bmatrix} \begin{bmatrix} a^{h(1)}_1 & a^{h(1)}_2 & \dots & a^{h(1)}_{n_h}\\[6pt] a^{h(2)}_1 & a^{h(2)}_2 & \dots & a^{h(2)}_{n_h}\\[6pt] \vdots & \vdots & \ddots & \vdots\\[6pt] a^{h(m)}_1 & a^{h(m)}_2 & \dots & a^{h(m)}_{n_h} \end{bmatrix} = \frac{1}{m} \left(\mathbf D^o\right)^\intercal \mathbf A^h \end{align} $$

La moltiplicazione matriciale esegue automaticamente la somma dei contributi prodotti dalle diverse osservazioni. Per un generico peso $w^o_{t,s}$, infatti:

$$ \pdv{J}{w^o_{t,s}} = \frac{1}{m} \sum_{i=1}^{m} d^{o(i)}_t a^{h(i)}_s $$

Ad esempio:

$$ \begin{align} \pdv{J}{w^o_{1,1}} = \frac{1}{m} \biggl[ & d^{o(1)}_1a^{h(1)}_1 + d^{o(2)}_1a^{h(2)}_1 + \cdots + d^{o(m)}_1a^{h(m)}_1 \biggr] \end{align} $$

Questo accade perché ciascuna osservazione contribuisce alla funzione di costo complessiva. Il gradiente associato a un determinato peso è quindi ottenuto aggregando i contributi prodotti da tutte le osservazioni del batch. Se la funzione di costo fosse invece definita come somma delle Loss,

$$ J=\sum_{i=1}^{m}L^{(i)}, $$

le stesse equazioni rimarrebbero valide, ma senza il fattore $1/m$.

Calcolo del Gradiente Rispetto ai Bias

Con lo stesso procedimento utilizzato per i pesi, calcoliamo il gradiente della funzione di costo rispetto ai bias dell’output layer:

$$ \begin{align} \pdv{L}{b^o_1} & = \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1} \cdot \pdv{z^o_1}{b^o_1}\\[6pt] \pdv{L}{b^o_2} & = \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2} \cdot \pdv{z^o_2}{b^o_2}\\[6pt] \end{align} $$

Come abbiamo già fatto per i pesi, possiamo utilizzare i termini $d^o_j$:

$$ \begin{align} d^o_1 & = \pdv{L}{a^o_1} \cdot \pdv{a^o_1}{z^o_1}\\[6pt] d^o_2 & = \pdv{L}{a^o_2} \cdot \pdv{a^o_2}{z^o_2}\\[6pt] \end{align} $$

ottenendo:

$$ \begin{align} \pdv{L}{b^o_1} & = d^o_1 \cdot \pdv{z^o_1}{b^o_1}\\[6pt] \pdv{L}{b^o_2} & = d^o_2 \cdot \pdv{z^o_2}{b^o_2}\\[6pt] \end{align} $$

Ricordando che:

$$ \begin{align} z^o_1 &= a^h_1 w^o_{1,1} + a^h_2 w^o_{1,2} + b^o_1\\[6pt] z^o_2 &= a^h_1 w^o_{2,1} + a^h_2 w^o_{2,2} + b^o_2 \end{align} $$

abbiamo:

$$ \begin{align} \pdv{z^o_1}{b^o_1} & = \pdv{}{b^o_1} \left( a^h_1 w^o_{1,1} + a^h_2 w^o_{1,2} + b^o_1 \right) = 1\\[6pt] \pdv{z^o_2}{b^o_2} & = \pdv{}{b^o_2} \left( a^h_1 w^o_{2,1} + a^h_2 w^o_{2,2} + b^o_2 \right) = 1 \end{align} $$

Di conseguenza:

$$ \begin{align} \pdv{L}{b^o_1} &= d^o_1 \cdot 1 = d^o_1\\[6pt] \pdv{L}{b^o_2} &= d^o_2 \cdot 1 = d^o_2 \end{align} $$

A differenza di quanto accade per i pesi, la derivata di $z^o_j$ rispetto al proprio bias è quindi sempre uguale a $1$. Per una singola osservazione, il gradiente rispetto al bias di un nodo di output coincide direttamente con il corrispondente termine $d^o_j$.

Generalizzazione

Generalizzando, per una singola osservazione di input, $\mathbf{x}^{(i)}$, e per un output layer formato da $n_o$ nodi, il gradiente della funzione di costo $L$ rispetto al vettore dei bias $\mathbf{b}^o$ può essere espresso mediante un vettore di dimensione $n_o \times 1$:

$$ \begin{align} \pdv{L}{\mathbf{b}^o} & = \begin{bmatrix} d^o_1\\[6pt] d^o_2\\[6pt] \vdots\\[6pt] d^o_{n_o}\\[6pt] \end{bmatrix} = \mathbf{d}^o \end{align} $$

Per osservazioni multiple, $\mathbf{X}$, consideriamo nuovamente la funzione di costo $J$, definita come la media delle Loss calcolate sulle $m$ osservazioni:

$$ \begin{align} J &= \frac{1}{m} \sum_{i=1}^{m} L^{(i)} \end{align} $$

Il gradiente della funzione di costo rispetto al vettore dei bias $\mathbf{b}^o$ viene quindi ottenuto mediando i contributi prodotti dalle diverse osservazioni.

La matrice $\mathbf{D}^o$ ha dimensione $m\times n_o$; la sua trasposta ha quindi dimensione $n_o\times m$. Moltiplicandola per un vettore di $m$ elementi tutti uguali a $1$, otteniamo un vettore di dimensione $n_o\times1$:

$$ \begin{align} \pdv{J}{\mathbf{b}^o} &= \frac{1}{m} \begin{bmatrix} d^{o(1)}_1 & d^{o(2)}_1 & \cdots & d^{o(m)}_1\\[6pt] d^{o(1)}_2 & d^{o(2)}_2 & \cdots & d^{o(m)}_2\\[6pt] \vdots & \vdots & \ddots & \vdots\\[6pt] d^{o(1)}_{n_o} & d^{o(2)}_{n_o} & \cdots & d^{o(m)}_{n_o} \end{bmatrix} \cdot \begin{bmatrix} 1\\[6pt] 1\\[6pt] \vdots\\[6pt] 1 \end{bmatrix} = \frac{1}{m} \left(\mathbf{D}^o\right)^\intercal \cdot \mathbf{1} \end{align} $$

La moltiplicazione per $\mathbf{1}$ somma, per ciascun nodo dell’output layer, i contributi prodotti dalle $m$ osservazioni; il fattore $1/m$ calcola quindi la loro media. Ad esempio, per il primo bias:

$$ \begin{align} \pdv{J}{b^o_1} &= \frac{1}{m} \left( d^{o(1)}_1 + d^{o(2)}_1 + \cdots + d^{o(m)}_1 \right) \end{align} $$

Generalizzando, per un generico bias $b^o_j$:

$$ \begin{align} \pdv{J}{b^o_j} &= \frac{1}{m} \sum_{i=1}^{m} d^{o(i)}_j \end{align} $$

Questo accade perché lo stesso bias $b^o_j$ viene utilizzato nel calcolo del $j$-esimo nodo per tutte le osservazioni del dataset. Il suo gradiente rispetto alla funzione di costo $J$ è quindi ottenuto mediando i contributi provenienti da ciascuna osservazione.

Caricamento
  • Rendering delle formule LaTeX...