CLASSE QUARTA • REGRESSIONE MULTIPLA

Regressione Multipla

Usa più informazioni insieme per fare previsioni migliori

Passiamo al livello successivo! Nella vita reale, le cose non dipendono da un solo fattore. La regressione multipla ci permette di considerare tutte le variabili insieme per fare previsioni più accurate.

🎯 Cos'è la Regressione Multipla?

🤔 In parole semplici...

Immagina di voler predire il prezzo di una casa. Non basta guardare solo la metratura!

Regressione Semplice: Prezzo = f(Metratura)

Regressione Multipla: Prezzo = f(Metratura, Numero camere, Età, Quartiere, ...)

Più informazioni abbiamo, più precisa sarà la nostra previsione!

🍎 Un'analogia semplice

Regressione Semplice è come fare una torta con solo farina.
Regressione Multipla è come fare una torta con farina, zucchero, uova, burro...
Il risultato sarà molto migliore!

🏠
Prezzo = β₀ + β₁×Metratura + β₂×Camere + β₃×Età + ...
Ogni β (beta) misura quanto quella caratteristica influisce sul prezzo
Metratura
β₁ = +200 €/m²
Numero Camere
β₂ = +15.000 €/camera
Età Casa
β₃ = -1.000 €/anno
Distanza Centro
β₄ = -5.000 €/km

Passo 1: Raccogli tutte le informazioni

Per ogni casa, misuriamo tutte le caratteristiche che potrebbero influenzare il prezzo.

Passo 2: Trova i pesi giusti (β)

Il modello calcola automaticamente quanto ogni caratteristica "pesa" sul prezzo finale.

Passo 3: Combina tutto insieme

Moltiplica ogni caratteristica per il suo peso e somma tutto per ottenere la previsione.

Passo 4: Verifica se funziona

Controlliamo se le previsioni sono vicine ai prezzi reali e migliorano con più variabili.

🔧 Implementazione Pratica: Case Study Completo

Vediamo un esempio completo passo-passo. Useremo dati realistici per capire ogni concetto.

