Machine Learning · Insper

k-Nearest Neighbors

Fundamental approach

How k-NN decides, and what it needs to decide well

Two ways to use the training data

Logistic regression

  • reads the whole training set
  • sums it up in \(d + 1\) numbers
  • draws a straight line
  • throws the rows away

k-NN

  • keeps every row
  • for a new point, finds the \(k\) most similar
  • they vote
  • the boundary takes any shape the data suggests
The k-NN "model" is the training data itself.

The path of this part

  1. Neighbors votedistance to every row; the k nearest vote; fit only stores
  2. Scale firstunits decide the distance; the scaler goes inside a pipeline
  3. Choose k, test oncebias and variance; validation chooses, the test confirms
Each stage answers one question: how does k-NN decide? → what makes "near" fair? → how many neighbors should vote?
Stage 1 · How does k-NN decide?

A problem: will they like the series?

  • A streaming service is about to recommend a series that mixes action and documentary.
  • For 10 subscribers who have watched it, we know their weekly hours of action films and of documentaries, and whether they liked it.
  • A new subscriber watches 4 h of action and 4 h of documentaries.

"Similar" becomes a number: the Euclidean distance to the new subscriber.

\[ d = \sqrt{(\text{action} - 4)^2 + (\text{docs} - 4)^2} \]

First: measure the distance to each one

ranknameactiondocsliked?\(d\)
1Ana54yes1.000
2Bruno25no2.236
3Carla66no2.828
4Davi47yes3.000
5Elisa73yes3.162
6–10Felipe, Gabi, Hugo, Iara, João: from 3.606 to 5.831

Ana: \(\sqrt{(5-4)^2 + (4-4)^2} = 1\). Sort from nearest to farthest.

Then: the \(k\) nearest vote

The 10 subscribers and the circles holding the 1, 3 and 5 nearest neighbors of the new one
  • \(k = 1\): Ana → liked (1/1)
  • \(k = 3\): 1 yes × 2 no → not liked (1/3)
  • \(k = 5\): 3 yes × 2 no → liked (3/5)
Same data, same query, three answers. \(k\) is the main decision k-NN leaves to you.

The algorithm in three steps

  1. compute the distance from the query \(x\) to every training point;
  2. keep the \(k\) closest, the neighborhood \(N_k(x)\);
  3. predict the most frequent class among them.

The "probability" of class \(c\) is just the fraction of neighbors in it:

\[ \hat{p}_c(x) = \frac{1}{k} \sum_{i \in N_k(x)} \mathbb{1}[y_i = c] \]

In the example, predict_proba gives 1.000, 0.333 and 0.600 for \(k\) = 1, 3 and 5.

What fit stores

logistic regressionk-NN
fit produces\(d + 1\) numbersa copy of the training table
boundarya straight line (hyperplane)any shape
cost of fititerative optimizationalmost none: store
cost of predicting\(d\) multiplications\(n\) distances
explains withone coefficient per feature"these are the most similar cases"
A lazy learner: the real work is left for prediction time.
Stage 2 · What makes "near" fair?

Units decide the distance

A new travel-agency customer: R$ 5,000 per month, 25 years old. Will they buy the backpacking package?

customerincomeagebought?raw \(d\)standardized \(d\)
Rita5,20061no203.22.57
Otávio5,70026yes700.00.32
  • Raw: \(\sqrt{200^2 + 36^2} = 203.2\). The 36 years add only 3.2, because one year weighs as much as one real. 1-NN: Rita → no.
  • Standardized (each feature ÷ its standard deviation): the nearest is Otávio → yes.

The same case, as a figure

In raw units the circle around the new customer is a vertical band; standardized, the nearest neighbor is Otávio

In raw units, a "circle" of radius 203 is a band R$ 203 wide on each side that contains every age.

On a real dataset: the wines

Standard deviation of the wine features:

proline314.91
magnesium14.28
alcalinity_of_ash3.34
color_intensity2.32

Test accuracy of 5-NN (54 wines):

no scaling0.722
StandardScaler0.963
MinMaxScaler0.963
Unscaled, 5-NN classifies wines by one column out of 13: proline.

Rule: scale, always inside a pipeline


from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

knn = make_pipeline(StandardScaler(),
                    KNeighborsClassifier(n_neighbors=5))
knn.fit(X_train, y_train)
  
  • the scaler learns means and standard deviations from training rows only;
  • validation and test get the same transformation, without refitting;
  • exception: features that already share one unit (the pixels of the advanced lab).
Stage 3 · How many neighbors should vote?

\(k\) is the complexity knob

k-NN decision regions on the moons for k = 1, 15 and 451

\(k = 1\): islands around each point (train 100%, validation 85.0%) · \(k = 15\): follows the moons (89.5%) · \(k = 451\): almost a straight line (76.5%)

small \(k\) = complex model (high variance) · large \(k\) = simple model (high bias)

Choose \(k\) on validation

Training and validation accuracy as a function of k
  • 100% training accuracy at \(k = 1\) is by construction: each point is its own neighbor.
  • Plateau: from \(k = 7\) to ~140, validation stays between 88.5% and 91.0%.
  • Large \(k\): training and validation fall together → too simple.

The protocol: each split has a job

training · 600

the rows k-NN stores, and the ones that vote

validation · 200

chooses \(k\)

\(k = 73\)
validation 91.0%

test · 200

measured once, at the end, after refitting on training + validation

90.0%
The test is a point lower: \(k = 73\) was chosen because it did well on those 200 rows, so part of its 91.0% is luck. Choosing \(k\) on the test set would destroy that honesty.

Simulator: the vote of the neighbors

Predict. The ★ sits on the boundary. Sweep \(k\) from 1 to 25 without moving it: does its class stay the same? Then drag it through the overlap: which \(k\) flips more?

Summary: the k-NN recipe

  1. Pipeline with scaler + k-NN.
  2. Split before looking: training, validation, test.
  3. Sweep odd \(k\) on validation (with two classes, an odd \(k\) never ties).
  4. Refit on training + validation and measure the test set once.
Next: the fundamental lab, with wines.
Fundamental lab · about 35 min, in pairs

Wines: steps 1 to 5

  • Open the notebook on Colab (in Portuguese): nothing to download. Save a copy to your Drive to keep your answers.
  • In each step, answer the Predict before running.
  • Simulations: the unit of proline (step 3) and the vote changes with \(k\) (step 4).
  • Compare with the output and the reading on the lesson page.

Pace: steps 1 to 3 in 15 min; steps 4 and 5 in 20 min.