Tecniche di ottimizzazione I

\gdef \sam #1 {\mathrm{softargmax}(#1)} \gdef \vect #1 {\boldsymbol{#1}} \gdef \matr #1 {\boldsymbol{#1}} \gdef \E {\mathbb{E}} \gdef \V {\mathbb{V}} \gdef \R {\mathbb{R}} \gdef \N {\mathbb{N}} \gdef \relu #1 {\texttt{ReLU}(#1)} \gdef \D {\,\mathrm{d}} \gdef \deriv #1 #2 {\frac{\D #1}{\D #2}} \gdef \pd #1 #2 {\frac{\partial #1}{\partial #2}} \gdef \set #1 {\left\lbrace #1 \right\rbrace} % My colours \gdef \aqua #1 {\textcolor{8dd3c7}{#1}} \gdef \yellow #1 {\textcolor{ffffb3}{#1}} \gdef \lavender #1 {\textcolor{bebada}{#1}} \gdef \red #1 {\textcolor{fb8072}{#1}} \gdef \blue #1 {\textcolor{80b1d3}{#1}} \gdef \orange #1 {\textcolor{fdb462}{#1}} \gdef \green #1 {\textcolor{b3de69}{#1}} \gdef \pink #1 {\textcolor{fccde5}{#1}} \gdef \vgrey #1 {\textcolor{d9d9d9}{#1}} \gdef \violet #1 {\textcolor{bc80bd}{#1}} \gdef \unka #1 {\textcolor{ccebc5}{#1}} \gdef \unkb #1 {\textcolor{ffed6f}{#1}} % Vectors \gdef \vx {\pink{\vect{x }}} \gdef \vy {\blue{\vect{y }}} \gdef \vb {\vect{b}} \gdef \vz {\orange{\vect{z }}} \gdef \vtheta {\vect{\theta }} \gdef \vh {\green{\vect{h }}} \gdef \vq {\aqua{\vect{q }}} \gdef \vk {\yellow{\vect{k }}} \gdef \vv {\green{\vect{v }}} \gdef \vytilde {\violet{\tilde{\vect{y}}}} \gdef \vyhat {\red{\hat{\vect{y}}}} \gdef \vycheck {\blue{\check{\vect{y}}}} \gdef \vzcheck {\blue{\check{\vect{z}}}} \gdef \vztilde {\green{\tilde{\vect{z}}}} \gdef \vmu {\green{\vect{\mu}}} \gdef \vu {\orange{\vect{u}}} % Matrices \gdef \mW {\matr{W}} \gdef \mA {\matr{A}} \gdef \mX {\pink{\matr{X}}} \gdef \mY {\blue{\matr{Y}}} \gdef \mQ {\aqua{\matr{Q }}} \gdef \mK {\yellow{\matr{K }}} \gdef \mV {\lavender{\matr{V }}} \gdef \mH {\green{\matr{H }}} % Coloured math \gdef \cx {\pink{x}} \gdef \ctheta {\orange{\theta}} \gdef \cz {\orange{z}} \gdef \Enc {\lavender{\text{Enc}}} \gdef \Dec {\aqua{\text{Dec}}}
🎙️ Aaron Defazio

Discesa del gradiente

Iniziamo il nostro studio delle tecniche di ottimizzazione con il più basilare e peggiore (motivazioni a seguire) metodo fra quelli che vedremo, la discesa del gradiente.

Problema:

minwf(w)\min_w f(w)

Soluzione iterativa:

wk+1=wkγkf(wk)w_{k+1} = w_k - \gamma_k \nabla f(w_k)

dove:

  • wk+1w_{k+1} è il valore aggiornato dopo la kk-esima iterazione,
  • wkw_k è il valore iniziale prima della kk-esima iterazione,
  • γk\gamma_k è la dimensione del passo,
  • f(wk)\nabla f(w_k) è il gradiente di ff.

L’assunzione che facciamo è che la funzione ff sia continua e differenziabile. Il nostro scopo è trovare il punto più basso (“valle”) della funzione da ottimizzare. In ogni caso, la direzione corrente di questa valle è sconosciuta. Possiamo solamente orientarci a livello locale, quindi la direzione del gradiente negativo è la miglior informazione che possediamo. Fare un piccolo passo in questa direzione può solo che avvicinarci al minimo. Una volta che abbiamo preso effettuato questo passo, possiamo nuovamente computare il nuovo gradiente e nuovamente muoverci di una breve distanza in quella direzione, fintantoché non raggiungiamo la valle. Quindi, essenzialmente, tutto ciò che fa la discesa del gradiente è seguire la direzione in cui il pendio è maggiore (gradiente negativo).