🚫 COPIA BLOCCATA
# REGRESSIONE MULTIPLA COMPLETA - TUTTO SPIEGATO PASSO PASSO import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score from sklearn.preprocessing import StandardScaler print("🏠 REGRESSIONE MULTIPLA - PREZZI CASE (TUTTO SPIEGATO)") print("=" * 80) print("\n🎯 OBIETTIVO: Predire il prezzo di una casa usando PIÙ informazioni insieme") print("=" * 80) # ------------------------------------------------------------ # PASSO 1: CREIAMO DATI REALISTICI (FACILI DA CAPIRE) # ------------------------------------------------------------ print("\n\n📝 PASSO 1: CREIAMO DATI DI ESEMPIO") print("-" * 40) np.random.seed(42) # Per risultati riproducibili n_case = 200 # 200 case nel nostro dataset print("Creiamo 200 case con queste caratteristiche:") # 1. METRATURA (in metri quadri) # Case normali: tra 80 e 200 m² metratura = np.random.uniform(80, 200, n_case) print(f"• Metratura: {metratura.min():.0f} - {metratura.max():.0f} m² (media: {metratura.mean():.0f} m²)") # 2. NUMERO DI CAMERE # Case normali: tra 2 e 6 camere camere = np.random.randint(2, 7, n_case) print(f"• Camere: {camere.min()} - {camere.max()} (media: {camere.mean():.1f})") # 3. ETÀ DELLA CASA (in anni) # Case tra 0 e 50 anni eta = np.random.uniform(0, 50, n_case) print(f"• Età: {eta.min():.0f} - {eta.max():.0f} anni (media: {eta.mean():.0f} anni)") # 4. DISTANZA DAL CENTRO (in km) # Case tra 1 e 15 km dal centro distanza = np.random.uniform(1, 15, n_case) print(f"• Distanza centro: {distanza.min():.0f} - {distanza.max():.0f} km (media: {distanza.mean():.1f} km)") # ------------------------------------------------------------ # PASSO 2: CALCOLIAMO I PREZZI REALI (CON LOGICA SEMPLICE) # ------------------------------------------------------------ print("\n\n📝 PASSO 2: CALCOLIAMO I PREZZI REALI") print("-" * 40) print("Usiamo una formula semplice per calcolare il prezzo di ogni casa:") print("Prezzo = (2000 × Metratura) + (30000 × Camere) - (1000 × Età) - (5000 × Distanza) + Rumore") # Formula del prezzo (facile da capire) prezzo_base = ( 2000 * metratura + # Ogni m² vale 2000€ 30000 * camere + # Ogni camera vale 30000€ -1000 * eta + # Ogni anno riduce il valore di 1000€ -5000 * distanza + # Ogni km dal centro riduce di 5000€ np.random.normal(0, 20000, n_case) # Rumore (variazioni casuali) ) # Assicuriamoci che i prezzi siano realistici (100k - 800k €) prezzo = np.clip(prezzo_base, 100000, 800000) print(f"\n💰 PREZZI FINALI:") print(f"Prezzo minimo: €{prezzo.min():,.0f}") print(f"Prezzo massimo: €{prezzo.max():,.0f}") print(f"Prezzo medio: €{prezzo.mean():,.0f}") # ------------------------------------------------------------ # PASSO 3: CREIAMO IL DATAFRAME (TABELLA DEI DATI) # ------------------------------------------------------------ print("\n\n📝 PASSO 3: ORGANIZZIAMO I DATI IN UNA TABELLA") print("-" * 40) df_case = pd.DataFrame({ 'Metratura_m2': metratura, 'Camere': camere, 'Eta_anni': eta, 'DistanzaCentro_km': distanza, 'Prezzo_€': prezzo }) print("📊 LA NOSTRA TABELLA DATI (prime 8 case):") print(df_case.head(8).round(0)) print(f"\n📈 DIMENSIONI: {df_case.shape[0]} righe (case) × {df_case.shape[1]} colonne (caratteristiche)") # ------------------------------------------------------------ # PASSO 4: ANALIZZIAMO LE RELAZIONI TRA I DATI # ------------------------------------------------------------ print("\n\n📝 PASSO 4: VEDIAMO COME LE CARATTERISTICHE SI RELAZIONANO") print("-" * 40) print("🎯 CORRELAZIONI (quanto le cose vanno insieme):") correlazioni = df_case.corr() # Calcola correlazioni print(correlazioni.round(2)) # Arrotonda a 2 decimali print("\n📊 INTERPRETAZIONE:") print("• Prezzo vs Metratura: {:.2f} (forte positiva)".format(correlazioni.loc['Prezzo_€', 'Metratura_m2'])) print("• Prezzo vs Camere: {:.2f} (media positiva)".format(correlazioni.loc['Prezzo_€', 'Camere'])) print("• Prezzo vs Età: {:.2f} (media negativa)".format(correlazioni.loc['Prezzo_€', 'Eta_anni'])) print("• Prezzo vs Distanza: {:.2f} (media negativa)".format(correlazioni.loc['Prezzo_€', 'DistanzaCentro_km'])) print("\n📈 SU GOOGLE COLAB, SCOMMENTA PER VEDERE I GRAFICI:") """ # Grafico 1: Prezzo vs Metratura plt.figure(figsize=(10, 6)) plt.scatter(df_case['Metratura_m2'], df_case['Prezzo_€'], alpha=0.6) plt.title('Prezzo vs Metratura') plt.xlabel('Metratura (m²)') plt.ylabel('Prezzo (€)') plt.grid(True, alpha=0.3) plt.show() # Grafico 2: Matrice di correlazione plt.figure(figsize=(8, 6)) sns.heatmap(correlazioni, annot=True, cmap='coolwarm', center=0, fmt='.2f') plt.title('Come le caratteristiche si influenzano a vicenda') plt.show() """ # ------------------------------------------------------------ # PASSO 5: PREPARIAMO I DATI PER IL MACHINE LEARNING # ------------------------------------------------------------ print("\n\n📝 PASSO 5: PREPARIAMO I DATI PER L'ANALISI") print("-" * 40) print("Dividiamo i dati in due parti:") print("1. TRAINING (80%): Per insegnare al modello") print("2. TEST (20%): Per verificare se ha imparato bene") # Caratteristiche (X) e Prezzo da predire (y) X = df_case[['Metratura_m2', 'Camere', 'Eta_anni', 'DistanzaCentro_km']] y = df_case['Prezzo_€'] print(f"\n📊 CARATTERISTICHE (X): {X.shape[0]} case × {X.shape[1]} caratteristiche") print(f"🎯 PREZZO DA PREDIRE (y): {len(y)} valori") # Divisione training/test X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, # 20% per test random_state=42 # Per risultati riproducibili ) print(f"\n✅ DIVISIONE COMPLETATA:") print(f"Case per training: {X_train.shape[0]} ({X_train.shape[0]/len(X)*100:.0f}%)") print(f"Case per test: {X_test.shape[0]} ({X_test.shape[0]/len(X)*100:.0f}%)") # ------------------------------------------------------------ # PASSO 6: STANDARDIZZAZIONE (MOLTO IMPORTANTE!) # ------------------------------------------------------------ print("\n\n📝 PASSO 6: STANDARDIZZIAMO LE CARATTERISTICHE") print("-" * 40) print("Perché standardizzare? Perché le caratteristiche hanno scale diverse:") print("• Metratura: 80-200 m²") print("• Camere: 2-6 (solo numeri interi)") print("• Età: 0-50 anni") print("• Distanza: 1-15 km") print("\n🎯 Standardizziamo tutto su una scala comune (media=0, deviazione=1)") scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) print("✅ Standardizzazione completata!") print(f"Media dopo standardizzazione: {X_train_scaled.mean():.2f} (dovrebbe essere circa 0)") print(f"Deviazione standard: {X_train_scaled.std():.2f} (dovrebbe essere circa 1)") # ------------------------------------------------------------ # PASSO 7: CREIAMO E ALLENIAMO IL MODELLO # ------------------------------------------------------------ print("\n\n📝 PASSO 7: CREIAMO IL MODELLO DI REGRESSIONE MULTIPLA") print("-" * 40) print("Creiamo il modello...") model = LinearRegression() print("Alleniamo il modello con i dati di training...") model.fit(X_train_scaled, y_train) print("✅ Modello addestrato con successo!") print("\n🎯 COSA HA IMPARATO IL MODELLO? (Coefficienti β):") # Mostriamo i coefficienti in modo chiaro caratteristiche = ['Metratura', 'Camere', 'Età', 'Distanza Centro'] print("\nPer ogni caratteristica, il modello ha imparato questo peso (β):") for i, (caratteristica, coef) in enumerate(zip(caratteristiche, model.coef_)): segno = "+" if coef > 0 else "" print(f"• {caratteristica:15} : {segno}{coef:,.0f} €") print(f"\n📐 PREZZO BASE (quando tutto è zero): {model.intercept_:,.0f} €") print("\n📖 COME INTERPRETARE I COEFFICIENTI:") print("Per ogni aumento di 1 deviazione standard...") print("1. Metratura: +{:,}€ al prezzo".format(int(model.coef_[0]))) print("2. Camere: +{:,}€ al prezzo".format(int(model.coef_[1]))) print("3. Età: {:,}€ al prezzo (negativo = riduce il valore)".format(int(model.coef_[2]))) print("4. Distanza Centro: {:,}€ al prezzo (negativo = riduce il valore)".format(int(model.coef_[3]))) # ------------------------------------------------------------ # PASSO 8: FACCIAMO PREVISIONI E VERIFICHIAMO # ------------------------------------------------------------ print("\n\n📝 PASSO 8: FACCIAMO PREVISIONI E VERIFICHIAMO") print("-" * 40) print("Usiamo il modello per predire i prezzi delle case di test...") y_pred = model.predict(X_test_scaled) print("✅ Previsioni completate!") print(f"\n📊 CONFRONTO PRIME 5 CASE DI TEST:") # Confronto per le prime 5 case confronto = pd.DataFrame({ 'Prezzo Reale': y_test.values[:5], 'Prezzo Predetto': y_pred[:5], 'Differenza': y_test.values[:5] - y_pred[:5] }).round(0) print(confronto) print(f"\n📈 ERRORI MEDI:") mse = mean_squared_error(y_test, y_pred) rmse = np.sqrt(mse) print(f"• Errore quadratico medio: {mse:,.0f}") print(f"• Errore medio: €{rmse:,.0f}") print(f"• Errore percentuale medio: {(rmse/y_test.mean()*100):.1f}%") # ------------------------------------------------------------ # PASSO 9: VALUTIAMO LA BONTÀ DEL MODELLO # ------------------------------------------------------------ print("\n\n📝 PASSO 9: VALUTIAMO QUANTO È BUONO IL MODELLO") print("-" * 40) # Calcoliamo R² (coefficiente di determinazione) r2 = r2_score(y_test, y_pred) print(f"🎯 R² SCORE: {r2:.3f}") print(f"\n📊 COSA SIGNIFICA R² = {r2:.3f} ?") print(f"Il nostro modello spiega il {r2*100:.1f}% della variazione nei prezzi delle case.") print("Più è vicino a 1, migliore è il modello.") if r2 > 0.8: print("✅ OTTIMO! Il modello spiega molto bene i prezzi.") elif r2 > 0.6: print("⚠️ BUONO, ma potremmo migliorare aggiungendo più caratteristiche.") else: print("❌ POTREMMO FARE MEGLIO. Forse ci mancano caratteristiche importanti.") print("\n📈 SU GOOGLE COLAB, SCOMMENTA PER VEDERE IL CONFRONTO:") """ # Grafico Prezzo Reale vs Predetto plt.figure(figsize=(10, 6)) plt.scatter(y_test, y_pred, alpha=0.6) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2) plt.xlabel('Prezzo Reale (€)') plt.ylabel('Prezzo Predetto (€)') plt.title('Confronto: Prezzi Reali vs Predetti') plt.grid(True, alpha=0.3) plt.show() # Grafico errori errori = y_test - y_pred plt.figure(figsize=(10, 6)) plt.hist(errori, bins=30, edgecolor='black', alpha=0.7) plt.axvline(x=0, color='red', linestyle='--', lw=2) plt.xlabel('Errore (Prezzo Reale - Predetto)') plt.ylabel('Numero di Case') plt.title('Distribuzione degli Errori') plt.grid(True, alpha=0.3) plt.show() """ # ------------------------------------------------------------ # PASSO 10: FACCIAMO PREVISIONI SU NUOVE CASE # ------------------------------------------------------------ print("\n\n📝 PASSO 10: USIAMO IL MODELLO PER NUOVE CASE") print("-" * 40) print("Immaginiamo di avere 3 nuove case da valutare:") nuove_case = pd.DataFrame({ 'Metratura_m2': [120, 180, 90], 'Camere': [3, 4, 2], 'Eta_anni': [10, 5, 30], 'DistanzaCentro_km': [5, 2, 12] }) print("\n🏠 CARATTERISTICHE DELLE NUOVE CASE:") print(nuove_case) # Standardizziamo come abbiamo fatto per il training nuove_case_scaled = scaler.transform(nuove_case) # Predici i prezzi prezzi_predetti = model.predict(nuove_case_scaled) print("\n💰 PREZZI PREDETTI DAL NOSTRO MODELLO:") for i in range(len(nuove_case)): print(f"\nCasa {i+1}:") print(f" • {nuove_case.iloc[i]['Metratura_m2']} m², {nuove_case.iloc[i]['Camere']} camere") print(f" • {nuove_case.iloc[i]['Eta_anni']} anni, {nuove_case.iloc[i]['DistanzaCentro_km']} km dal centro") print(f" • 💰 Prezzo stimato: €{prezzi_predetti[i]:,.0f}") # Aggiungiamo un intervallo di confidenza semplice print(f"\n📊 INTERVALLO DI CONFIDENZA (approssimato):") print(f"Considerando un errore medio di €{rmse:,.0f}, il vero prezzo probabilmente è:") for i, prezzo_pred in enumerate(prezzi_predetti): print(f"Casa {i+1}: €{prezzo_pred - rmse:,.0f} - €{prezzo_pred + rmse:,.0f}") # ------------------------------------------------------------ # PASSO 11: CONFRONTO CON REGRESSIONE SEMPLICE # ------------------------------------------------------------ print("\n\n📝 PASSO 11: CONFRONTO CON REGRESSIONE SEMPLICE") print("-" * 40) print("Vediamo se la regressione multipla è davvero migliore...") # Regressione semplice (solo metratura) from sklearn.linear_model import LinearRegression as LR_semplice model_semplice = LR_semplice() model_semplice.fit(X_train[['Metratura_m2']], y_train) y_pred_semplice = model_semplice.predict(X_test[['Metratura_m2']]) r2_semplice = r2_score(y_test, y_pred_semplice) print("🎯 CONFRONTO PERFORMANCE:") print(f"• Regressione SEMPLICE (solo metratura): R² = {r2_semplice:.3f}") print(f"• Regressione MULTIPLA (4 caratteristiche): R² = {r2:.3f}") miglioramento = ((r2 - r2_semplice) / r2_semplice) * 100 print(f"\n📈 MIGLIORAMENTO: +{miglioramento:.1f}% di accuratezza!") print("\n🎯 PERCHÉ LA REGRESSIONE MULTIPLA È MEGLIO?") print("1. Considera TUTTE le informazioni disponibili") print("2. Isola l'effetto di ogni caratteristica") print("3. Fa previsioni più accurate e realistiche") print("4. Ci aiuta a capire cosa conta davvero nel prezzo") # ------------------------------------------------------------ # PASSO 12: CONCLUSIONI E CONSIGLI PRATICI # ------------------------------------------------------------ print("\n\n📝 PASSO 12: COSA ABBIAMO IMPARATO E COME USARLO") print("-" * 40) print("✅ COSA ABBIAMO IMPARATO OGGI:") print("1. La regressione multipla usa più caratteristiche insieme") print("2. Ogni caratteristica ha un 'peso' (coefficiente β)") print("3. Dobbiamo standardizzare le caratteristiche prima dell'analisi") print("4. R² ci dice quanto il modello spiega i dati") print("5. Più caratteristiche ≠ sempre meglio, devono essere utili") print("\n🎯 COME USARE LA REGRESSIONE MULTIPLA NELLA VITA REALE:") print("1. Raccogli tutte le informazioni rilevanti") print("2. Controlla che non ci siano errori nei dati") print("3. Standardizza se le scale sono diverse") print("4. Dividi in training e test") print("5. Addestra il modello e interpreta i coefficienti") print("6. Verifica con R² e errori") print("7. Usa per fare previsioni su nuovi dati") print("\n⚠️ COSA EVITARE:") print("1. Non usare troppe caratteristiche irrilevanti") print("2. Non dimenticare di standardizzare") print("3. Non testare sui dati usati per il training") print("4. Non interpretare correlazioni come causalità") print("\n📊 RIEPILOGO DEL NOSTRO MODELLO:") print(f"Equazione: Prezzo = {model.intercept_:,.0f} + {model.coef_[0]:,.0f}×Metratura + {model.coef_[1]:,.0f}×Camere + {model.coef_[2]:,.0f}×Età + {model.coef_[3]:,.0f}×Distanza") print(f"Accuratezza: R² = {r2:.3f} ({r2*100:.1f}% varianza spiegata)") print(f"Errore tipico: ±€{rmse:,.0f} per previsione") print("\n🎉 COMPLIMENTI! HAI MASTERIZZATO LA REGRESSIONE MULTIPLA!") print("Ora puoi analizzare problemi complessi con più variabili!")
Varianza Spiegata
0.85
4
Caratteristiche
Usate insieme
±€45k
Errore Tipico
Per previsione
+25%
Miglioramento
vs Semplice

