Regressione lineare multipla: forma matriciale ed equazioni normali
Nell'articolo sulla regressione lineare abbiamo stimato il prezzo di un appartamento a partire dalla sola superficie. La retta funzionava bene, ma sbagliava comunque di qualche migliaio di euro: due appartamenti di metratura simile possono costare diversamente per tanti motivi.
Uno di questi è l'età dell'immobile. In questo articolo la aggiungiamo al modello e vediamo come cambiano i conti quando le variabili sono più di una: si passa dalle formule con le sommatorie alla forma matriciale, che vale per qualunque numero di variabili.
I dati
Agli stessi dieci appartamenti aggiungiamo gli anni dalla costruzione:
| Superficie (m²) | Età (anni) | Prezzo (k€) |
|---|---|---|
| 40 | 20 | 105 |
| 50 | 45 | 120 |
| 55 | 25 | 140 |
| 65 | 40 | 150 |
| 70 | 10 | 175 |
| 80 | 30 | 185 |
| 90 | 15 | 210 |
| 100 | 35 | 225 |
| 110 | 20 | 255 |
| 120 | 40 | 265 |
Guardando i residui della retta del primo articolo, l'appartamento da 70 m² costava 8,5 k€ più del previsto e quello da 65 m² 6,2 k€ meno: il primo ha 10 anni, il secondo 40. L'età sembra spiegare una parte di quegli errori.
La notazione
Con più variabili, e non bastano più. Si usa una notazione generale:
- indica l'osservazione: l'i-esimo appartamento, da 1 a ;
- sono i regressori (o feature): le variabili di input, qui superficie ed età;
- sono i parametri da stimare. è l'intercetta, quella che prima chiamavamo ;
- è l'errore: la parte di che il modello non riesce a spiegare.
Il caso del primo articolo è quello con un solo regressore: , .
C'è un trucco che semplifica tutto: si aggiunge un regressore finto , sempre uguale a uno. Così anche l'intercetta diventa un parametro "moltiplicato per qualcosa", e l'intero modello si scrive come un prodotto scalare tra due vettori:
è il numero di parametri, intercetta compresa. Nel nostro esempio : intercetta, superficie, età.
Con un regressore il modello è una retta, con due è un piano nello spazio tridimensionale (superficie, età, prezzo). Oltre è un iperpiano, che non si può disegnare ma si calcola esattamente allo stesso modo.
Tutto in una matrice
Scrivere un'equazione per ogni appartamento diventa scomodo. Si impilano allora le osservazioni una sotto l'altra:
Ogni riga di è un appartamento, ogni colonna un regressore. La prima colonna è tutta di uni: è il di prima. Le dieci equazioni diventano una sola:
dove è il vettore dei dieci errori.
La funzione di costo
È lo stesso errore quadratico medio del primo articolo, riscritto con i nuovi simboli. La previsione per l'appartamento prima era , ora è ; l'MSE prende il nome di funzione di costo e si indica con :
La stessa formula, con i vettori
La sommatoria si può scrivere in modo più compatto usando le matrici della sezione precedente:
- è il vettore delle previsioni: moltiplicare la matrice per calcola per ogni riga, cioè per ogni appartamento;
- è quindi il vettore dei residui, uno per appartamento;
- , la norma al quadrato di un vettore, è la somma dei quadrati dei suoi elementi: .
Mettendo insieme le tre cose, la somma dei residui al quadrato diventa :
Esempio
Scegliamo dei parametri e calcoliamo a mano, nei due modi. Prendiamo:
| Parametro | Valore | Significato |
|---|---|---|
| 30 k€ | prezzo di partenza, fisso | |
| 2 k€/m² | quanto vale ogni metro quadro | |
| 0 k€/anno | l'età non conta |
Con l'età sparisce dal modello, e resta la retta : è la "retta B" del primo articolo, che lì aveva MSE 35 k€².
1. La previsione per un appartamento. Si moltiplica la sua riga di per , termine a termine, e si somma. Per il primo appartamento (40 m², 20 anni) la riga è :
Il prezzo vero è 105 k€, quindi il residuo è k€: il modello sovrastima di 5.
2. Lo stesso per tutte le righe. Ripetendo il conto per ogni appartamento:
| # | m² | Anni | Previsto (k€) | Vero (k€) | Residuo (k€) | Residuo² (k€²) |
|---|---|---|---|---|---|---|
| 1 | 40 | 20 | 110 | 105 | −5 | 25 |
| 2 | 50 | 45 | 130 | 120 | −10 | 100 |
| 3 | 55 | 25 | 140 | 140 | 0 | 0 |
| 4 | 65 | 40 | 160 | 150 | −10 | 100 |
| 5 | 70 | 10 | 170 | 175 | +5 | 25 |
| 6 | 80 | 30 | 190 | 185 | −5 | 25 |
| 7 | 90 | 15 | 210 | 210 | 0 | 0 |
| 8 | 100 | 35 | 230 | 225 | −5 | 25 |
| 9 | 110 | 20 | 250 | 255 | +5 | 25 |
| 10 | 120 | 40 | 270 | 265 | −5 | 25 |
| Somma | 350 |
3. Con la formula sommatoria si fa esattamente questo: si sommano i residui al quadrato dell'ultima colonna (350 k€²) e si divide per , cioè k€².
4. Con la formula vettoriale si fa lo stesso conto, ma guardando le colonne della tabella come vettori:
- la colonna "Previsto" è il vettore ;
- la colonna "Vero" è il vettore ;
- la colonna "Residuo" è la loro differenza, :
La norma al quadrato è la somma dei quadrati di questi dieci numeri, cioè di nuovo 350 k€², e k€².
I due modi danno lo stesso risultato perché sono lo stesso calcolo: la sommatoria lo scrive riga per riga, la forma vettoriale tutto in una volta. La seconda è più comoda per fare i conti con le matrici, ed è quella che useremo da qui in avanti.
Il θ migliore
La stima è il vettore che rende più piccolo possibile, e si scrive così:
Attenzione alla differenza tra le due parole:
- è il valore più basso che l'errore può raggiungere. Nel nostro esempio, 4,28 k€²;
- è il che lo raggiunge, cioè i parametri da usare: , come vedremo tra poco. "arg" sta per argomento, il valore da mettere dentro la funzione.
A noi interessa il secondo: non quanto vale l'errore minimo, ma quale modello lo produce.
Perché il minimo è unico
Nel primo articolo, tenendo fermo e muovendo solo , l'MSE disegnava una curva a U. Qui succede lo stesso con qualunque parametro: ogni residuo è una funzione lineare di , il suo quadrato è una parabola rivolta verso l'alto, e una somma di parabole rivolte verso l'alto è ancora una parabola rivolta verso l'alto.
Se è una U in ogni direzione, nel suo insieme è una scodella. Una scodella ha un solo punto più basso: non ci sono "buche" secondarie in cui un algoritmo potrebbe fermarsi credendo di aver trovato il minimo. In matematica questa proprietà si chiama convessità.
C'è un'eccezione. Se due colonne di contengono la stessa informazione, la scodella ha il fondo piatto, come una grondaia, e i minimi sono infiniti. È il caso dei regressori ridondanti, che vedremo più avanti.
Le equazioni normali
Nel punto più basso della scodella, la funzione di costo non diminuisce più in nessuna direzione: il suo gradiente è nullo. Possiamo sfruttare questa proprietà per trovare direttamente i parametri che minimizzano l'errore, senza procedere per tentativi come nella discesa del gradiente.
Partiamo dalla funzione di costo della regressione lineare multipla:
Sviluppando il quadrato, otteniamo:
Formula, passo per passo
1. Dalla norma al prodotto matriciale. La norma al quadrato di un vettore è la somma dei quadrati dei suoi elementi. In notazione matriciale, questa operazione si scrive moltiplicando il vettore trasposto per il vettore stesso:
Per esempio:
Applicando la stessa regola al vettore degli errori , otteniamo:
2. Calcoliamo la trasposta. La trasposta di una differenza è la differenza delle trasposte. Inoltre, la trasposta di un prodotto inverte l'ordine dei fattori:
Di conseguenza:
3. Sviluppiamo il prodotto. Ora moltiplichiamo ogni termine della prima parentesi per ogni termine della seconda, proprio come nello sviluppo di . Dobbiamo solo rispettare l'ordine dei fattori, perché le matrici non si possono scambiare liberamente:
4. Perché i due termini centrali sono uguali? I termini
sono entrambi scalari, cioè singoli numeri. Infatti, le dimensioni dei fattori nel primo prodotto sono:
Il secondo prodotto è il trasposto del primo. Lo vediamo applicando la regola della trasposta di un prodotto:
Ma il trasposto di uno scalare è lo scalare stesso. I due termini sono quindi uguali e possiamo sommarli:
Otteniamo così la formula iniziale:
Deriviamo la funzione di costo
Abbiamo sviluppato la funzione di costo in modo da separare i termini costanti da quelli che dipendono dai parametri :
Ora vogliamo trovare i valori di che rendono minima. Per farlo, calcoliamo il gradiente, cioè il vettore delle derivate parziali rispetto a tutti i parametri del modello.
1. Due regole di derivazione matriciale
Ci servono due regole, che generalizzano quelle delle funzioni a una variabile:
Nella prima formula, è un vettore di costanti: la derivata di una funzione lineare restituisce i suoi coefficienti. È l'equivalente di:
La seconda formula riguarda invece una funzione quadratica. È l'equivalente della regola:
La differenza è che, nel caso matriciale, la derivata contiene , perché il vettore compare sia a sinistra sia a destra della matrice.
Se è simmetrica, cioè , la formula si semplifica:
2. Applichiamo le regole alla funzione di costo
Riprendiamo i tre termini di e deriviamoli uno alla volta rispetto a .
Primo termine:
Questo termine non dipende da : contiene soltanto i dati osservati , che consideriamo costanti durante l'addestramento. La sua derivata è quindi zero.
Secondo termine:
Applichiamo la prima regola, identificando e :
Il fattore rimane davanti alla derivata:
Terzo termine:
Applichiamo la seconda regola, identificando e :
La matrice è simmetrica, perché:
I due termini nella parentesi sono quindi uguali. Possiamo sommarli:
3. Mettiamo insieme le derivate
La derivata di una somma è la somma delle derivate. Inoltre, il fattore costante rimane davanti:
Abbiamo così ottenuto il gradiente della funzione di costo.
4. Poniamo il gradiente uguale a zero
Nel punto di minimo, il gradiente è nullo. Poniamo quindi:
Moltiplichiamo entrambi i membri per , eliminando i fattori costanti:
Portiamo il primo termine a destra:
Queste sono le equazioni normali: abbiamo trasformato il problema di minimizzazione della funzione di costo in un sistema lineare di equazioni.
La soluzione in forma chiusa
Se è invertibile, possiamo moltiplicare entrambi i membri delle equazioni normali per :
Poiché , dove è la matrice identità, rimane:
Questa è la soluzione dei minimi quadrati in forma chiusa: una formula che permette di calcolare direttamente i parametri ottimali, senza aggiornamenti iterativi.
Se non è invertibile, la formula con l'inversa non è applicabile direttamente. Il problema può comunque avere soluzioni, eventualmente non uniche.
Con un solo regressore e un'intercetta, questa formula restituisce gli stessi parametri e della regressione lineare semplice. Cambia la notazione, ma il principio è identico.
Esempio
Con i nostri dieci appartamenti le tre colonne di sono gli uni dell'intercetta, le superfici e le età .
1. Le due matrici del sistema. Nel prodotto ogni casella è la somma, su tutti gli appartamenti, del prodotto tra due colonne di :
Per esempio, la casella al centro è la somma delle superfici al quadrato: . Con tutti i numeri:
Allo stesso modo moltiplica ogni colonna di per i prezzi, e somma:
ha una riga e una colonna per ogni parametro.
2. Il sistema per esteso. Le equazioni normali , scritte riga per riga, sono tre equazioni nelle tre incognite , , :
Si risolvono come un qualsiasi sistema lineare, a mano per sostituzione oppure con il computer.
3. La soluzione.
| Parametro | Significato | Valore |
|---|---|---|
| intercetta | 32,59 k€ | |
| per ogni m² in più | +2,069 k€/m² | |
| per ogni anno in più | −0,392 k€/anno |
Controprova sulla prima equazione: . Torna.
Ogni metro quadro vale circa 2.070 €, come prima. Ogni anno di età toglie circa 390 € al prezzo. L'errore quadratico medio scende da 23,6 a 4,3 k€² e l'R² sale da 0,991 a 0,998.
4. Previsione. Per un appartamento da 85 m² con 20 anni ora possiamo stimare il prezzo:
Ora dipendente anche dall'età:
| Età | Prezzo stimato |
|---|---|
| nuovo (0 anni) | 208,5 k€ |
| 20 anni | 200,6 k€ |
| 40 anni | 192,8 k€ |
In Python, con NumPy, l'intero calcolo è una riga:
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])
# la colonna di uni è il regressore finto per l'intercetta
X = np.column_stack([np.ones(len(prezzo)), superficie, eta])
theta = np.linalg.solve(X.T @ X, X.T @ prezzo)
print(theta.round(3)) # [32.595 2.069 -0.392]
print(round(np.array([1, 85, 20]) @ theta, 1)) # 200.6Nota che il codice non calcola l'inversa: np.linalg.solve risolve direttamente il sistema . Il risultato è lo stesso, ma è più veloce e più preciso numericamente. La formula con serve a capire il ragionamento.
Quando XᵀX non è invertibile
La formula funziona solo se si può invertire. Ci sono due casi tipici in cui non succede.
Regressori ridondanti
Supponiamo di aggiungere al dataset anche la superficie in piedi quadrati: è la stessa informazione della superficie in metri quadrati. La quarta colonna di è un multiplo esatto della seconda, quindi le colonne sono linearmente dipendenti. Le soluzioni sono infinite: un m² in più può essere "pagato" dal parametro dei metri, da quello dei piedi o da un mix dei due, e la previsione non cambia.
è singolare, o meglio, a causa degli arrotondamenti, quasi singolare. Ed è qui che sta il pericolo: np.linalg.inv spesso non dà errore, ma restituisce numeri insensati. Sul mio computer:
| Metodo | (k€) | (k€/m²) | (k€/anno) | (k€/ft²) | MSE (k€²) |
|---|---|---|---|---|---|
inv(XᵀX) |
272,8 | −1,184 | −0,392 | 0,130 | 11.301 |
pinv(X) |
32,6 | 0,0177 | −0,392 | 0,191 | 4,28 |
Con l'inversa "forzata" la funzione di costo esplode da 4,3 a oltre 11.000 k€². I valori esatti dipendono dalla libreria e dal processore, il che è un altro sintomo del problema.
Esiste anche una versione più subdola del problema: regressori non identici ma molto correlati, come la superficie e il numero di locali. è invertibile, ma i parametri diventano instabili e difficili da interpretare: il modello non sa a chi dei due attribuire l'effetto.
Troppi pochi dati
Se le osservazioni sono meno dei parametri, o tante quante (), i dati non bastano a determinare il modello. Con soli 3 appartamenti e 3 parametri il piano passa esattamente per tutti e tre i punti, con errore zero:
| Dati usati | (k€) | (k€/m²) | (k€/anno) | MSE sui dati usati (k€²) |
|---|---|---|---|---|
| 3 appartamenti | 14,2 | 2,462 | −0,385 | 0 |
| tutti e 10 | 32,6 | 2,069 | −0,392 | 4,28 |
Errore zero non vuol dire modello perfetto: vuol dire che il modello ha memorizzato i dati invece di imparare la relazione. Con 3 appartamenti il valore di un m² sale a 2.460 €, un numero che non regge su nessun altro immobile. È il primo assaggio dell'overfitting.
Il limite delle equazioni normali
è una matrice , e risolvere il sistema costa all'incirca operazioni. Con 3 parametri è istantaneo, con qualche migliaio ancora fattibile. Con milioni di feature, come nei modelli per testo o immagini, diventa impraticabile, anche solo per la memoria necessaria a contenere la matrice.
In quei casi si rinuncia alla soluzione esatta e si usa un metodo iterativo: la discesa del gradiente, già vista nel primo articolo. Con più variabili però nasconde una trappola, e il prossimo articolo è dedicato a quella: discesa del gradiente e normalizzazione dei dati.
Riepilogo
- Con più regressori il modello si scrive , con un regressore finto per l'intercetta.
- Impilando le osservazioni si ottiene : ogni riga di è un'osservazione, ogni colonna un regressore.
- La funzione di costo è convessa: ha un unico minimo.
- Annullando il gradiente si ottengono le equazioni normali , da cui .
- non è invertibile con regressori ridondanti (si toglie la colonna, o si usa la pseudo-inversa) o con troppi pochi dati (): il modello memorizza invece di imparare.
- Con moltissimi parametri le equazioni normali diventano troppo costose e si passa alla discesa del gradiente.