Machine Learning Academy · Lezione

KNN per la regressione e i suoi limiti di scalabilità

Applicherà KNeighborsRegressor a un target continuo, quindi misurerà il tempo di previsione su dataset di grandi dimensioni per comprendere il costo d'inferenza O(n) di KNN.

Lezione 4 di 413 passaggi

KNN per la regressione e i suoi limiti di scalabilità è una lezione Machine Learning Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Machine Learning Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Machine Learning Academy include 4 lezioni in totale.

KNN per la regressione: calcolare la media dei vicini

KNN non è limitato alla classificazione: può anche prevedere valori continui. Nella regressione KNN, la previsione per un nuovo punto è la media dei valori target dei suoi k vicini più prossimi. Ad esempio, per prevedere il prezzo di una casa, KNN individua le k case più simili nel set di addestramento e calcola la media dei loro prezzi. In questo modo si ottiene un modello di regressione locale non parametrico, capace di catturare schemi complessi senza assumere una forma funzionale specifica per la relazione tra caratteristiche e target.

import numpy as np

# Training data: house sizes (sqm) -> prices (thousands)
X_train = np.array([[50], [70], [90], [110], [130]])
y_train = np.array([150, 200, 260, 310, 380])

# Query: predict price for 80 sqm house
x_new = np.array([[80]])

# k=3: find 3 nearest neighbors
dists = np.abs(X_train - x_new).flatten()
nearest_idx = np.argsort(dists)[:3]
neighbor_prices = y_train[nearest_idx]

prediction = neighbor_prices.mean()
print('Neighbor prices:', neighbor_prices)
print('KNN regression prediction:', prediction)

KNeighborsRegressor in scikit-learn

KNeighborsRegressor di scikit-learn implementa KNN per target continui con la stessa API del classificatore. Supporta gli stessi parametri: n_neighbors, metric, weights e algorithm. La regressione pesata sulla distanza (weights='distance') è spesso vantaggiosa: i vicini più prossimi contribuiscono maggiormente al valore previsto rispetto a quelli più lontani, il che è particolarmente utile ai margini della distribuzione dei dati di addestramento, dove un vicino distante potrebbe introdurre un bias significativo.

from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import numpy as np

X, y = fetch_california_housing(return_X_y=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)

pipe = Pipeline([
    ('sc', StandardScaler()),
    ('knn', KNeighborsRegressor(n_neighbors=10, weights='distance'))
])
pipe.fit(X_tr, y_tr)
rmse = mean_squared_error(y_te, pipe.predict(X_te), squared=False)
print('RMSE:', rmse.round(3))

Scegliere k per la regressione

La stessa logica bias-varianza si applica alla regressione KNN: k piccolo = varianza elevata, previsioni irregolari; k grande = bias elevato, previsioni eccessivamente smussate. Può visualizzare questo effetto tracciando la funzione prevista su un intervallo di input unidimensionale. Con k=1, la linea delle previsioni raggiunge il valore esatto di ciascun punto di addestramento. All'aumentare di k, la linea diventa più uniforme. Il valore ottimale di k minimizza l'RMSE (o MAE) calcolato con la convalida incrociata. Nella regressione non esiste il problema della risoluzione dei pareggi, quindi anche i valori pari di k vanno bene.

from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

best_k, best_score = 1, float('inf')

for k in range(1, 31):
    pipe = Pipeline([
        ('sc', StandardScaler()),
        ('knn', KNeighborsRegressor(n_neighbors=k))
    ])
    scores = -cross_val_score(pipe, X_tr, y_tr, cv=5, scoring='neg_root_mean_squared_error')
    mean_rmse = scores.mean()
    if mean_rmse < best_score:
        best_score, best_k = mean_rmse, k

print(f'Best k={best_k} with CV RMSE={best_score:.3f}')

Tempo di previsione di KNN: O(N * d) per query