💡 Perché standardizzare è così importante?

Problema: Se una caratteristica ha numeri grandi (es: metratura 80-200) e un'altra ha numeri piccoli (es: camere 2-6), il modello darebbe più importanza a quella con numeri grandi anche se non è più importante.

Soluzione: Standardizziamo tutto! Così tutte le caratteristiche hanno media=0 e deviazione=1, e il modello può dare il peso giusto a ciascuna.

Formula semplice: Nuovo valore = (Vecchio valore - Media) / Deviazione Standard

⚠️ Il Problema della Multicollinearità

Cosa succede quando le caratteristiche sono troppo correlate tra loro?

🤔 Cosa significa Multicollinearità?

Immagina di avere due caratteristiche che dicono quasi la stessa cosa...

Esempio: "Superficie totale" e "Numero di stanze" sono spesso correlate (più stanze = più superficie).

Problema: Il modello non sa più a chi dare il merito!

✅ Caso Ideale

Caratteristiche indipendenti:

  • Metratura
  • Età della casa
  • Distanza dal centro

✔️ Il modello funziona bene

⚠️ Problema Moderato

Caratteristiche correlate:

  • Metratura
  • Numero stanze
  • Superficie soggiorno

⚠️ Coefficienti instabili

❌ Problema Grave

Caratteristiche identiche:

  • Superficie in m²
  • Superficie in piedi²
  • Superficie in iarde²

