<marco />
← Tutti gli articoli
16 min di lettura

Discesa del gradiente: perché normalizzare i dati

aiprogrammazione

Nel primo articolo sulla regressione lineare la discesa del gradiente aveva un difetto evidente: la pendenza mm si sistemava in pochi passi, l'intercetta qq impiegava centinaia di migliaia di iterazioni. Avevamo accennato alla soluzione: normalizzare i dati.

Qui riprendiamo il modello con superficie ed età dell'articolo sulla forma matriciale, scriviamo la discesa del gradiente per un numero qualunque di parametri e misuriamo quanto cambia normalizzando: da quasi 300.000 iterazioni a 31.

L'idea, con un solo parametro

La funzione di costo J(θ)J(\theta ) misura quanto sbaglia il modello. La discesa del gradiente parte da un valore iniziale θ^(0)\hat{\theta}(0) e lo corregge a ogni passo:

θ^(k+1)=θ^(k)−αdJdθ∣θ=θ^(k)\hat\theta(k+1) = \hat\theta(k) - \alpha \left. \frac{dJ}{d\theta} \right|_{\theta = \hat\theta(k)}

α>0\alpha > 0 è il learning rate, la lunghezza del passo:

  • se la derivata è positiva, JJ cresce andando verso destra: il minimo è a sinistra, e infatti θ^\hat{\theta} diminuisce;
  • se la derivata è negativa, il minimo è a destra, e θ^\hat{\theta} aumenta.

In entrambi i casi la nuova stima è più vicina al valore ottimo θ∗\theta^*. Vicino al minimo la derivata tende a zero, quindi anche i passi si accorciano.

Con più parametri: il gradiente

Con un vettore di parametri θ\theta la derivata diventa il gradiente ∇J(θ)\nabla J(\theta ): un vettore con una derivata per ogni parametro, che punta nella direzione in cui JJ sale più ripidamente. Si va nella direzione opposta:

θ^(k+1)=θ^(k)−α ∇J(θ^(k))\hat\theta(k+1) = \hat\theta(k) - \alpha\, \nabla J\bigl(\hat\theta(k)\bigr)

Per la regressione lineare il gradiente si calcola a mano. Con J(θ)=1N∑(y(i)−φ(i)⊤θ)2J(\theta ) = \tfrac{1}{N} \sum (y(i) - \varphi (i)^{\top}\theta )^{2}, derivando rispetto al parametro θj\theta _j:

∂J∂θj=2N∑i=1N(y(i)−φ(i)⊤θ)(−φj(i))\frac{\partial J}{\partial \theta_j} = \frac{2}{N} \sum_{i=1}^{N} \bigl( y(i) - \varphi(i)^\top \theta \bigr) \bigl( -\varphi_j(i) \bigr)

L'errore di ogni osservazione, moltiplicato per il valore del regressore corrispondente. Per l'intercetta φ0=1\varphi _0 = 1, quindi resta solo la somma degli errori. Mettendo insieme tutte le componenti, con la matrice XX dei regressori:

∇J(θ)=−2N X⊤(Y−Xθ)\nabla J(\theta) = -\frac{2}{N}\, X^\top (Y - X\theta)

Y−XθY - X\theta è il vettore degli errori; moltiplicarlo per X⊤X^{\top} calcola tutte le somme pesate.

Attenzione all'aggiornamento simultaneo. Tutti i parametri vanno aggiornati con il gradiente calcolato sullo stesso θ^(k)\hat{\theta}(k). Se aggiorni θ0\theta _0 e poi usi il nuovo θ0\theta _0 per calcolare la derivata di θ1\theta _1, stai facendo un altro algoritmo. La forma vettoriale evita il problema per costruzione.

Il problema: dati su scale diverse

Proviamo sui dati di superficie (40–120 m²) ed età (10–45 anni), partendo da θ=0\theta = 0:

Iterazione θ0\theta _0 (k€) θ1\theta _1 (k€/m²) θ2\theta _2 (k€/anno) MSE (k€²)
1 0,04 3,119 1,020 8.904
10 0,03 2,149 0,501 112,6
100 0,06 2,301 0,053 70,5
1.000 0,42 2,305 0,029 68,9
10.000 3,84 2,280 −0,016 55,9
100.000 23,25 2,138 −0,270 9,73
soluzione esatta 32,59 2,069 −0,392 4,28

Il learning rate è α=0,0001\alpha = 0{,}0001, e non si può alzare: già con 0,000150{,}00015 l'algoritmo diverge. Per arrivare entro l'1% dall'errore minimo servono 294.203 iterazioni.

La valle stretta

Con due parametri la funzione di costo si può immaginare come una mappa con le curve di livello: ogni ellisse unisce i punti con lo stesso valore di JJ, e il minimo sta al centro. Il gradiente è sempre perpendicolare alle curve di livello.

Dati non normalizzati 14 passi a zig-zag Dati normalizzati 6 passi, arrivato

