Regressione Multipla
Scopri come funziona la regressione lineare multipla, interpreta i coefficienti, gestisci la multicollinearità e costruisci un modello completo in Python.
La regressione lineare multipla estende la regressione lineare semplice utilizzando due o più variabili indipendenti per prevedere un target continuo. Invece di tracciare una retta in due dimensioni, il modello adatta un iperpiano attraverso tante dimensioni quante sono le feature. Comprendere come i coefficienti interagiscono — e quando possono trarre in inganno — è la competenza fondamentale che questa pagina insegna.
Questa pagina tratta:
- L'equazione della regressione multipla e il significato di ciascun coefficiente
- Come costruire una pipeline completa con scikit-learn: caricamento, preprocessing, addestramento, valutazione
- Perché il feature scaling è importante e come eseguirlo correttamente
- Come interpretare e confrontare coefficienti scalati e non scalati
- La diagnosi della multicollinearità — l'insidia più comune nella regressione multipla
- L'analisi dei residui per verificare le assunzioni del modello
- Quando scegliere la regressione multipla e cosa provare quando non è sufficiente
L'Equazione della Regressione Multipla
La regressione lineare multipla modella il target y come combinazione lineare di n feature di input:
y = β₀ + β₁x₁ + β₂x₂ + ... + βₙxₙ + εβ₀— l'intercetta: il valore previsto diyquando ogni feature è uguale a zeroβ₁ … βₙ— i coefficienti: di quanto cambiayper un aumento di un'unità in ciascunaxᵢ, mantenendo costanti tutte le altre featureε— il termine di errore: la parte diyche il modello non riesce a spiegare
L'algoritmo trova i coefficienti minimizzando la somma dei residui al quadrato (ordinary least squares):
SSR = Σ(yᵢ - ŷᵢ)²Questo ha una soluzione analitica esatta in forma chiusa, quindi LinearRegression di scikit-learn non necessita di gradient descent iterativo — l'addestramento è quasi istantaneo anche su dataset con centinaia di migliaia di righe.
Differenza dalla Regressione Lineare Semplice
La regressione lineare semplice utilizza una sola feature. La regressione multipla aggiunge più feature in modo che ciascun coefficiente catturi l'effetto parziale di quella feature — il suo impatto sul target mentre le feature rimanenti sono tenute costanti. Questo è più potente ma introduce nuovi rischi, in particolare la multicollinearità (vedi Diagnosi della Multicollinearità).
Il Dataset
Gli esempi di seguito utilizzano il dataset California Housing integrato in scikit-learn. Registra statistiche abitative a livello di blocco censimento per la California nel 1990 e contiene 20.640 campioni distribuiti su 8 feature.
import pandas as pd
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
df = pd.DataFrame(housing.data, columns=housing.feature_names)
df['MedHouseVal'] = housing.target # median house value in $100,000s
print(df.shape) # (20640, 9)
print(df.head())Le 8 feature di input sono:
| Feature | Descrizione |
|---|---|
MedInc | Reddito mediano del blocco (in decine di migliaia di dollari) |
HouseAge | Età mediana delle abitazioni nel blocco |
AveRooms | Numero medio di stanze per nucleo familiare |
AveBedrms | Numero medio di camere da letto per nucleo familiare |
Population | Popolazione del blocco |
AveOccup | Occupazione media del nucleo familiare |
Latitude | Latitudine del blocco |
Longitude | Longitudine del blocco |
Il target MedHouseVal è il valore mediano delle abitazioni in unità di $100.000, quindi un valore di 2.0 rappresenta $200.000.
Costruire il Modello Passo per Passo
Passo 1 — Suddividere i Dati
Suddividi sempre i dati prima di qualsiasi preprocessing. Adattare uno scaler sull'intero dataset farebbe trapelare le statistiche del test set nell'addestramento, fornendo una valutazione eccessivamente ottimistica. Vedi Train/Test Split per una spiegazione completa.
from sklearn.model_selection import train_test_split
X = df[housing.feature_names]
y = df['MedHouseVal']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"Training samples: {len(X_train)}") # 16512
print(f"Test samples: {len(X_test)}") # 4128Passo 2 — Scalare le Feature
I coefficienti della regressione multipla riflettono le unità di ciascuna feature. MedInc è misurata in decine di migliaia di dollari; Population è un conteggio grezzo che può raggiungere 35.000. Senza scaling, il coefficiente di Population sarà piccolo non perché la popolazione sia irrilevante, ma perché la sua unità è piccola.
StandardScaler trasforma ogni feature in modo che abbia media zero e deviazione standard unitaria, rendendo le magnitudini dei coefficienti direttamente confrontabili. Vedi Feature Scaling per i dettagli sugli scaler disponibili.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # fit ONLY on training data
X_test_scaled = scaler.transform(X_test) # apply the same transformationLa regola fondamentale: chiama fit_transform sui dati di addestramento e transform (senza fit) sui dati di test. Eseguire il fit sui dati di test contaminerebbe la valutazione.
Passo 3 — Addestrare il Modello
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train_scaled, y_train)LinearRegression.fit() risolve il problema OLS analiticamente. Non ci sono iperparametri da ottimizzare nella regressione multipla standard — quello che si sceglie è quali feature includere.
Passo 4 — Valutare il Modello
import numpy as np
from sklearn.metrics import mean_squared_error, r2_score
y_pred = model.predict(X_test_scaled)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)
print(f"Mean Squared Error: {mse:.4f}")
print(f"Root Mean Squared Error: {rmse:.4f} (±${rmse * 100_000:,.0f})")
print(f"R-squared: {r2:.4f}")Output atteso:
Mean Squared Error: 0.5559
Root Mean Squared Error: 0.7456 (±$74,558)
R-squared: 0.5758Cosa significano le metriche:
- MSE (Mean Squared Error) — media delle differenze al quadrato tra previsioni e valori reali. Il quadrato penalizza gli errori grandi più di quelli piccoli. Le unità sono il quadrato del target ($100.000²), quindi è più difficile da interpretare direttamente.
- RMSE (Root Mean Squared Error) — la radice quadrata dell'MSE, espressa nelle stesse unità del target. Un RMSE di 0,75 significa che le previsioni del modello si discostano in media di circa $75.000.
- R² (coefficiente di determinazione) — la frazione della varianza del target spiegata dal modello. Un R² di 0,58 significa che il modello cattura il 58% della variazione dei prezzi delle abitazioni. I valori vanno da 0 (non meglio della previsione della media) a 1 (previsioni perfette). Un R² negativo è possibile se il modello è peggiore della media — è un segnale forte che qualcosa non va.
Un R² di ~0,58 è tipico per questo dataset con la regressione lineare standard. La relazione tra i prezzi delle abitazioni e queste feature è in parte non lineare e coinvolge raggruppamenti geografici che un iperpiano non riesce a catturare bene. Algoritmi come i gradient-boosted tree raggiungono regolarmente 0,80+ su questo dataset.
Passo 5 — Esaminare i Coefficienti
Dopo lo scaling, le magnitudini dei coefficienti sono direttamente confrontabili — mostrano quali feature influenzano maggiormente la previsione:
coef_df = pd.DataFrame({
'Feature': housing.feature_names,
'Coefficient': model.coef_
}).sort_values('Coefficient', key=abs, ascending=False)
print(coef_df.to_string(index=False))
print(f"\nIntercept: {model.intercept_:.4f}")Output atteso:
Feature Coefficient
Latitude -0.8969
Longitude -0.8698
MedInc 0.8544
AveBedrms 0.3393
AveRooms -0.2944
HouseAge 0.1225
AveOccup -0.0408
Population -0.0023
Intercept: 2.0719Lettura dei coefficienti dopo la standardizzazione:
MedInc = 0.854— il predittore singolarmente più forte. Un aumento di una deviazione standard nel reddito mediano prevede un aumento di $85.400 nel valore dell'abitazione, mantenendo tutto il resto costante.Latitude = -0.897eLongitude = -0.869— il modello ha appreso che i blocchi censimento più a nord e più a est tendono ad essere più economici. Tuttavia, queste due feature geografiche sono altamente correlate (r = -0,93), il che può rendere instabili i loro coefficienti individuali (vedi Diagnosi della Multicollinearità).AveBedrms = +0.339vsAveRooms = -0.294— hanno segni opposti anche se più stanze e più camere da letto in genere significano case più grandi e costose. Questo è un classico segnale di multicollinearità:AveRoomseAveBedrmssono correlate (r = 0,85), quindi i loro coefficienti si compensano a vicenda. Non interpretarli in isolamento.Population = -0.002— molto vicino a zero dopo lo scaling. La popolazione del blocco ha un potere predittivo minimo una volta tenute in conto le altre feature.
L'intercetta (2,07) è il MedHouseVal previsto quando ogni feature scalata è zero — ovvero quando tutte le feature sono alla loro media del training set. Corrisponde alla media dei target di addestramento e non ha un significato aziendale diretto al di là di questo.
Passo 6 — Fare Previsioni su Nuovi Dati
# A new census block: high income, older house, San Francisco Bay Area
new_block = pd.DataFrame([[8.0, 41.0, 6.0, 1.0, 322, 2.5, 37.88, -122.23]],
columns=housing.feature_names)
new_block_scaled = scaler.transform(new_block)
prediction = model.predict(new_block_scaled)
print(f"Predicted median house value: ${prediction[0] * 100_000:,.0f}")Output atteso:
Predicted median house value: $410,895Lo scaler deve essere lo stesso scaler adattato sui dati di addestramento. Non riadattare mai lo scaler sui nuovi dati — ciò sposterebbe gli input rispetto a ciò che il modello ha appreso.
Diagnosi della Multicollinearità
La multicollinearità si verifica quando due o più variabili indipendenti sono altamente correlate tra loro. Non impedisce al modello di fare previsioni accurate, ma rende i coefficienti individuali inaffidabili e difficili da interpretare. I coefficienti possono diventare grandi, cambiare segno o diventare statisticamente non significativi anche per feature genuinamente importanti.
Verifica con una Matrice di Correlazione
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
corr = df[housing.feature_names].corr()
print(corr.round(2))Coppie chiave da osservare:
| Coppia di feature | Correlazione | Problema |
|---|---|---|
AveRooms / AveBedrms | 0,85 | Alta — i coefficienti si compensano a vicenda |
Latitude / Longitude | -0,93 | Molto alta — co-movimento geografico |
MedInc / MedHouseVal (target) | 0,69 | Buon predittore, non è un problema di collinearità |
Una correlazione superiore a 0,80 tra due feature è un segnale d'allarme. Quando la si riscontra, considera:
- Eliminare una delle feature correlate. Se sia
AveRoomscheAveBedrmssono nel modello, prova a rimuovereAveBedrmse verifica se le prestazioni predittive del modello cambiano molto. - Combinarle. Crea una feature derivata (es.
rooms_per_bedroom = AveRooms / AveBedrms) che cattura la relazione senza ridondanza. - Usare un modello regolarizzato. La regressione Ridge aggiunge una penalità L2 che riduce i coefficienti correlati verso i loro valori medi, stabilizzandoli. Lasso (L1) può azzerare completamente le feature ridondanti.
Analisi dei Residui
Un residuo è la differenza tra un valore reale e la previsione del modello: residual = y_actual - y_predicted. Tracciare i residui rivela se le assunzioni del modello sono verificate.
residuals = y_test - y_pred
print(f"Mean of residuals: {residuals.mean():.4f}") # should be close to 0
print(f"Std of residuals: {residuals.std():.4f}")Output atteso:
Mean of residuals: 0.0035
Std of residuals: 0.7457La media è vicina a zero — un segnale che il modello è non distorto in media. Ma la deviazione standard di 0,75 (±$75.000) mostra una dispersione sostanziale.
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# Predicted vs Actual
axes[0].scatter(y_test, y_pred, alpha=0.2, s=8)
axes[0].plot([0, 5], [0, 5], 'r--')
axes[0].set_xlabel('Actual')
axes[0].set_ylabel('Predicted')
axes[0].set_title('Predicted vs Actual')
# Residuals vs Predicted
axes[1].scatter(y_pred, residuals, alpha=0.2, s=8)
axes[1].axhline(0, color='r', linestyle='--')
axes[1].set_xlabel('Predicted')
axes[1].set_ylabel('Residual')
axes[1].set_title('Residuals vs Predicted')
plt.tight_layout()
plt.savefig('residuals.png', dpi=120)
print("Saved residuals.png")Cosa cercare:
- Previsti vs Reali — idealmente, i punti cadono lungo la diagonale. Una deviazione sistematica dalla diagonale (una curva, o un appiattimento ai valori alti) significa che l'assunzione lineare è errata per parte dell'intervallo.
- Residui vs Previsti — idealmente, i residui si disperdono casualmente attorno allo zero a tutti i livelli di previsione. Una forma a imbuto (dispersione più ampia a previsioni più alte) segnala eteroschedasticità — l'errore del modello non è costante, il che può rendere inaffidabili le stime degli intervalli.
- Cluster — gruppi distinti nel grafico dei residui possono rivelare che il dataset contiene sotto-popolazioni (es. urbano vs rurale) che richiedono modelli separati o feature aggiuntive.
Pipeline Completa
Ecco tutto quanto sopra come script eseguibile in una sola volta:
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score
# 1. Load data
housing = fetch_california_housing()
df = pd.DataFrame(housing.data, columns=housing.feature_names)
df['MedHouseVal'] = housing.target
# 2. Split — before any preprocessing
X = df[housing.feature_names]
y = df['MedHouseVal']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 3. Scale
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 4. Train
model = LinearRegression()
model.fit(X_train_scaled, y_train)
# 5. Evaluate
y_pred = model.predict(X_test_scaled)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.4f}")
print(f"R²: {r2:.4f}")
# 6. Coefficients (most important first)
coef_df = pd.DataFrame({
'Feature': housing.feature_names,
'Coefficient': model.coef_
}).sort_values('Coefficient', key=abs, ascending=False)
print(coef_df.to_string(index=False))Quando Usare la Regressione Multipla
La regressione multipla è una scelta iniziale valida quando:
- La relazione tra ciascuna feature e il target è approssimativamente lineare
- L'interpretabilità è importante — ogni coefficiente ha un significato chiaro
- Si vuole una baseline rapida prima di provare modelli complessi
- Si hanno abbastanza campioni rispetto al numero di feature (regola approssimativa: almeno 10–20 osservazioni per feature)
Considera alternative quando:
| Situazione | Alternativa |
|---|---|
| Relazioni non lineari tra feature e target | Regressione Polinomiale |
| Molte feature, rischio di overfitting | Ridge o Lasso (modelli lineari regolarizzati) |
| Il target è una categoria, non un numero | Regressione Logistica |
| Interazioni complesse e non linearità | Gradient-boosted tree o random forest |
Errori Comuni
Adattare lo scaler su tutti i dati prima della suddivisione. Questo fa trapelare la media e la varianza del test set nell'addestramento. Suddividi sempre prima, poi adatta lo scaler solo sulla porzione di addestramento.
Aggiungere più feature aiuta sempre. Aggiungere feature irrilevanti o ridondanti può ridurre l'interpretabilità, introdurre multicollinearità e peggiorare la generalizzazione. Usa la conoscenza del dominio o una tecnica di selezione delle feature per scegliere le feature in modo deliberato.
Fidarsi dei coefficienti quando le feature sono correlate. Quando sia AveRooms che AveBedrms sono nel modello, nessuno dei due coefficienti riflette in modo affidabile il vero effetto di quella variabile. Controlla la matrice di correlazione prima di interpretare i coefficienti individuali.
Ignorare i grafici dei residui. Un R² di 0,58 sembra ragionevole sulla carta, ma i pattern dei residui possono rivelare che il modello è sistematicamente errato per le proprietà di alto valore o per specifiche regioni geografiche.
Estrapolare oltre l'intervallo di addestramento. Un modello lineare addestrato su abitazioni con prezzi tra $50.000 e $500.000 non dovrebbe essere usato per prevedere proprietà da $5.000.000. Verifica che i nuovi input rientrino nell'intervallo osservato durante l'addestramento.
Prossimi Passi
- Regressione Lineare — le fondamenta: la regressione semplice (con una feature) e il metodo OLS spiegato per intero
- Regressione Polinomiale — estendi il modello lineare per catturare curve aggiungendo termini di feature polinomiali
- Feature Scaling — approfondimento su StandardScaler, MinMaxScaler e RobustScaler
- Train/Test Split — perché la suddivisione corretta è fondamentale e come eseguirla senza far trapelare i dati
- Cross-Validation — un'alternativa più robusta a una singola suddivisione train/test