❌ Modello non funziona

🚫 COPIA BLOCCATA
# DIMOSTRAZIONE DELLA MULTICOLLINEARITÀ import numpy as np import pandas as pd from sklearn.linear_model import LinearRegression print("⚠️ DIMOSTRAZIONE MULTICOLLINEARITÀ") print("=" * 60) # Caso 1: Caratteristiche indipendenti (ideale) print("\n🎯 CASO 1: CARATTERISTICHE INDIPENDENTI") np.random.seed(42) n = 100 # Caratteristiche realmente indipendenti X1_ind = np.random.normal(0, 1, (n, 1)) # Feature 1 X2_ind = np.random.normal(0, 1, (n, 1)) # Feature 2 (indipendente dalla 1) # Target: combinazione delle due y_ind = 2 * X1_ind.flatten() + 3 * X2_ind.flatten() + np.random.normal(0, 0.5, n) # Regressione model_ind = LinearRegression() model_ind.fit(np.column_stack([X1_ind, X2_ind]), y_ind) print("Coefficienti trovati:") print(f"β₁ (Feature 1): {model_ind.coef_[0]:.3f} (vero valore: 2.0)") print(f"β₂ (Feature 2): {model_ind.coef_[1]:.3f} (vero valore: 3.0)") print(f"R²: {model_ind.score(np.column_stack([X1_ind, X2_ind]), y_ind):.3f}") print("✅ Coefficienti stabili e vicini ai valori veri") # Caso 2: Caratteristiche correlate (problema) print("\n\n⚠️ CASO 2: CARATTERISTICHE CORRELATE") # Creiamo due feature correlate X1_corr = np.random.normal(0, 1, n) X2_corr = X1_corr + np.random.normal(0, 0.1, n) # Quasi uguale a X1 # Target: dipende solo da X1 y_corr = 2 * X1_corr + np.random.normal(0, 0.5, n) # Regressione model_corr = LinearRegression() model_corr.fit(np.column_stack([X1_corr, X2_corr]), y_corr) print("Coefficienti trovati:") print(f"β₁ (Feature 1): {model_corr.coef_[0]:.3f} (vero valore: 2.0)") print(f"β₂ (Feature 2): {model_corr.coef_[1]:.3f} (vero valore: 0.0)") print(f"R²: {model_corr.score(np.column_stack([X1_corr, X2_corr]), y_corr):.3f}") print("⚠️ Coefficienti instabili! Il modello non sa a chi dare il merito") # Caso 3: Caratteristiche identiche (grave problema) print("\n\n❌ CASO 3: CARATTERISTICHE IDENTICHE") X1_ident = np.random.normal(0, 1, n) X2_ident = X1_ident # Esattamente identica! # Target y_ident = 2 * X1_ident + np.random.normal(0, 0.5, n) # Tentativo di regressione (dovrebbe fallire o dare risultati strani) try: model_ident = LinearRegression() model_ident.fit(np.column_stack([X1_ident, X2_ident]), y_ident) print("Coefficienti (molto strani!):") print(f"β₁: {model_ident.coef_[0]:.3f}") print(f"β₂: {model_ident.coef_[1]:.3f}") print("❌ Coefficienti inaffidabili! Infiniti modi per ottenere lo stesso risultato") except Exception as e: print(f"❌ ERRORE: {e}") print("Il modello non può risolvere il problema con caratteristiche identiche") print("\n🎯 COME DETECTARE MULTICOLLINEARITÀ:") print("1. Calcola matrice di correlazione tra le features") print("2. Cerca correlazioni > 0.8 o < -0.8") print("3. Calcola VIF (Variance Inflation Factor)") print("4. Se VIF > 5-10, c'è multicollinearità") print("\n🔧 COME RISOLVERE:") print("1. Rimuovi una delle feature correlate") print("2. Combina feature correlate in una sola") print("3. Usa tecniche come PCA (Principal Component Analysis)") print("4. Usa regularizzazione (Ridge, Lasso)") # Esempio di calcolo VIF print("\n📊 ESEMPIO PRATICO DI VIF:") from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant # Dati di esempio X_example = np.column_stack([X1_ind.flatten(), X2_ind.flatten()]) X_with_const = add_constant(X_example) vif_data = [] for i in range(1, X_with_const.shape[1]): vif = variance_inflation_factor(X_with_const, i) vif_data.append(vif) print("VIF per feature indipendenti:") for i, vif in enumerate(vif_data, 1): print(f" Feature {i}: VIF = {vif:.2f}") if vif > 5: print(" ⚠️ Potenziale multicollinearità") else: print(" ✅ OK")

