Machine Learning course menu
k-Nearest Neighbours (KNN) Classifier in Python
Learn the k-nearest neighbours algorithm: predicting from the closest training examples, distance metrics, choosing k, why feature scaling is essential, KNN for regression, and its strengths and limits.
On this page
k-nearest neighbours (KNN) is the simplest classifier to understand: to classify a new example, find the k training examples closest to it and take a majority vote of their labels. There is no real "training" step; the model just stores the data.
How it works#
import numpy as np
# fruits described by (weight in grams, colour score from 0 = green to 10 = red)
X = np.array([[150, 8], [170, 9], [140, 7], [120, 2], [130, 3], [110, 1]])
labels = np.array(["apple", "apple", "apple", "lime", "lime", "lime"])
new = np.array([135, 6])
distances = np.sqrt(((X - new) ** 2).sum(axis=1)) # Euclidean distance
nearest = np.argsort(distances)[:3] # k = 3
print(distances.round(1))
print(labels[nearest], "->", max(set(labels[nearest]), key=list(labels[nearest]).count))[15.1 35.1 5.1 15.5 5.8 25.5]
['apple' 'lime' 'apple'] -> appleThe three closest fruits vote, and the majority label wins. scikit-learn does the same with efficient search structures.
KNN in scikit-learn#
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_wine(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y)
unscaled = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train)
scaled = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5)).fit(X_train, y_train)
print("without scaling:", round(unscaled.score(X_test, y_test), 3))
print("with scaling: ", round(scaled.score(X_test, y_test), 3))without scaling: 0.722
with scaling: 0.963Why scaling matters so much#
Distances add up differences across features. In the wine data, one feature (proline) has values in the hundreds or thousands while others are below 1, so without scaling the distance is almost entirely proline and the other 12 features are ignored. StandardScaler puts every feature on the same scale, which is why accuracy jumps. Always scale features for KNN (and for any distance-based method).
Choosing k#
A small k follows the training data closely (and its noise); a large k smooths decisions but can blur real boundaries:
from sklearn.datasets import load_wine
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_wine(return_X_y=True)
for k in [1, 3, 5, 11, 25, 51]:
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=k))
scores = cross_val_score(model, X, y, cv=5)
print(f"k={k:>2}: mean accuracy {scores.mean():.3f}")k= 1: mean accuracy 0.950
k= 3: mean accuracy 0.944
k= 5: mean accuracy 0.949
k=11: mean accuracy 0.955
k=25: mean accuracy 0.961
k=51: mean accuracy 0.961Cross-validation averages the score over five different splits, a more reliable way to pick k than a single test set. Odd values of k avoid ties in two-class problems.
KNN for regression#
KNeighborsRegressor predicts the average of the neighbours' values:
import numpy as np
from sklearn.neighbors import KNeighborsRegressor
sizes = np.array([[600], [750], [900], [1100], [1300], [1500]])
prices = np.array([40, 52, 63, 80, 95, 110])
model = KNeighborsRegressor(n_neighbors=2).fit(sizes, prices)
print(model.predict([[1000], [1600]]))[ 71.5 102.5]For 1,600 sq ft the prediction is the average of the two largest houses: KNN can never predict beyond the range of values it has seen.
Strengths and limits#
The same "find the most similar items" idea powers recommendation and semantic search, using embeddings as the features.
Common mistakes#
Practice#
Practice exercise
On the iris dataset, use 5-fold cross-validation to compare scaled KNN with k = 1, 7 and 15, and print the mean accuracy for each (3 decimals).
Show hint
Loop over k, build make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=k)) and call cross_val_score(model, X, y, cv=5).mean().
Show solution
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_iris(return_X_y=True)
for k in [1, 7, 15]:
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=k))
print(k, round(cross_val_score(model, X, y, cv=5).mean(), 3))1 0.947
7 0.96
15 0.94Key takeaway#
Next: Decision Trees.
Finished this lesson?