TLDR

  • La regressione lineare modella la relazione tra feature continue e target attraverso una combinazione lineare pesata dei parametri.
  • La funzione di costo MSE (Mean Squared Error) definisce una superficie tridimensionale convessa a ciotola con un unico minimo globale.
  • L'algoritmo di discesa del gradiente aggiorna simultaneamente tutti i pesi muovendosi nella direzione opposta alle derivate parziali.
  • La standardizzazione Z-Score e la vettorizzazione SIMD con NumPy accelerano la convergenza numerica di ordini di grandezza.

Il Modello di Regressione Lineare Univariata

La Regressione Lineare è l'algoritmo fondamentale dell'Apprendimento Supervisionato. Il suo obiettivo è prevedere un valore continuo reale $y \in \mathbb{R}$ a partire da una o più variabili predittive $x$ (feature).

Consideriamo un caso industriale concreto: la previsione della resa energetica in kilowatt ($\text{kW}$) immessa in rete da un parco solare fotovoltaico in funzione dell'irraggiamento solare misurato dai piranometri in $\text{W/m}^2$.

Dato un dataset di addestramento composto da $m$ osservazioni storiche: $$\mathcal{D} = \left{ (x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), \dots, (x^{(m)}, y^{(m)}) \right}$$

L'equazione del modello è definita come: $$f_{w,b}(x) = w \cdot x + b$$

Dove:

  • $w$ (Weight o coefficiente angolare): quantifica l'incremento di potenza elettrica prodotta per ogni unità addizionale di irraggiamento solare.
  • $b$ (Bias o intercetta): modella la baseline del sistema (produzione a irraggiamento nullo o assorbimento parassita dei trasformatori).
Guarda la video-lezione: Meccanica della regressione lineare, superfici di errore e discesa del gradiente.

La Funzione di Costo: Mean Squared Error (MSE)

Tra le infinite rette generabili al variare di $w$ e $b$, quale descrive in modo ottimale la distribuzione dei dati? Per quantificare oggettivamente l'errore del modello, definiamo la Funzione di Costo MSE (Mean Squared Error):

$$J(w, b) = \frac{1}{2m} \sum_{i=1}^{m} \left( f_{w,b}(x^{(i)}) - y^{(i)} \right)^2 = \frac{1}{2m} \sum_{i=1}^{m} \left( (w \cdot x^{(i)} + b) - y^{(i)} \right)^2$$

Proprietà di Convessità e Topologia Parabolica

Elevare l'errore residuo al quadrato garantisce due proprietà matematiche cruciali:

  1. Penalizzazione Quadratica: Errori grandi vengono penalizzati in modo esponenzialmente più severo rispetto a piccole oscillazioni.
  2. Convessità Globale: La funzione $J(w,b)$ è una forma quadratica strettamente definita positiva. La matrice Hessiana di $J$ è sempre semidefinita positiva per ogni coppia di parametri $(w,b)$. Non esistono minimi locali né punti di sella ingannevoli: la superficie è una "ciotola" tridimensionale con un unico minimo globale assoluto.
🎧 Podcast: Come le macchine imparano a ragionare Spiegazione concettuale sulla convergenza numerica e l'interpretazione geometrica dell'errore.

L'Algoritmo di Discesa del Gradiente

Come trova il calcolatore la combinazione ottima $(w^, b^)$ senza ricorrere a calcoli combinatori esaustivi? Tramite la Discesa del Gradiente (Gradient Descent).

Diagramma della superficie della funzione di costo e discesa del gradiente
Superficie convessa 3D e curve di livello: traiettoria della discesa verso il minimo globale.

Partendo da valori iniziali arbitrari $(w_0, b_0)$, l'algoritmo compie passi proporzionali all'opposto del gradiente $\nabla J(w,b)$:

$$\begin{aligned} w &:= w - \alpha \frac{\partial J(w,b)}{\partial w} \ b &:= b - \alpha \frac{\partial J(w,b)}{\partial b} \end{aligned}$$

Dove $\alpha > 0$ è l'iperparametro del Learning Rate (tasso di apprendimento).