🎯 Regola pratica per la multicollinearità

Correlazione tra due features:

• < 0.3: Bassa correlazione ✅

• 0.3 - 0.7: Moderata correlazione ⚠️

• > 0.7: Alta correlazione (controlla VIF) ❌

• > 0.9: Molto alta correlazione (rimuovi una feature) 🚫

🔍 Feature Selection e Regularizzazione

Come scegliere le caratteristiche migliori e evitare l'overfitting.

🤔 Perché non usare TUTTE le caratteristiche?

Problema: Se usiamo troppe caratteristiche (specialmente quelle inutili), il modello "memorizza" i dati invece di "imparare" i pattern.

Risultato: Funziona bene sui dati di training, ma male su nuovi dati (overfitting).

Soluzione: Scegliamo solo le caratteristiche davvero importanti!

🚫 COPIA BLOCCATA
# FEATURE SELECTION E REGULARIZZAZIONE PRATICA import numpy as np import pandas as pd from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import r2_score print("🔍 FEATURE SELECTION E REGULARIZZAZIONE") print("=" * 70) # Creiamo dati con alcune feature utili e alcune inutili np.random.seed(42) n_samples = 200 n_features = 10 # 10 feature totali print(f"Creiamo {n_samples} campioni con {n_features} caratteristiche...") print("Di queste, solo 5 sono realmente utili per la predizione.") # Creiamo le feature features = [] feature_names = [] # 5 feature utili for i in range(5): feature = np.random.normal(0, 1, n_samples) features.append(feature) feature_names.append(f'Utile_{i+1}') # 5 feature inutili (rumore) for i in range(5): feature = np.random.normal(0, 1, n_samples) features.append(feature) feature_names.append(f'Inutile_{i+1}') # Target: dipende solo dalle prime 5 feature true_coefs = [2.0, 1.5, -1.0, 0.5, 0.8] + [0.0] * 5 # Ultime 5 = 0 target = np.zeros(n_samples) for i in range(n_features): target += true_coefs[i] * features[i] # Aggiungiamo rumore target += np.random.normal(0, 0.5, n_samples) # Creiamo DataFrame df_features = pd.DataFrame(np.column_stack(features), columns=feature_names) df_features['Target'] = target print("\n📊 DATASET CREATO:") print(f"• {len([c for c in feature_names if 'Utile' in c])} feature utili") print(f"• {len([c for c in feature_names if 'Inutile' in c])} feature inutili (rumore)") print(f"• Coefficienti veri delle feature utili: {true_coefs[:5]}") # ------------------------------------------------------------ # 1. REGRESSIONE LINEARE STANDARD (con tutte le feature) # ------------------------------------------------------------ print("\n\n1. 🤖 REGRESSIONE LINEARE STANDARD (tutte le feature)") print("-" * 40) X = df_features[feature_names] y = df_features['Target'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Standardizzazione scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # Regressione standard model_standard = LinearRegression() model_standard.fit(X_train_scaled, y_train) # Predizioni y_pred_standard = model_standard.predict(X_test_scaled) r2_standard = r2_score(y_test, y_pred_standard) print("📊 RISULTATI REGRESSIONE STANDARD:") print(f"R² sul test set: {r2_standard:.3f}") print("\n🎯 COEFFICIENTI TROVATI vs VERI:") coef_comparison = pd.DataFrame({ 'Feature': feature_names, 'Coefficiente Vero': true_coefs, 'Coefficiente Trovato': model_standard.coef_.round(3) }) print(coef_comparison.to_string(index=False)) print("\n⚠️ PROBLEMA: Le feature inutili hanno coefficienti diversi da zero!") print("Il modello sta 'memorizzando' anche il rumore (overfitting).") # ------------------------------------------------------------ # 2. REGRESSIONE RIDGE (L2 Regularization) # ------------------------------------------------------------ print("\n\n2. 🏔️ REGRESSIONE RIDGE (L2 Regularization)") print("-" * 40) print("Ridge aggiunge una penalità ai coefficienti troppo grandi.") print("Fa sì che tutti i coefficienti siano piccoli, ma nessuno diventi zero.") # Regressione Ridge model_ridge = Ridge(alpha=1.0) # alpha = forza della regularizzazione model_ridge.fit(X_train_scaled, y_train) y_pred_ridge = model_ridge.predict(X_test_scaled) r2_ridge = r2_score(y_test, y_pred_ridge) print("📊 RISULTATI REGRESSIONE RIDGE:") print(f"R² sul test set: {r2_ridge:.3f}") print("\n🎯 COEFFICIENTI RIDGE:") coef_ridge = pd.DataFrame({ 'Feature': feature_names, 'Coefficiente Vero': true_coefs, 'Coefficiente Ridge': model_ridge.coef_.round(3) }) print(coef_ridge.to_string(index=False)) print("\n✅ VANTAGGIO: I coefficienti delle feature inutili sono più vicini a zero.") print("Ma non diventano esattamente zero.") # ------------------------------------------------------------ # 3. REGRESSIONE LASSO (L1 Regularization) # ------------------------------------------------------------ print("\n\n3. 🎯 REGRESSIONE LASSO (L1 Regularization)") print("-" * 40) print("Lasso fa una cosa potente: manda a ZERO i coefficienti delle feature inutili!") print("In pratica, fa automaticamente feature selection.") # Regressione Lasso model_lasso = Lasso(alpha=0.1) # alpha = forza della regularizzazione model_lasso.fit(X_train_scaled, y_train) y_pred_lasso = model_lasso.predict(X_test_scaled) r2_lasso = r2_score(y_test, y_pred_lasso) print("📊 RISULTATI REGRESSIONE LASSO:") print(f"R² sul test set: {r2_lasso:.3f}") print("\n🎯 COEFFICIENTI LASSO (MAGIA!):") coef_lasso = pd.DataFrame({ 'Feature': feature_names, 'Coefficiente Vero': true_coefs, 'Coefficiente Lasso': model_lasso.coef_.round(3) }) print(coef_lasso.to_string(index=False)) # Conta quante feature ha selezionato Lasso n_selected = sum(abs(model_lasso.coef_) > 0.01) print(f"\n✅ LASSO HA SELEZIONATO: {n_selected} feature su {n_features}") print("Le feature inutili hanno coefficiente (quasi) zero!") # ------------------------------------------------------------ # 4. CONFRONTO COMPLETO # ------------------------------------------------------------ print("\n\n4. 📊 CONFRONTO COMPLETO DEI TRE METODI") print("-" * 40) print("🎯 PERFORMANCE SUL TEST SET:") print(f"• Regressione Standard: R² = {r2_standard:.3f}") print(f"• Regressione Ridge: R² = {r2_ridge:.3f}") print(f"• Regressione Lasso: R² = {r2_lasso:.3f}") print("\n🔍 FEATURE SELEZIONATE:") selected_features = { 'Standard': [f for f, c in zip(feature_names, abs(model_standard.coef_) > 0.01) if c], 'Ridge': [f for f, c in zip(feature_names, abs(model_ridge.coef_) > 0.01) if c], 'Lasso': [f for f, c in zip(feature_names, abs(model_lasso.coef_) > 0.01) if c] } print(f"• Standard: {len(selected_features['Standard'])} feature (tutte)") print(f"• Ridge: {len(selected_features['Ridge'])} feature (tutte ma coefficienti piccoli)") print(f"• Lasso: {len(selected_features['Lasso'])} feature (solo quelle utili)") print("\n🎯 COME SCEGLIERE TRA RIDGE E LASSO?") print("✅ USARE RIDGE QUANDO:") print(" • Tutte le feature potrebbero essere utili") print(" • Le feature sono correlate tra loro") print(" • Vuoi coefficienti piccoli ma non zero") print("✅ USARE LASSO QUANDO:") print(" • Molte feature potrebbero essere inutili") print(" • Vuoi un modello semplice e interpretabile") print(" • Vuoi fare feature selection automatica") print("\n🔧 COME SCEGLIERE IL VALORE DI ALPHA?") print("1. Prova diversi valori (0.001, 0.01, 0.1, 1, 10)") print("2. Usa cross-validation per trovare il migliore") print("3. Scegli l'alpha che dà il miglior R² sul validation set") # ------------------------------------------------------------ # 5. ESEMPIO DI CROSS-VALIDATION PER TROVARE ALPHA OTTIMALE # ------------------------------------------------------------ print("\n\n5. 🔄 TROVARE L'ALPHA OTTIMALE CON CROSS-VALIDATION") print("-" * 40) from sklearn.linear_model import LassoCV # Lasso con cross-validation model_lasso_cv = LassoCV(cv=5, random_state=42) model_lasso_cv.fit(X_train_scaled, y_train) print("🎯 ALPHA OTTIMALE TROVATO CON CROSS-VALIDATION:") print(f"Alpha = {model_lasso_cv.alpha_:.3f}") print("\n📊 COEFFICIENTI CON ALPHA OTTIMALE:") coef_lasso_cv = pd.DataFrame({ 'Feature': feature_names, 'Coefficiente Vero': true_coefs, 'Coefficiente LassoCV': model_lasso_cv.coef_.round(3) }) print(coef_lasso_cv.to_string(index=False)) # Valutazione sul test set y_pred_lasso_cv = model_lasso_cv.predict(X_test_scaled) r2_lasso_cv = r2_score(y_test, y_pred_lasso_cv) print(f"\n✅ PERFORMANCE CON ALPHA OTTIMALE: R² = {r2_lasso_cv:.3f}") print("\n🎯 RIEPILOGO FINALE:") print("1. Regressione Standard: Facile ma soggetta a overfitting") print("2. Regressione Ridge: Più stabile, coefficienti piccoli") print("3. Regressione Lasso: Feature selection automatica") print("4. Cross-validation: Trova i parametri ottimali") print("\n💡 CONSIGLIO PRATICO:") print("Inizia con Lasso per selezionare le feature importanti,") print("poi usa Ridge o regressione standard solo su quelle feature.")