Quando i regressori hanno scale molto diverse, la "scodella" di JJ diventa una valle lunga e stretta: ripidissima in una direzione, quasi piatta nell'altra.

  • Il learning rate deve essere abbastanza piccolo da non far schizzare fuori l'algoritmo nella direzione ripida. Se è troppo grande, ogni passo scavalca il fondo della valle e risale sulla parete opposta, sempre più in alto: è la divergenza che abbiamo visto con α=0,00015\alpha = 0{,}00015.
  • Con un passo così piccolo, nella direzione piatta si avanza a fatica. Il gradiente punta quasi tutto verso la parete, non verso il minimo, e il percorso rimbalza a zig-zag.

Nel nostro esempio la valle è circa 120.000 volte più ripida in una direzione che nell'altra.

La normalizzazione

La cura è portare tutti i regressori sulla stessa scala. Oggi la superficie va da 40 a 120 m², l'età da 10 a 45 anni, e la colonna dell'intercetta vale sempre 1: numeri di grandezza diversa, misurati in unità diverse.

Il metodo più comune è la standardizzazione (z-score). Sostituisce ogni valore con la risposta a una domanda: quanto è lontano dalla media, rispetto a quanto si allontanano di solito gli altri? Si fa in tre passi, separatamente per ogni regressore, intercetta esclusa. Seguiamoli sulla superficie.

1. La media: dov'è il centro.

μj=1N∑i=1Nφj(i)\mu_j = \frac{1}{N} \sum_{i=1}^{N} \varphi_j(i)

Per la superficie: (40+50+⋯+120)/10=780/10=78(40 + 50 + \dots + 120) / 10 = 780 / 10 = 78 m².

2. La deviazione standard: quanto si allontanano di solito i valori dal centro.

σj=1N∑i=1N(φj(i)−μj)2\sigma_j = \sqrt{ \frac{1}{N} \sum_{i=1}^{N} \bigl( \varphi_j(i) - \mu_j \bigr)^2 }