Calcolo Analitico delle Derivate Parziali

Derivando formalmente la funzione di costo $J(w,b)$ rispetto a ciascun parametro:

$$\frac{\partial J(w,b)}{\partial w} = \frac{1}{m} \sum_{i=1}^{m} \left( f_{w,b}(x^{(i)}) - y^{(i)} \right) x^{(i)}$$

$$\frac{\partial J(w,b)}{\partial b} = \frac{1}{m} \sum_{i=1}^{m} \left( f_{w,b}(x^{(i)}) - y^{(i)} \right)$$

Regola dell'Aggiornamento Simultaneo:
È fondamentale calcolare simultaneamente i gradienti per $w$ e $b$ prima di aggiornare i valori dei parametri:

temp_w = w - alpha * dj_dw
temp_b = b - alpha * dj_db
w = temp_w
b = temp_b

Regressione Multivariata e Vettorizzazione SIMD

Nei contesti reali, il fenomeno da prevedere dipende da $n$ indicatori simultanei. Consideriamo ad esempio la stima del degrado della capacità residua ($y$ in $\text{Ah}$) di celle agli ioni di litio per veicoli elettrici:

  • $x_1$: Numero totale di cicli completi di ricarica.
  • $x_2$: Temperatura media di esercizio ($^\circ\text{C}$).
  • $x_3$: Picco massimo di corrente di ricarica rapida ($\text{A}$).
  • $x_4$: Resistenza interna ohmica stimata ($\text{m}\Omega$).

Il modello multivariato si esprime come combinazione lineare vettoriale: $$f_{\mathbf{w},b}(\mathbf{x}) = \mathbf{w} \cdot \mathbf{x} + b = w_1 x_1 + w_2 x_2 + \dots + w_n x_n + b$$

Efficienza SIMD con NumPy

Grazie alle istruzioni vettoriali hardware dei processori moderni (AVX-512, ARM Neon), l'implementazione matriciale sostituisce i cicli iterativi lenti:

import numpy as np

# Calcolo vettorizzato su milioni di campioni
def compute_predictions(X, w, b):
    """
    X: Matrice dei dati (m campioni, n feature)
    w: Vettore dei pesi (n feature,)
    b: Scalare del bias
    """
    return np.dot(X, w) + b

Standardizzazione Z-Score delle Feature

Se una variabile oscilla tra $0$ e $2000$ (es. irraggiamento solare) e un'altra varia tra $0$ e $0.05$ (es. resistenza ohmica), la superficie della funzione di costo si deforma in un ellissoide schiacciato. Il gradiente rimbalzerà lateralmente in modo instabile, richiedendo un learning rate piccolissimo.

Applicando la Standardizzazione Z-Score, ogni feature viene normalizzata a media zero e varianza unitaria:

$$x_j' = \frac{x_j - \mu_j}{\sigma_j} \quad \text{con } \mu_j = \frac{1}{m}\sum_{i=1}^m x_j^{(i)}, ; \sigma_j = \sqrt{\frac{1}{m}\sum_{i=1}^m (x_j^{(i)} - \mu_j)^2}$$

In questo modo, la superficie di costo torna a essere perfettamente simmetrica e circolare, consentendo alla discesa del gradiente di procedere direttamente verso il minimo.


Laboratorio Pratico Completo in Python

Di seguito il codice sorgente completo ed eseguibile per addestrare un modello di regressione lineare multivariata con discesa del gradiente e standardizzazione:

import numpy as np

# 1. Generazione dataset sintetico industriale (100 campioni, 3 feature)
np.random.seed(42)
m = 100
X_raw = np.random.rand(m, 3) * np.array([1000.0, 50.0, 10.0])
true_w = np.array([0.15, -1.2, 3.4])
true_b = 45.0
y_train = np.dot(X_raw, true_w) + true_b + np.random.randn(m) * 2.0

# 2. Standardizzazione Z-Score
def zscore_normalize(X):
    mu = np.mean(X, axis=0)
    sigma = np.std(X, axis=0)
    X_norm = (X - mu) / sigma
    return X_norm, mu, sigma