🍝 Un'analogia culinaria per capire Ridge vs Lasso

Regressione Standard: Metti tutto quello che hai in pentola, anche se non sai se serve.

Ridge: Usi tutti gli ingredienti, ma in quantità controllate (non esageri con nulla).

Lasso: Assaggi ogni ingrediente e tiri via quelli che non aggiungono sapore.

Il risultato? Con Lasso ottieni un piatto più pulito e gustoso!

💪 Esercizi Pratici di Regressione Multipla

Esercizio 1: Predizione Performance Studenti FACILE

Predici il voto finale di uno studente basandoti su:

  • Ore di studio settimanali
  • Partecipazione in classe (0-10)
  • Voto medio test precedenti
  • Numero di assenze

Cosa fare:

  1. Crea dati sintetici per 100 studenti
  2. Addestra regressione multipla
  3. Interpreta i coefficienti: cosa influenza di più il voto?
  4. Predici il voto per uno studente con: 10 ore studio, partecipazione 8, voto medio 7, 2 assenze

Esercizio 2: Analisi Multicollinearità MEDIO

Hai queste feature per predire il consumo di benzina:

  1. Peso dell'auto (kg)
  2. Cilindrata (cc)
  3. Potenza (CV)
  4. Lunghezza (cm)
  5. Larghezza (cm)