A differenza dei modelli parametrici addestrati (regressione lineare, reti neurali), che effettuano previsioni in tempo O(d) usando i parametri memorizzati, KNN deve analizzare tutti i punti di addestramento al momento dell'inferenza. Ogni previsione richiede il calcolo delle distanze rispetto a ogni campione di addestramento: un'operazione O(N * d). Per N=1,000,000 e d=100, si tratta di 100 milioni di operazioni per previsione. Se ogni operazione richiede 1 millisecondo, una singola previsione richiede 100 secondi. Di conseguenza, il KNN ingenuo è del tutto inadatto ai sistemi di produzione in tempo reale con set di addestramento di grandi dimensioni.

import numpy as np
import time

np.random.seed(42)

for N in [1000, 10000, 100000, 1000000]:
    X_big = np.random.randn(N, 10)
    query = np.random.randn(1, 10)
    
    start = time.time()
    dists = np.linalg.norm(X_big - query, axis=1)
    _ = np.argsort(dists)[:5]
    elapsed = time.time() - start
    
    print(f'N={N:>8}: {elapsed*1000:.1f} ms')
# Prediction time scales linearly with N

Vicini più prossimi approssimati: KD-Tree e Ball Tree

Scikit-learn offre due strutture di indicizzazione spaziale per velocizzare la ricerca dei vicini. Un KD-Tree suddivide ricorsivamente lo spazio delle caratteristiche lungo la dimensione con la varianza maggiore, consentendo ricerche dei vicini in O(log N) per dati a bassa dimensionalità. Un Ball Tree suddivide i dati in ipersfere annidate, risultando più efficiente per dati ad alta dimensionalità o con metriche non euclidee. Entrambe riducono significativamente il tempo medio di previsione. Imposti il parametro algorithm di KNeighborsClassifier su 'kd_tree', 'ball_tree' o 'auto' (sceglie scikit-learn).

from sklearn.neighbors import KNeighborsClassifier
import time, numpy as np

X = np.random.randn(50000, 5)
y = (X[:, 0] > 0).astype(int)

algorithms = ['brute', 'kd_tree', 'ball_tree']
for alg in algorithms:
    knn = KNeighborsClassifier(n_neighbors=5, algorithm=alg)
    knn.fit(X, y)
    start = time.time()
    knn.predict(X[:100])
    print(f'{alg:10}: {(time.time()-start)*1000:.1f} ms for 100 predictions')

Requisiti di memoria di KNN

KNN deve conservare in memoria l'intero set di addestramento in ogni momento, perché le previsioni richiedono l'accesso ai campioni di addestramento. Per N=1 milione di campioni con d=100 caratteristiche float64, la sola matrice dei dati richiede 800 MB di RAM. Con N=10 milioni, sono 8 GB: una quantità eccessiva per molti ambienti di distribuzione. I modelli parametrici, come la regressione lineare o le reti neurali, comprimono N campioni in un numero fisso di parametri, risultando molto più efficienti in termini di memoria durante l'inferenza. Il costo in memoria di KNN è O(N * d), indipendentemente dalla complessità del problema.

import numpy as np

def memory_mb(N, d, dtype=np.float64):
    bytes_per_value = np.dtype(dtype).itemsize
    total_bytes = N * d * bytes_per_value
    return total_bytes / (1024**2)

for N in [1000, 10000, 100000, 1000000]:
    mb = memory_mb(N, d=100)
    print(f'N={N:>8}, d=100: {mb:.1f} MB')

# 1,000:      0.8 MB (fine)
# 1,000,000: 762.9 MB (borderline)
# A linear model: same O(d) parameters regardless of N

FAISS: vicini più prossimi approssimati su larga scala