X_norm, mu, sigma = zscore_normalize(X_raw)

# 3. Funzione di costo MSE multivariata
def compute_cost_matrix(X, y, w, b):
    m = X.shape[0]
    predictions = np.dot(X, w) + b
    cost = (1.0 / (2.0 * m)) * np.sum((predictions - y) ** 2)
    return cost

# 4. Calcolo matriciale del gradiente
def compute_gradient_matrix(X, y, w, b):
    m, n = X.shape
    predictions = np.dot(X, w) + b
    err = predictions - y
    dj_dw = (1.0 / m) * np.dot(X.T, err)
    dj_db = (1.0 / m) * np.sum(err)
    return dj_dw, dj_db

# 5. Algoritmo di discesa del gradiente
def gradient_descent(X, y, w_init, b_init, alpha, num_iters):
    w = w_init.copy()
    b = b_init
    for i in range(num_iters):
        dj_dw, dj_db = compute_gradient_matrix(X, y, w, b)
        w -= alpha * dj_dw
        b -= alpha * dj_db
        if i % 200 == 0:
            current_cost = compute_cost_matrix(X, y, w, b)
            print(f"Iterazione {i:4d}: Costo = {current_cost:.4f}")
    return w, b

# 6. Addestramento
w_init = np.zeros(X_norm.shape[1])
b_init = 0.0
alpha = 0.1
num_iters = 1000

w_opt, b_opt = gradient_descent(X_norm, y_train, w_init, b_init, alpha, num_iters)

print(f"\nPesi ottimali normalizzati: {w_opt}")
print(f"Bias ottimale: {b_opt:.2f}")

# 7. Predizione su nuovo campione reale
sample_raw = np.array([850.0, 32.0, 7.5])
sample_norm = (sample_raw - mu) / sigma
pred = np.dot(sample_norm, w_opt) + b_opt
print(f"Predizione per il campione: {pred:.2f} kW")
Infografica: Guida visuale al Machine Learning Supervisionato
Riepilogo didattico: regressione, classificazione, confini decisionali e tecniche di ottimizzazione.

Conclusioni e Passi Successivi

La combinazione di formulazione convessa, calcolo analitico delle derivate e vettorizzazione numerica costituisce la spina dorsale di gran parte del machine learning moderno.

Nel prossimo articolo esamineremo la tassonomia completa dei 9 algoritmi di regressione, confrontando modelli ad albero, Random Forests, XGBoost, Support Vector Regression e reti neurali.

FAQ

Perché la funzione di costo della regressione lineare ha un fattore 1/(2m)?

Il fattore 1/m calcola l'errore quadratico medio sulle m osservazioni, mentre la divisione per 2 è una convenienza matematica: quando si calcola la derivata parziale rispetto ai pesi, l'esponente 2 si semplifica perfettamente con il 2 al denominatore.

Perché è fondamentale aggiornare simultaneamente tutti i pesi nella discesa del gradiente?

Se si aggiornasse un peso w e si utilizzasse il suo nuovo valore per calcolare immediatamente il gradiente del bias b, si romperebbe la coerenza geometrica dell'algoritmo, alterando la direzione del gradiente della superficie originale.

Cosa accade se il Learning Rate alpha è troppo grande o troppo piccolo?

Se alpha è eccessivamente piccolo, la discesa del gradiente richiede milioni di iterazioni per convergere; se è troppo grande, l'algoritmo può oltrepassare il minimo, oscillando caoticamente e divergendo verso valori infiniti.

Qual è il vantaggio della standardizzazione Z-Score per le feature?

Quando le feature hanno ordini di grandezza molto differenti, le curve di livello della funzione di costo diventano ellissi schiacciate e il gradiente oscilla perpendicolarmente. Con lo Z-score le curve diventano cerchi concentrici, rendendo la discesa diretta e rapida.

Come funziona la vettorizzazione in NumPy?

Sfruttando le istruzioni hardware SIMD (Single Instruction Multiple Data) delle moderne CPU, NumPy calcola prodotti scalari tra matrici e vettori in parallelo a livello di registri, evitando i lenti cicli for interpretati di Python.

Fonti