Analisi richiesta:

  1. Simula dati con multicollinearità (peso, lunghezza e larghezza correlate)
  2. Calcola matrice di correlazione
  3. Confronta regressione standard vs Ridge vs Lasso
  4. Identifica quali feature sono realmente importanti

Esercizio 3: Dataset Reale - Boston Housing DIFFICILE

Usa il dataset Boston Housing (pre-caricato in scikit-learn):

🚫 COPIA BLOCCATA
from sklearn.datasets import load_boston boston = load_boston() # 13 feature per predire il prezzo delle case a Boston

Tasks complete:

  1. Analisi esplorativa completa
  2. Feature selection con Lasso
  3. Regressione multipla con le feature selezionate
  4. Cross-validation per valutazione robusta
  5. Interpretazione dei risultati in italiano semplice

🚀 Preparati per il Progetto Finale!

🎓 Cosa hai imparato in questi 5 moduli?

  1. Modulo 1: Pandas fundamentals - Gestire i dati
  2. Modulo 2: Data analysis avanzata - Pulire e analizzare
  3. Modulo 3: Intro ML - Concetti base e classificazione
  4. Modulo 4: Regressione lineare - Predire valori con una variabile
  5. Modulo 5: Regressione multipla - Predire con più variabili insieme

Ora sei pronto per mettere tutto insieme nel Progetto Finale!

🎯 Cosa faremo nel Progetto Finale?

Titolo: Sistema di Predizione Prezzi Case Complete

Obiettivo: Creare un modello ML professionale che:

  1. Carica dati reali da file CSV
  2. Pulisce e prepara i dati automaticamente
  3. Fa analisi esplorativa completa
  4. Seleziona le feature migliori
  5. Addestra più modelli e confronta le performance
  6. Fa previsioni su nuove case
  7. Genera un report automatico

💡 Consigli per il Progetto Finale

1. Prenditi il tuo tempo: È un progetto completo, non una corsa

2. Comprendi ogni passo: Non copiare, cerca di capire cosa stai facendo

3. Esperimenta: Prova a modificare parametri e vedere cosa succede

4. Divertiti! Stai creando il tuo primo sistema ML completo

Vai al Progetto Finale →