Per i casi d'uso di produzione su larga scala, le librerie per la ricerca dei vicini più prossimi approssimati (ANN) riducono drasticamente il tempo di ricerca, al costo di non individuare occasionalmente il vero vicino più prossimo. FAISS (Facebook AI Similarity Search) può interrogare un miliardo di vettori in pochi millisecondi usando strutture di indicizzazione accelerate dalla GPU. Annoy (Spotify) costruisce una foresta di alberi a proiezione casuale per le ricerche approssimate. I grafi HNSW (Hierarchical Navigable Small World) consentono query in meno di un millisecondo. Queste librerie vengono usate nei sistemi di raccomandazione e nella ricerca semantica su scala di produzione.

# Conceptual FAISS usage (requires: pip install faiss-cpu)
import numpy as np

# import faiss  # not available in standard envs

# Conceptual workflow:
# N = 1_000_000  # 1 million vectors
# d = 128        # dimensionality

# X = np.random.randn(N, d).astype('float32')
# index = faiss.IndexFlatL2(d)   # Exact L2 search
# index.add(X)                   # Index all vectors

# Query 10 vectors
# query = np.random.randn(10, d).astype('float32')
# distances, indices = index.search(query, k=5)
# print(indices.shape)  # (10, 5)

Quando KNN è pratico e quando usare alternative

KNN è pratico quando: N < 100,000, le previsioni vengono eseguite in batch (non in tempo reale) ed è necessaria l'interpretabilità (può mostrare gli esempi simili effettivi). KNN presenta difficoltà quando: N è molto grande, sono necessarie previsioni in tempo reale (latenza <100ms) oppure lo spazio delle caratteristiche è ad alta dimensionalità (>50 caratteristiche). Tra le alternative migliori per valori elevati di N figurano Random Forest e Gradient Boosting per i dati tabulari, e le reti neurali per immagini e testo. KNN rimane un solido modello di riferimento per i sistemi di raccomandazione e il rilevamento delle anomalie quando il dataset entra comodamente in memoria.

# Decision guide: KNN vs alternatives

def should_use_knn(N, d, latency_ms_required):
    if N > 500_000:
        return 'Too large for KNN -- use Random Forest or XGBoost'
    elif d > 50:
        return 'Too high-dimensional -- apply PCA first or use tree models'
    elif latency_ms_required < 50:
        return 'Too strict latency -- use parametric model'
    else:
        return 'KNN is suitable as a baseline'

print(should_use_knn(10000, 10, 500))   # KNN is suitable
print(should_use_knn(1000000, 10, 500)) # Too large
print(should_use_knn(10000, 100, 500))  # Too high-dimensional

Profilazione di KNN e della regressione lineare

Confrontare KNN e la regressione lineare sullo stesso problema di regressione mette in evidenza il compromesso in termini di scalabilità. La regressione lineare viene addestrata in pochi secondi indipendentemente da N (addestramento O(N*d^2), ma previsione O(d)). L'addestramento di KNN è istantaneo (non richiede calcoli), ma la previsione scala con N. KNN è quindi un modello a calcolo differito: sostiene tutti i costi al momento della previsione. Per un'attività di previsione batch eseguita una sola volta su 100.000 righe, KNN può essere accettabile. Per un'API che gestisce 1000 richieste al secondo, la regressione lineare o una rete neurale è più veloce di diversi ordini di grandezza.

import numpy as np
import time
from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression

X = np.random.randn(50000, 10)
y = X[:, 0] * 3 + np.random.randn(50000)
X_test = np.random.randn(1000, 10)

knn = KNeighborsRegressor(n_neighbors=5)
lr  = LinearRegression()

knn.fit(X, y); lr.fit(X, y)

for name, model in [('KNN', knn), ('LinearReg', lr)]:
    t0 = time.time()
    model.predict(X_test)
    dt = (time.time() - t0) * 1000
    print(f'{name}: {dt:.1f} ms for 1000 predictions')

Usare gli output di KNN nelle pipeline

