K-means
Objectives
Section titled “Objectives”After this lesson, you will be able to:
- Understand the fundamentals of unsupervised learning and the clustering problem.
- Master the inertia objective function and Lloyd’s algorithm with its two alternating phases: cluster assignment and centroid update.
- Explain why random initialization is prone to poor local minima and how K-means++ resolves this vulnerability.
- Select an appropriate number of clusters using the Elbow method and Silhouette scores.
Prerequisites
Section titled “Prerequisites”- Introduction to Machine Learning, in Machine Learning (Learned)
- Linear Algebra, in Math for AI(Not translated yet) (Learned)
Intuition
Section titled “Intuition”Imagine you manage a retail fashion chain with tens of thousands of customers. For each customer, you record monthly visit frequency and average transaction spending. You want to group customers to tailor marketing promotions, but possess no predefined labels such as “VIP”, “Budget”, or “Bargain Hunter”.
This is the domain of unsupervised learning and clustering: grouping similar observations together such that points within the same group are as close as possible, while separate groups remain well separated.
Clustering Around Centroids
Section titled “Clustering Around Centroids”A natural way to define a cluster is to appoint a representative center—a centroid. Suppose you want to partition data into clusters:
- Place 3 candidate centroids at arbitrary locations across the store space.
- Each customer observes the centroids and associates with the one closest to them, forming 3 temporary clusters.
- Each centroid steps to the center of gravity (arithmetic mean) of all customers assigned to it.
- As centroids relocate, some customers find another centroid now closer, switching groups accordingly.
This process alternates. After several rounds, assignments stabilize and centroids stop moving. The algorithm has converged.
36-Point Sample Dataset
Section titled “36-Point Sample Dataset”Our working dataset comprises 36 2D points arranged into 3 natural clusters (12 points each) centered near , , and . Representative coordinates include:
| Sample Index | Coordinates | Visual Region |
|---|---|---|
| 0 | Bottom-left cluster | |
| 1 | Bottom-left cluster | |
| 12 | Bottom-right cluster | |
| 24 | Upper cluster |
The human eye easily detects three isolated clusters. A computer, however, sees only an unlabeled coordinate matrix.
The objective is: partition unlabeled observations into clusters minimizing the total sum of squared Euclidean distances from each point to its assigned cluster centroid.
Theory
Section titled “Theory”Problem Formulation
Section titled “Problem Formulation”Given samples without target labels , we partition them into disjoint subsets:
Each cluster possesses centroid . Let denote the cluster assigned to sample .
The objective to minimize is total squared distance from points to centroids, termed inertia or distortion:
where:
- represents sample assignments.
- denotes the set of centroids.
Simultaneous optimization over both discrete assignments and continuous coordinates is NP-hard. We turn to an alternating iterative heuristic: Lloyd’s algorithm (commonly referred to as standard K-means).
Lloyd’s Algorithm: Two Alternating Steps
Section titled “Lloyd’s Algorithm: Two Alternating Steps”Lloyd’s algorithm alternates between optimizing assignments and centroids :
Initialize k candidate centroids μ_1, ..., μ_kRepeat until convergence: Step 1 (Assignment): Fix μ, find optimal assignments c Step 2 (Update): Fix c, find optimal centroids μStep 1: Cluster Assignment
Section titled “Step 1: Cluster Assignment”Holding centroids fixed, minimizing decouples across individual samples. Each sample is assigned to its nearest centroid:
Ties are broken arbitrarily. At this step, inertia is guaranteed non-increasing: .
Step 2: Centroid Update
Section titled “Step 2: Centroid Update”Holding assignments fixed, we optimize coordinates to minimize :
Because clusters partition samples independently, we minimize each cluster sum separately. Differentiating with respect to centroid :
Setting the gradient to :
The optimal centroid is the arithmetic mean (centroid) of all samples currently in that cluster. Inertia is again non-increasing: .
Initialization and the K-means++ Algorithm
Section titled “Initialization and the K-means++ Algorithm”Although guaranteed to converge, Lloyd’s algorithm settles at a local minimum dictated heavily by initial centroid placement:
- Poor initial seeds (e.g., placing two initial centroids within a single natural cluster) can split one true cluster while merging two separate clusters.
- Empty clusters () can emerge if an initial centroid attracts no samples.
Strategy 1: Multiple Random Restarts (n_init)
Section titled “Strategy 1: Multiple Random Restarts (n_init)”The simplest mitigation is running Lloyd’s algorithm across multiple independent random initializations (historically n_init=10 in scikit-learn), retaining the solution achieving lowest final inertia .
Strategy 2: The K-means++ Algorithm
Section titled “Strategy 2: The K-means++ Algorithm”Arthur & Vassilvitskii (2007) proposed K-means++, establishing that initial centroids should be seeded as far apart as possible.
K-means++ seeding steps:
- Choose the first centroid uniformly at random from the dataset.
- For each sample , compute its distance to the nearest already chosen centroid:
- Select the next centroid with probability proportional to squared distance:
- Repeat steps 2 and 3 until centroids are chosen.
Samples remote from established centroids exhibit large , drastically boosting their odds of becoming subsequent centroids. This dramatically curbs poor local minima and accelerates convergence. K-means++ is the standard default across modern libraries (init='k-means++').
Choosing Number of Clusters k: Elbow and Silhouette
Section titled “Choosing Number of Clusters k: Elbow and Silhouette”In real applications, ground-truth cluster count is typically unknown. Why not simply select that minimizes inertia ?
Because inertia strictly decreases as increases. At , every sample is its own centroid, achieving trivially. This extreme lacks practical grouping utility. Two quantitative heuristics guide selection:
The Elbow Method
Section titled “The Elbow Method”Plot inertia across . As advances toward the true cluster count, plunges sharply. Once past the true count, additional centroids merely subdivide natural clusters, causing to flatten.
The resulting curve exhibits an angle resembling a bent elbow. The location of this “elbow” provides an intuitive trade-off between compactness and model complexity.
The Silhouette Score
Section titled “The Silhouette Score”When the elbow curve is ambiguous, the Silhouette score (Rousseeuw, 1987) measures cluster quality directly per sample via intra-cluster cohesion and inter-cluster separation.
For each sample :
- Compute : average distance from to all other points within its assigned cluster (intra-cluster cohesion; lower is better):
- Compute : smallest average distance from to all points in any other cluster (nearest-cluster separation; higher is better):
- The Silhouette coefficient of sample is:
Interpretation of :
- : ; the sample is deeply embedded within its cluster, far from neighboring clusters (ideal).
- : ; the sample sits right on the boundary between two clusters.
- : ; the sample is closer to an adjacent cluster than its assigned cluster (misclustered).
The mean Silhouette score across all samples peaks at the optimal cluster count .
Limitations
Section titled “Limitations”- Requires predetermining . K-means cannot automatically deduce intrinsic cluster counts.
- Assumes convex, isotropic clusters. Because it minimizes Euclidean distance, K-means partitions space into linear Voronoi polyhedra. It fails when clusters have non-convex geometries (such as interlocking crescents or concentric circles) or drastically disparate densities.
- Sensitive to outliers. Squaring Euclidean distances gives distant anomalies outsized leverage, pulling centroids away from dense modes.
- Demands feature scaling. If one feature has vastly larger numerical magnitude than others, Euclidean distance is dominated by that single dimension.
Visualization
Section titled “Visualization”The table below traces Lloyd’s algorithm across our 36-sample dataset with 3 initial centroids seeded at samples 2, 20, and 1:
| Iteration | Phase | Inertia | Dynamics Summary |
|---|---|---|---|
| 0 | Init | — | Samples 2, 20, and 1 selected as initial seeds |
| 1 | Assign | 406.70 | Samples assign to nearest candidate centroid |
| 1 | Update | 241.67 | Centroids shift to means of assigned points, dropping |
| 2 | Assign | 230.88 | Boundary samples switch to closer updated centers |
| 2 | Update | 200.73 | Centroids migrate toward natural point cloud centers |
| 3 | Assign | 139.13 | Reassignments induce structural partition shift |
| 3 | Update | 83.62 | Inertia drops sharply as 3 distinct clusters crystallize |
| 4 | Assign | 58.49 | Final boundary points settle |
| 4 | Update | 52.12 | Centroids stabilize at , , |
| 5 | Assign | 52.12 | Zero reassignments occur Convergence reached |
Key takeaways:
- Inertia declines monotonically at each step: from 406.70 to 241.67, 230.88, 200.73, 139.13, 83.62, 58.49, halting at 52.12.
- At iteration 5, assignments are identical to iteration 4, confirming convergence after 4 updates. Final cluster sizes partition evenly: 12, 12, 12.
Code Examples
Section titled “Code Examples”The three cells execute independently.
The first cell implements Lloyd’s algorithm from scratch with NumPy: computing pairwise distance matrices via array broadcasting, updating centroids via conditional cluster means, and terminating when assignments freeze.
Lloyd's algorithm step-by-step with NumPy
import numpy as npfrom sklearn.datasets import make_blobs
# 36 2D points into 3 clusters, rounded to 2 decimals.X, _ = make_blobs(n_samples=36, centers=[[2, 2], [8, 3], [5, 8]], cluster_std=1.0, random_state=10)X = np.round(X, 2)
k = 3mu = X[[2, 20, 1]].copy() # 3 initial centroids: points 2, 20 and 1c = np.full(len(X), -1) # no point assigned yet
for t in range(1, 101): # Assignment step: squared distance from each point to each centroid. d2 = ((X[:, None, :] - mu[None, :, :]) ** 2).sum(axis=2) c_new = d2.argmin(axis=1) # index of closest centroid if np.array_equal(c_new, c): # no point changed cluster: stop break c = c_new J = d2[np.arange(len(X)), c].sum() print(f"iter {t}: assign J = {J:7.2f}", end=" ") # Update step: each centroid becomes the mean of points in its cluster. for j in range(k): if np.any(c == j): # skip empty cluster mu[j] = X[c == j].mean(axis=0) J = ((X - mu[c]) ** 2).sum() print(f"update J = {J:7.2f}")
print(f"converged, sizes = {np.bincount(c, minlength=k)}")print("centroids:")print(np.round(mu, 2))The first run downloads Python, about 10 MB.
The console output matches our theoretical trace: converging in 4 iterations at . Centroids settle at , , and —closely reproducing true data-generation centers , , and .
The second cell uses scikit-learn’s KMeans to illustrate local minimum vulnerabilities: comparing 50 random initializations (init="random") against init="k-means++".
Random initialization, K-means++, and local minimum traps
import numpy as npfrom sklearn.cluster import KMeansfrom sklearn.datasets import make_blobs
X, _ = make_blobs(n_samples=36, centers=[[2, 2], [8, 3], [5, 8]], cluster_std=1.0, random_state=10)X = np.round(X, 2)
def run(init, n_init): # Run KMeans with 50 different seeds, record inertia (J). J = [KMeans(3, init=init, n_init=n_init, random_state=s).fit(X).inertia_ for s in range(50)] J = np.round(J, 2) stuck = np.sum(J > 52.12) # times stuck at J > 52.12 print(f"{init:>9}, n_init={n_init:>2}: min J = {J.min():6.2f}, " f"max J = {J.max():6.2f}, stuck {stuck}/50")
run("random", 1) # 3 random points as centroids, run oncerun("k-means++", 1) # k-means++, run oncerun("random", 10) # random, run 10 times, keep the one with lowest J
# One run that gets stuck: J, cluster sizes and centroids.km = KMeans(3, init="random", n_init=1, random_state=13).fit(X)print(f"seed 13: J = {km.inertia_:.2f}, sizes = {np.bincount(km.labels_)}")print(np.round(km.cluster_centers_, 2))
# Default: init='k-means++', n_init='auto' (1 run with k-means++).km = KMeans(3, random_state=0).fit(X)print(f"default: J = {km.inertia_:.2f}")The first run downloads Python, about 10 MB.
Under single random restarts (init="random", n_init=1), 3 of 50 trials (6%) become trapped in suboptimal local minima with . In contrast, K-means++ achieves the global minimum across all 50/50 trials without trapping.
The third cell sweeps from 1 to 6 using scikit-learn, generating Elbow and Silhouette diagnostic curves.
Selecting k via Elbow and Silhouette methods
import matplotlib.pyplot as pltimport numpy as npfrom sklearn.cluster import KMeansfrom sklearn.datasets import make_blobsfrom sklearn.metrics import silhouette_score
X, _ = make_blobs(n_samples=36, centers=[[2, 2], [8, 3], [5, 8]], cluster_std=1.0, random_state=10)X = np.round(X, 2)
ks = range(1, 7)inertia, sil = [], []for k in ks: km = KMeans(k, n_init=10, random_state=0).fit(X) inertia.append(km.inertia_) # Silhouette requires at least 2 clusters. sil.append(silhouette_score(X, km.labels_) if k > 1 else np.nan) print(f"k = {k}: J = {km.inertia_:6.2f}, silhouette = {sil[-1]:.3f}")
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 3.5))ax1.plot(ks, inertia, "o-")ax1.set_xlabel("k")ax1.set_ylabel("inertia J")ax1.set_title("Elbow")ax2.plot(ks, sil, "o-", color="tab:orange")ax2.set_xlabel("k")ax2.set_ylabel("mean silhouette")ax2.set_title("Silhouette")fig.tight_layout()plt.show()The first run downloads Python, about 10 MB.
Both diagnostics cleanly identify as optimal:
- The Elbow plot displays an inflection point at , after which reductions in plateau.
- The Silhouette curve achieves an isolated maximum at with score .
Common Pitfalls
Section titled “Common Pitfalls”Quick Check
Section titled “Quick Check”Summary and Further Reading
Section titled “Summary and Further Reading”Key Takeaways:
- K-means is a foundational unsupervised algorithm partitioning unlabeled samples into clusters by minimizing inertia .
- Lloyd’s algorithm alternates between assigning points to closest centroids and updating centroids to cluster arithmetic means, guaranteed to reach local convergence.
- K-means++ initialization disperses initial seeds using probability weighting, preventing poor local minima.
- Optimal cluster count is identified via Elbow inflection points and maximum Silhouette scores. Always scale features beforehand.
Further Reading:
- Lloyd, S. (1982). Least squares quantization in PCM (PDF). IEEE Transactions on Information Theory, 28(2), 129–137. Original paper introducing K-means iterative clustering.
- Arthur, D., & Vassilvitskii, S. (2007). k-means++: The advantages of careful seeding (PDF). Foundational paper on K-means++ initialization.
- Rousseeuw, P. J. (1987). Silhouettes: A graphical aid to the interpretation and validation of cluster analysis. Introduces the Silhouette cluster validation metric.
- Vũ Hữu Tiệp (2017). Bài 4: K-means Clustering. Vietnamese guide detailing K-means step-by-step with graphics.
- scikit-learn documentation. K-means. Official documentation for
sklearn.cluster.KMeans.
Your browser is blocking storage. Your progress will be lost when you reload the page.
Content licensed under CC BY-NC-SA 4.0. Code under the MIT license.About