CodeBlanks Logo Icon CodeBlanks

KMeans algorithm

KMeans initializes k centroids randomly and updates them iteratively.

Lloyd's algorithm

The standard solution for KMeans is Lloyd's algorithm. At each step inertia either drops or stays the same; that's why the algorithm always converges, but the solution it finds may be a local optimum.

def kmeans_adim(X, merkez):
    d = ((X[:, None, :] - merkez[None, :, :]) ** 2).sum(axis=2)
    etiket = d.argmin(axis=1)                                   # assignment
    yeni = np.array([X[etiket == k].mean(axis=0) for k in range(len(merkez))])
    return etiket, yeni                                         # update

n_iter_ shows how many rounds were needed to converge; max_iter is the upper limit.

Distance matrix with broadcasting

The expression X[:, None, :] - merkez[None, :, :] broadcasts the shapes (n, 1, d) and (1, k, d) to (n, k, d); summing along the last axis gives each example's squared distance to each centre. It is loop-free and fast:

print(X.shape, merkez.shape)               # (500, 2) (5, 2)
d = ((X[:, None, :] - merkez[None, :, :]) ** 2).sum(axis=2)
print(d.shape)                             # (500, 5)

You'll meet the same pattern in KNN, the RBF kernel and the attention computation.

Practice KMeans by hand

Taking the first 5 examples as the initial centres, run Lloyd's algorithm for 10 rounds (assignment + update). Print the final inertia (sum of squared distances to the nearest centre) as an integer with round.

Starter code

model.py
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

X, _ = make_blobs(n_samples=500, centers=5, cluster_std=1.0, random_state=3)

# buraya yaz

Sample solution

model.py
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

X, _ = make_blobs(n_samples=500, centers=5, cluster_std=1.0, random_state=3)

merkez = X[:5].copy()
for _ in range(10):
    d = ((X[:, None, :] - merkez[None, :, :]) ** 2).sum(axis=2)
    etiket = d.argmin(axis=1)
    merkez = np.array([X[etiket == k].mean(axis=0) for k in range(5)])
d = ((X[:, None, :] - merkez[None, :, :]) ** 2).sum(axis=2)
print(round(d.min(axis=1).sum()))

Expected output

1083

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 How does inertia change from one round to the next in Lloyd's algorithm?

  • It changes randomly
  • It is always constant
  • It decreases or stays the same Correct answer
  • It always increases

Explanation: Neither the assignment nor the update step can increase the sum of squared distances.

2 Assign each example to the nearest centre.

Question
etiket = d.____(axis=1)
Answer
etiket = d.argmin(axis=1)

Explanation: argmin(axis=1) gives the column of the smallest distance in each row.

3 Match the pieces.

Term Meaning
assignment d.argmin(axis=1)
update Cluster mean
n_iter_ Rounds to converge
local optimum Lloyd's limitation

All lessons in Intermediate