Anche quando KNN è troppo lento per essere usato direttamente in produzione, le distanze restituite possono fungere da caratteristiche informative per altri modelli. Ad esempio, calcolare la distanza media dai k vicini di addestramento più prossimi per ogni punto di test crea una singola caratteristica che misura quanto il punto sia insolito. I punti insoliti (lontani dai propri vicini) sono potenziali anomalie. Questo approccio, che usa KNN come estrattore di caratteristiche anziché come predittore finale, consente di sfruttare le informazioni sui vicini più prossimi all'interno di modelli ensemble veloci.

from sklearn.neighbors import KNeighborsClassifier
import numpy as np

X_train = np.random.randn(500, 10)
y_train = (X_train[:, 0] > 0).astype(int)
X_test  = np.random.randn(50, 10)

knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

# Extract neighbor distances as an anomaly score
dists, _ = knn.kneighbors(X_test)
avg_dist = dists.mean(axis=1)

print('Average neighbor distances (anomaly score):')
print(avg_dist.round(2))
# High values indicate potential anomalies

Riduzione della dimensionalità prima di KNN

Per rendere KNN pratico sui dati ad alta dimensionalità, applichi PCA prima di KNeighborsRegressor, così da ridurre la dimensionalità preservando la maggior parte della varianza. In questo modo affronta simultaneamente due problemi: riduce il tempo di previsione (meno dimensioni = calcolo delle distanze più rapido) e attenua la maledizione della dimensionalità (le distanze diventano più significative nello spazio a dimensionalità ridotta). PCA + KNN all'interno di un'unica Pipeline mantiene la pre-elaborazione priva di data leakage durante la convalida incrociata.

from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import cross_val_score
import numpy as np

X_high_d = np.random.randn(1000, 100)  # 100 features
y = X_high_d[:, :5].sum(axis=1)       # Only first 5 matter

pipe = Pipeline([
    ('sc', StandardScaler()),
    ('pca', PCA(n_components=10)),      # Reduce to 10 components
    ('knn', KNeighborsRegressor(n_neighbors=5))
])

scores = -cross_val_score(pipe, X_high_d, y, cv=5,
                          scoring='neg_root_mean_squared_error')
print('CV RMSE with PCA:', scores.mean().round(3))

Verifica rapida

Verifichi la Sua comprensione dei concetti di Machine Learning con Python presentati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato: come la regressione KNN prevede target continui calcolando la media dei valori dei vicini, il fondamentale costo di previsione O(N * d) che limita la scalabilità di KNN e come KD-Tree, Ball Tree e FAISS velocizzano la ricerca dei vicini per i dataset più grandi. Nella prossima lezione esploreremo gli alberi decisionali, un approccio fondamentalmente diverso che apprende regole esplicite attraverso il partizionamento ricorsivo dei dati.

Gratis per iniziare

Impara Python con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
30
Lezioni
120

Domande Frequenti

La lezione «KNN per la regressione e i suoi limiti di scalabilità» è gratuita?

Sì — il testo completo di «KNN per la regressione e i suoi limiti di scalabilità» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Machine Learning Academy, passa a CoddyKit PRO. Il corso Machine Learning Academy include 4 lezioni in totale.

Cosa imparerò in «KNN per la regressione e i suoi limiti di scalabilità»?

Applicherà KNeighborsRegressor a un target continuo, quindi misurerà il tempo di previsione su dataset di grandi dimensioni per comprendere il costo d'inferenza O(n) di KNN. Eserciti Machine Learning Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Machine Learning Academy?

Non è richiesta alcuna esperienza precedente. Machine Learning Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «KNN per la regressione e i suoi limiti di scalabilità»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Machine Learning Academy?

Sì. Ogni lezione Machine Learning Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Come funziona KNN: distanza, vicini e voti
  2. Scegliere k: metodo del gomito e curve di validazione
  3. Metriche di distanza: euclidea, Manhattan e Minkowski
  4. KNN per la regressione e i suoi limiti di scalabilità
← Torna a Machine Learning Academy