Il parametro γ\gamma nell’equazione di aggiornamento iterativo è chiamato dimensione del passo. Generalmente, non ne conosciamo il valore ottimale; dobbiamo dunque provare con diversi valori. La prassi è provare una serie di valori in scala logaritmica e usare il migliore fra questi. Ci sono differenti scenari che possono verificarsi. L’immagine sopra descrive questi scenari per una quadratica monodimensionale. Se il livello di apprendimento è troppo basso, allora facciamo una progressione lenta verso il minimo. In ogni caso, questo potrebbe impiegare più tempo di quanto a disposizione. È generalmente molto difficile (o impossibile) ottenere una dimensione del passo che ci porti direttamente al minimo. Ciò che vorremmo idealmente è avere una dimensione del passo un minimo più grande rispetto a quella ottimale. In pratica, ciò fornisce la convergenza più rapida. Comunque, se usiamo un livello di apprendimento troppo grande, le iterazioni si allontanano progressivamente dal minimo ed otteniamo una divergenza. In pratica, vorremmo usare un livello di apprendimento che sia un minimo meno di quello che porti alla divergenza.


Figure 1: dimensione del passo per una quadratica monodimensionale

Discesa stocastica del gradiente

Nella discesa stocastica del gradiente, sostituiamo il vettore del gradiente corrente con una stima stocastica di quest’ultimo. Specificamente, per una rete neurale, la stima stocastica è ottenuta dal gradiente della funzione di perdita per un singolo punto (istanza) dei dati.

Sia fif_i la perdita della rete per l’istanza ii-esima.

fi=l(xi,yi,w)f_i = l(x_i, y_i, w)

La funzione da minimizzare è ff, la perdita totale su tutte le istanze.

f=1ninfif = \frac{1}{n}\sum_i^n f_i

In SGD, aggiorniamo i pesi in base al gradiente di fif_i (anziché alla perdita totale ff).

wk+1=wkγkfi(wk)(i chosen uniformly at random)\begin{aligned} w_{k+1} &= w_k - \gamma_k \nabla f_i(w_k) & \quad\text{(i chosen uniformly at random)} \end{aligned}

Se ii è scelto casualmente, allora fif_i è uno stimatore di ff rumoroso ma non distorto, e può essere scritto matematicamente come:

E[fi(wk)]=f(wk)\mathbb{E}[\nabla f_i(w_k)] = \nabla f(w_k)

Come risultato di ciò, il passo kk-esimo di SGD è in media identico al passo kk-esimo della discesa del gradiente operata su tutte le istanze:

E[wk+1]=wkγkE[fi(wk)]=wkγkf(wk)\mathbb{E}[w_{k+1}] = w_k - \gamma_k \mathbb{E}[\nabla f_i(w_k)] = w_k - \gamma_k \nabla f(w_k)

Quindi, ogni aggiornamento di SGD è lo stesso, in media, di un aggiornamento calcolato su tutti i dati. Comunque, SGD non è solo un SGD rapido con rumore. Oltre a essere più rapido, SGD ci può anche fornire risultati migliori di una discesa del gradiente completa. Il rumore di SGD ci può anche aiutare ad evitare i minimi locali poco profondi e aiutarci a trovare un migliore (più profondo) punto di minimo. Questo fenomeno è chiamato annealing (“ricottura”).


Figure 2: *Annealing* con SGD

Riassumendo, i vantaggi della discesa stocastica del gradiente sono i seguenti:

  1. C’è molta informazione ridondante fra le varie istanze. SGD ne previene in buona parte la computazione.
  2. Per le prime iterazioni, il rumore è piccolo se comparato all’informazione trasmessa dal gradiente. Quindi un passo del SGD è virtualmente tanto buono quanto un passo del GD.
  3. Annealing – il rumore nell’aggiornamento del SGD può prevenire la convergenza verso minimi locali cattivi (poco profondi).
  4. La discesa stocastica del gradiente e drasticamente più economica da un punto di vista computazionale (in quanto il gradiente non dev’essere ricalcolato su tutte le istanze).

Mini-batching

La tecnica chiamata mini-batching prevede che si calcoli la funzione di perdita su più istanze selezionate anziché su una sola istanza. Questo riduce il rumore nel passo di aggiornamento.

wk+1=wkγk1BijBifj(wk)w_{k+1} = w_k - \gamma_k \frac{1}{|B_i|} \sum_{j \in B_i}\nabla f_j(w_k)

Spesso siamo in grado di fare un miglior uso del nostro hardware utilizzando mini batch invece di una singola istanza. Per esempio, le GPU sono sottoutilizzate quando usiamo singole istanze per l’addestramento. Le tecniche distribuite di addestramento di reti suddividono un grosso mini-batch fra più macchine di un cluster e ne aggregano i gradienti risultanti. Facebook ha recentemente addestrato una rete sul dataset ImageNet in un’ora usando l’addestramento distribuito.

È importante notare che la discesa del gradiente non dovrebbe mai essere utilizzata con batch grandi quanto il dataset. Nel caso in cui si voglia addestrare una rete in questo modo, si usi la tecnica di ottimizzazione nota come LBFGS. PyTorch e SciPy implementano tutte e due questa tecnica.

Momento

Al momento, abbiamo due quantità da aggiornare iterativamente, anziché una sola. Le formule di aggiornamento sono le seguenti:

pk+1=βk^pk+fi(wk)wk+1=wkγkpk+1\begin{aligned} p_{k+1} &= \hat{\beta_k}p_k + \nabla f_i(w_k) \\ w_{k+1} &= w_k - \gamma_kp_{k+1} \\ \end{aligned}