Si legge dall'interno verso l'esterno:

  • si calcola lo scarto di ogni valore dalla media: 40−78=−3840 - 78 = -38, 50−78=−2850 - 78 = -28, …, 120−78=42120 - 78 = 42;
  • si eleva ogni scarto al quadrato, così quelli negativi non annullano quelli positivi (lo stesso trucco dell'MSE): 1.444, 784, …, 1.7641.444,\ 784,\ \dots,\ 1.764;
  • si fa la media dei quadrati: 6.410/10=6416.410 / 10 = 641 m⁴. È la varianza;
  • si fa la radice quadrata, per tornare all'unità di partenza: 641≈25,32\sqrt{641} \approx 25{,}32 m².

Tipicamente, quindi, una superficie dista una ventina di metri quadri dai 78 m² medi: la deviazione standard è questa distanza "tipica", che pesa un po' di più gli scarti grandi.

3. La sostituzione: ogni valore diventa "quante deviazioni standard dalla media".

φj(i)  ←  φj(i)−μjσj\varphi_j(i) \;\leftarrow\; \frac{\varphi_j(i) - \mu_j}{\sigma_j}

Per l'appartamento da 40 m²: (40−78)/25,32≈−1,50(40 - 78) / 25{,}32 \approx -1{,}50. Vuol dire che è una volta e mezza la deviazione standard sotto la media: piccolo, rispetto agli altri. Un valore 0 indica un appartamento esattamente nella media, un valore positivo uno sopra la media.

Nella divisione m² su m² l'unità di misura sparisce: il risultato è un numero puro. Lo stesso succede con l'età (media 28 anni, deviazione standard 11,22 anni), e così tutti i regressori finiscono sulla stessa scala, qualunque fosse l'unità di partenza:

m² Superficie standardizzata Anni Età standardizzata
40 −1,50 20 −0,71
50 −1,11 45 +1,51
55 −0,91 25 −0,27
65 −0,51 40 +1,07
70 −0,32 10 −1,60
80 +0,08 30 +0,18
90 +0,47 15 −1,16
100 +0,87 35 +0,62
110 +1,26 20 −0,71
120 +1,66 40 +1,07

Dopo la trasformazione ogni colonna ha media 0 e deviazione standard 1: i valori stanno tutti all'incirca tra −2 e +2.

L'intercetta si lascia com'è. La sua colonna vale sempre 1, quindi ha deviazione standard zero, e dividere per zero non si può. Del resto è già un numero della grandezza giusta.

Il risultato sulla funzione di costo è quello che volevamo: la valle diventa quasi rotonda.

Risultato

Iterazione θ0\theta _0 (k€) θ1\theta _1 (k€) θ2\theta _2 (k€) MSE (k€²)
1 36,6 10,44 −0,38 23.197
5 123,0 35,11 −1,94 3.901
10 163,4 46,67 −3,26 423,8
20 180,9 51,75 −4,21 9,16
50 183,0 52,39 −4,40 4,28

Con α=0,1\alpha = 0{,}1 l'errore arriva entro l'1% dal minimo in 31 iterazioni invece di 294.203. E il learning rate ora ha margine: funziona fino a circa 0,950{,}95, mille volte più grande di prima.

I parametri sono diversi da prima perché sono espressi nelle nuove unità. I regressori normalizzati non hanno più unità di misura (contano "quante deviazioni standard sopra la media"), quindi tutti e tre i parametri sono in k€:

  • θ0=183\theta _0 = 183 k€ è il prezzo medio. Con i regressori centrati sulla media, l'intercetta è il prezzo dell'appartamento "medio", con superficie e età medie;
  • θ1=52,39\theta _1 = 52{,}39 k€ è l'effetto di una deviazione standard di superficie (25,32 m²);
  • θ2=−4,40\theta _2 = -4{,}40 k€ è l'effetto di una deviazione standard di età (11,22 anni).

Per tornare alle unità originali basta dividere per σ\sigma: 52,386/25,32=2,06952{,}386 / 25{,}32 = 2{,}069 k€ per m², −4,404/11,22=−0,392-4{,}404 / 11{,}22 = -0{,}392 k€ per anno. Sono esattamente i valori delle equazioni normali.

Codice
python
import numpy as np

superficie = np.array([40, 50, 55, 65, 70, 80, 90, 100, 110, 120])
eta = np.array([20, 45, 25, 40, 10, 30, 15, 35, 20, 40])
prezzo = np.array([105, 120, 140, 150, 175, 185, 210, 225, 255, 265])
N = len(prezzo)

# normalizzazione: media e deviazione standard di ogni regressore
regressori = np.column_stack([superficie, eta])
mu = regressori.mean(axis=0)
sigma = regressori.std(axis=0)
X = np.column_stack([np.ones(N), (regressori - mu) / sigma])

theta = np.zeros(3)
alpha = 0.1
for k in range(100):
    gradiente = -2 / N * X.T @ (prezzo - X @ theta)
    theta = theta - alpha * gradiente

print(theta.round(3))  # [183.     52.386  -4.404]

La regola d'oro: stessa media, stessa deviazione standard

Per fare una previsione su un nuovo appartamento, i suoi dati vanno normalizzati con la stessa μ\mu e la stessa σ\sigma calcolate sui dati di addestramento, non con quelle del nuovo dato:

python
# 85 m², 20 anni: normalizzati con mu e sigma dell'addestramento
z = (np.array([85, 20]) - mu) / sigma
print(round(theta[0] + z @ theta[1:], 1))  # 200.6

Il modello ha imparato che "+1" significa "25,32 m² sopra i 78 m²". Se normalizzassi il nuovo dato con un'altra media, "+1" significherebbe un'altra cosa e la previsione sarebbe sbagliata, senza nessun errore visibile. Per questo μ\mu e σ\sigma fanno parte del modello tanto quanto θ\theta: vanno salvate insieme.

Lo stesso vale se dividi i dati tra addestramento e test: media e deviazione standard si calcolano solo sui dati di addestramento.

Scegliere il learning rate

Anche con i dati normalizzati α\alpha va scelto. Qualche regola pratica:

  • prova valori su scala logaritmica: 0,0010{,}001, 0,010{,}01, 0,10{,}1, 11;
  • controlla che JJ scenda a ogni iterazione. Se sale anche una volta, α\alpha è troppo grande;
  • se JJ scende ma lentissimamente, α\alpha è troppo piccolo;
  • fermati quando JJ smette di migliorare in modo apprezzabile, non dopo un numero fisso di iterazioni.
α\alpha Iterazioni (entro l'1% dal minimo)
0,01 339
0,1 31
0,3 8
1,0 diverge

Equazioni normali o discesa del gradiente?

Per la regressione lineare esistono entrambe le strade, e arrivano allo stesso risultato.

Equazioni normali Discesa del gradiente
Risultato esatto, in un colpo approssimato, per passi
Parametri da scegliere nessuno learning rate, iterazioni
Normalizzazione non serve indispensabile
Costo con dd parametri circa d3d^{3} circa N⋅dN\cdot d per iterazione
Adatto a dd fino a qualche migliaio dd molto grande, modelli non lineari

Per un modello piccolo come il nostro le equazioni normali vincono. La discesa del gradiente diventa indispensabile quando dd è enorme o quando il modello non è lineare e una formula chiusa non esiste: è il caso delle reti neurali.

Gli esercizi 7 e 8 della raccolta di esercizi rifanno i conti.

  • La discesa del gradiente aggiorna i parametri con θ^(k+1)=θ^(k)−α⋅∇J(θ^(k))\hat{\theta}(k+1) = \hat{\theta}(k) - \alpha \cdot \nabla J(\hat{\theta}(k)), aggiornandoli tutti insieme.
  • Per la regressione lineare il gradiente è ∇J(θ)=−(2/N)⋅X⊤(Y−Xθ)\nabla J(\theta ) = -(2/N)\cdot X^{\top}(Y - X\theta ).
  • Con regressori su scale diverse la funzione di costo è una valle stretta: il learning rate deve essere minuscolo e la convergenza è lentissima.
  • La standardizzazione (sottrarre la media, dividere per la deviazione standard) rende la valle rotonda.
  • I nuovi dati si normalizzano con la stessa media e deviazione standard dell'addestramento.