2.4 Algoritmul K-Nearest Neighbors (K-NN)
Cel mai intuitiv algoritm de clasificare: „spune-mi cu cine te asemeni, ca să-ți spun cine ești". Un exemplu nou primește clasa majoritară a celor mai apropiați k vecini.
Teorie
Principiul celor mai apropiați vecini
K-NN nu „învață" nimic la antrenare — memorează pur și simplu datele. La predicție: calculează distanța de la exemplul nou la toate exemplele din train, ia cele mai apropiate k, și votează: clasa majoritară câștigă (la regresie: media valorilor vecinilor).
Alegerea parametrului k
| k mic (1–3) | k mare (31+) |
|---|---|
| granițe de decizie foarte fine; sensibil la zgomot → risc de overfitting | granițe foarte netede; ignoră structura locală → risc de underfitting |
Practic: se încearcă mai multe valori impare (evită egalitatea la vot) și se alege cea cu cel mai bun scor pe validare sau prin cross-validation.
Alegerea metricii de distanță
Euclidiană (implicit): √(Σ(aᵢ−bᵢ)²) — linia dreaptă dintre puncte.
Manhattan: Σ|aᵢ−bᵢ| — distanța „pe străzi", mai robustă la outlieri.
Minkowski: generalizarea ambelor (parametrul p: p=2 euclidiană, p=1 Manhattan).
Utilizarea scikit-learn
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score
sc = StandardScaler()
X_tr = sc.fit_transform(X_train)
for k in [1, 3, 5, 7, 11, 21]:
model = KNeighborsClassifier(n_neighbors=k, metric="euclidean")
scoruri = cross_val_score(model, X_tr, y_train, cv=5)
print(f"k={k}: {scoruri.mean():.3f}")
# weights="distance": vecinii mai apropiați au vot mai greu
model = KNeighborsClassifier(n_neighbors=5, weights="distance")
Problemă rezolvată: Iris cu K-NN
Aceeași problemă din lecția 1.4, acum cu K-NN și alegerea sistematică a lui k — ca să compari direct cele două modele.
Soluția pas cu pas
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")
feats = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
sc = StandardScaler()
X = sc.fit_transform(train[feats])
y = train["species"]
# alegem k prin cross-validation
best_k, best_s = None, 0
for k in range(1, 22, 2):
s = cross_val_score(KNeighborsClassifier(n_neighbors=k), X, y, cv=5).mean()
if s > best_s:
best_k, best_s = k, s
print("Cel mai bun k:", best_k, "scor:", round(best_s, 3))
model = KNeighborsClassifier(n_neighbors=best_k).fit(X, y)
pred = model.predict(sc.transform(test[feats]))
pd.DataFrame({"SampleID": test["SampleID"], "species": pred}) \
.to_csv("submission.csv", index=False)
Probleme propuse
Ai punctele A(1,1)→roșu, B(2,2)→roșu, C(5,5)→albastru, D(6,5)→albastru. Ce clasă primește punctul (4,4) cu k=1 și cu k=3, folosind distanța euclidiană? Calculează manual.
Pe orice dataset de clasificare, desenează acuratețea pe train și pe validare pentru k de la 1 la 50. Identifică zonele de overfitting și underfitting pe grafic.
Compară euclidiană vs Manhattan pe un dataset cu outlieri adăugați artificial. Care rezistă mai bine și de ce?
Rezolvă problema vinului cu K-NN + scalare + alegerea lui k și compară cu soluția de referință.