pp è chiamato momento del SGD. A ogni passo di aggiornamento, aggiungiamo al gradiente stocastico il vecchio valore del momento, dopo averlo “smorzato” di un fattore β\beta (il cui valore è compreso fra 0 e 1). pp può essere pensato come una media a valori mobili del gradiente. Infine muoviamo ww nella direzione del nuovo momento pp.

Forma alternativa: metodo stocastico della palla pesante

wk+1=wkγkfi(wk)+βk(wkwk1)0β<1\begin{aligned} w_{k+1} &= w_k - \gamma_k\nabla f_i(w_k) + \beta_k(w_k - w_{k-1}) & 0 \leq \beta < 1 \end{aligned}

Questa forma è matematicamente equivalente alla precedente. Qui, il passo successivo è una combinazione della direzione della direzione del passo precedente (wkwk1w_k - w_{k-1}) e il nuovo gradiente negativo.

Intuizione

Il momento del SGD è simile al concetto fisico del momento. Il processo di ottimizzazione può essere assimilato a quello del rotolamento di una palla pesante giù per una collina. Il momento mantiene la palla in movimento nella stessa direzione nella quale si sta muovendo. Il gradiente può essere pensato come una forza che spinge la palla in un’altra direzione.


Fig. 3: effetto del momento
Fonte: distill.pub

Anziché apportare grossi cambiamenti nella direzione di moto (come nella figura a sinistra), il momento ne causa di modesti. Il momento smorza le oscillazioni che sono molto comuni quando usiamo solo SGD.

Il parametro β\beta è chiamato “fattore di smorzamento”. β\beta dev’essere più grande di zero, perché se fosse uguale a zero, si starebbe facendo una semplice discesa del gradiente. Deve altresì essere più piccolo di 1, altrimenti il tutto esploderebbe. Valori piccoli di β\beta corrispondono a più repentini cambi di direzione. Per valori più grandi, è richiesto un maggior tempo per “curvare”.


Figure 4: effetto di β\beta sulla convergenza

Linee guida pratiche

Il momento dev’essere praticamente sempre utilizzato con la discesa stocastica del gradiente. Valori di β\beta pari a 0.9 o 0.99 quasi sempre danno buoni risultati.

Il parametro della lunghezza del passo, usualmente, dev’essere decrementato quando il parametro del momento aumenta per mantenere la convergenza. Se β\beta cambia da 0.9 a 0.99, il livello di apprendimento dev’essere decrementato di un fattore di 10.

Perché il momento funziona’?

Accelerazione

Di seguito sono presentate le regole di aggiornamento per il momento di Nesterov:

pk+1=βk^pk+fi(wk)wk+1=wkγk(fi(wk)+βk^pk+1)p_{k+1} = \hat{\beta_k}p_k + \nabla f_i(w_k) \\ w_{k+1} = w_k - \gamma_k(\nabla f_i(w_k) +\hat{\beta_k}p_{k+1})

Col momento di Nesterov, si può ottenere una convergenza accelerata se si scelgono attentamente le costanti. Ma ciò si applica solo a problemi convessi e non a reti neurali.

Molti sostengono che il momento classico sia anch’esso un metodo accelerato, ma, in realtà, è accelerato solo per quadratiche. Inoltre, l’accelerazione non funziona bene con SGD, in quanto quest’ultimo ha rumore e l’accelerazione non sinergizza bene col rumore. Di conseguenza nonostante vi sia un po’ di accelerazione anche nel SGD con momento, esso non è da solo una buona spiegazione delle buone performance della tecnica.

Lisciamento del rumore

Probabilmente, una motivazione più pratica del perché il momento funzioni è il lisciamento del rumore.

Il momento fa una media dei gradienti. È una media mobile dei gradienti che utilizziamo per ogni passo di aggiornamento.

Teoricamente, affinché SGD funzioni, dovremmo prendere una media su tutti i passi di aggiornamento:

wˉk=1Kk=1Kwk\bar w_k = \frac{1}{K} \sum_{k=1}^K w_k

Il bello del SGD con momento è che questa media non è più necessaria. Il momento dona un lisciamento al processo di ottimizzazione, il che rende ogni aggiornamento una buona approssimazione verso la soluzione. Con SGD si vuole fare una media di un certo numero di aggiornamenti e poi fare un passo in tale direzione.

Sia l’accelerazione che il lisciamento del rumore contribuiscono alle alte performance del momento.


Figure 5: SGD vs. momento

Con SGD, facciamo in prima battuta buoni progressi verso la soluzione, ma quando raggiungiamo una conca (il fondo della valle) continuiamo a rimbalzare attorno al fondale. Se aggiustiamo il livello di apprendimento, continueremo a rimbalzare, anche se un po’ più lentamente. Col momento, andiamo ad “addolcire” i passi, così che non c’è più questo effetto di “rimbalzi”.


📝 Vaibhav Gupta, Himani Shah, Gowri Addepalli, Lakshmi Addepalli
🇮🇹 Marco Zullich
24 Feb 2020