learn-unsupervised-with-phoebe / Session 3 of 10
Learn Unsupervised with Phoebe · Session 3 of 10

k-means from the inside

Last session you scaled the pile and five clean segments appeared - but the algorithm that found them was still a black box. Today we open it. k-means is two steps repeated until nothing moves, and you will run those steps yourself, one press at a time, on the real Mango Lane sample. You will watch it settle beautifully from a good start, then watch it settle permanently WRONG from a bad one - and understand exactly why both happen.

🟡 Building Builder track You will run the loop by hand 45 min + self-study depth
0-3 · Welcome 3-20 · Concepts 20-40 · Step the loop 40-45 · Q&A
Part 0

Why this session exists

k-means is the workhorse of clustering, and the field's best teachers vote with their syllabi: Andrew Ng's ML specialization teaches exactly one clustering algorithm - k-means - and Google's clustering course does the same. Their bet, and this course's bet, is that understanding one algorithm's two-step loop deeply beats knowing ten algorithms shallowly. After today you will know what k-means optimizes, why it always settles, why WHERE it starts decides WHERE it ends, and what modern scikit-learn actually does about that (spoiler: not what older tutorials say).

Live - covered in session Self-study - read after ✦ Lab moment 🥭 The Mango Lane case
★ What you walk out with today Lloyd's loop (assign, update, repeat - that is ALL of k-means), the number it optimizes (inertia) and why each round can only lower it, the local-optimum failure mode you will trigger live, and k-means++ - the initialization trick that makes bad starts rare.
The honesty rail (used all course) The lab below runs REAL k-means in your browser - real k-means++ placement, real assignments, real centroid moves, real inertia. The only fake thing is the "truth match" score, which exists because our 300 customers are simulated with five planted segments. Real projects never get that number.
Part 1 · covers the algorithm itself

Lloyd's loop 8 min live

Here is the entire algorithm. Place k centroids somewhere. Then repeat two steps: (1) assign every point to its nearest centroid, (2) move each centroid to the average of its members - until assignments stop changing. That is ALL of k-means. Everything else you will ever read about it is commentary on these two steps.

Start: place k centroids (k-means++ spreads them out) ① Assign Every customer joins the cluster of its NEAREST centroid. Distances can only shrink or hold: inertia falls (or stays). ② Update Each centroid walks to the AVERAGE of its current members. The mean is the best spot for them: inertia falls (or stays). memberships set go again ✅ Nothing changed this round? Stop. Both steps can only lower inertia - so the loop must settle. That is all of k-means.
🔍 Click to zoom - assign, update, repeat: the whole algorithm in one cycle
LiveOne round, six imaginary customers4 min▶

Take six customers on a single scaled axis (call it "engagement"): A at 1, B at 2, C at 3, and X at 8, Y at 9, Z at 10. Two centroids start awkwardly at 4 and 6. Run one round by hand:

Assign. A, B, C are nearer to the centroid at 4 - they join it. X, Y, Z are nearer to 6 - they join that one. Every customer now has exactly one cluster.

Update. Centroid 1 moves to the average of A, B, C: (1+2+3)/3 = 2. Centroid 2 moves to the average of X, Y, Z: 9. Both centroids just jumped into the heart of their members.

Assign again. Everyone re-checks their nearest centroid... and nobody switches. Assignments unchanged - the loop stops. Two rounds, done.

RoundCentroid 1Centroid 2What happened
Startat 4at 6awkward guesses, near neither group
1 · assigngets A, B, Cgets X, Y, Zmemberships set by nearest-centroid
1 · updatemoves to 2moves to 9each jumps to its members' average
2 · assignkeeps A, B, Ckeeps X, Y, Znobody switches - the loop stops

What did the loop optimize? Inertia: the within-cluster sum of squared distances - add up, for every customer, the squared distance to their own centroid. Assigning to the NEAREST centroid can only shrink those distances; moving a centroid to its members' AVERAGE is mathematically the spot that minimizes their squared distances. Each round can only lower inertia - which is exactly why k-means always settles and never oscillates forever.

Real worldAndrew Ng's ML specialization spends its entire clustering week on this one algorithm - the intuition, the optimization objective, the initialization, and choosing k - and never introduces a second one. When the field's most famous teacher budgets a full week for two steps of arithmetic, the lesson is that the depth is in understanding WHAT those steps optimize, not in collecting algorithm names.
Self-studyVoronoi territories: why the borders are straight3 min▶

"Assign every point to its nearest centroid" quietly draws a map. The set of locations closer to centroid 1 than to any other centroid is a territory; the border between two territories is the line exactly halfway between the two centroids - and halfway lines are STRAIGHT. This carving of space into nearest-centroid territories is called a Voronoi diagram, and it means k-means clusters are always separated by straight borders (flat planes, in more dimensions).

File this away for Part 3 and for session 5: any real group whose true boundary is curved - a crescent, a ring, an S - is a shape k-means literally cannot draw around. Not "struggles with." Cannot. Its borders have no curves in them.

Self-studyWhat a round costs (why k-means is everyone's default)2 min▶

Each round of the loop is honest arithmetic: measure the distance from every point to every centroid (assign), then average each cluster's members (update). No pairwise point-to-point comparisons, no tree building, no probability fitting - just points-times-centroids distance checks, a handful of rounds, done. That frugality is a big part of why Ng, Google, and half the BI tools on earth reach for k-means first: it is not just simple to explain, it is cheap to run at customer-base scale. Session 5's comparison card puts this in context - some of the rivals that fix k-means' blind spots pay for it in exactly this currency.

Part 2 · covers initialization

Where you start matters 6 min live

The loop only ever improves - and that is precisely its weakness. It rolls downhill on the inertia landscape and stops in the first valley it reaches. If the starting centroids are placed badly, that valley is a local optimum: a genuinely bad clustering that no amount of further looping will ever fix, because escaping it would require inertia to go UP first, and the loop cannot do that.

🎯 Good start: rolls to the deep valley settles: inertia 92 🎲 Unlucky start: stuck in a shallow dip locks: inertia 278 cannot climb the hill the better valley (92) The loop only improves - a bad start settles into a bad answer and STAYS. Fix the start, not the loop.
🔍 Click to zoom - two starts, two valleys: the loop stops in whichever it reaches first
Livek-means++ decoded3 min▶

The classic fix has two flavors. The brute-force one: run the whole thing many times from different random starts and keep the run with the lowest inertia. The smart one is k-means++, and it is one sentence: place the first centroid at random, then pick each next centroid preferring points FAR from the centroids already placed.

Why that works: bad starts are almost always crowded starts - two centroids landing in the same real group, leaving another real group centroid-less. Spreading the initial centroids out makes each one likely to land near a different real group, so the loop starts near the deep valley instead of near a shallow one. It is not a guarantee - you will see a good start land at 85% truth match in the lab, not 100% - but it turns "pray" into "usually fine".

Real worldscikit-learn made k-means++ the default initialization for its KMeans estimator - you get the smart start without asking for it. The sklearn user guide is also blunt about the reason: plain k-means converges to local minima, and initialization is the lever that decides which one you get.
Self-studyThe landscape metaphor, made precise2 min▶

The valley picture above is not just a cartoon. Imagine every possible clustering of the 300 customers as a location, and the inertia of that clustering as the altitude there. Lloyd's loop is a walker on this landscape who can only step downhill: assign is one kind of downhill step, update is another. Convergence means reaching a spot where neither step goes down - a valley floor. Which floor you reach depends entirely on where the walker was dropped, and the landscape for five centroids has many valleys. Initialization is the drop point. That is all of Part 2 in one image.

Self-studyYour restart budget, in plain terms3 min▶

Three strategies, one decision: how much will you pay for insurance against a shallow valley?

StrategyWhat it doesWhen it fits
Random starts, many retriesrun the full loop from many random drops, keep the lowest-inertia resultthe old world - random drops crash often, so the old n_init=10 default bought ten tickets
k-means++, one runone smart, spread-out startsklearn's modern n_init='auto' - usually enough, as your 85% lab run shows
k-means++, best of Nseveral smart starts, keep the bestwhen the answer ships to marketing and must not wobble - our full-run labs use best-of-5

Notice the pattern: the smarter the start, the fewer retries you need. The two fixes trade against each other - which is exactly why sklearn's default retry count changed when its default initialization did.

The stale-default trap in older tutorials Modern scikit-learn uses n_init='auto', which for k-means++ initialization means ONE run - because a spread-out start usually does not need retries. The old default, n_init=10, was designed for RANDOM initialization, where ten retries were cheap insurance against crowded starts. Plenty of older tutorials still teach "always set n_init=10" as gospel without saying why. Now you know what the knob actually does: it is the brute-force fix, and k-means++ is the reason you rarely need it anymore.
Part 3 · covers the fine print

What k-means assumes 4 min live

Averages and straight borders are not neutral machinery - they are a worldview. k-means implicitly assumes your real groups are round-ish blobs of roughly similar size, separable by straight lines. Our scaled Mango Lane pile happens to fit that worldview, which is why it works so well here. Not all data is so polite.

LiveThe worldview in three lines4 min▶
  • Round-ish blobs: a centroid is an average, and an average is only a good summary of a group that clusters around its center. Stretched or curved groups have averages that sit in weird places - sometimes outside the group entirely.
  • Similar sizes: a big sprawling group next to a tight small one tempts k-means to slice the big one in half and hand a piece to the small one's centroid, because that lowers inertia.
  • Straight borders: the Voronoi territories from Part 1. Two crescent moons interlocking? k-means will draw one straight line through both and confidently report two clusters that each contain half of each moon.
AssumptionData that breaks itWho handles it (session 5)
Round-ish blobsstretched, curved, or ring-shaped groupsDBSCAN traces arbitrary shapes
Similar sizesa sprawling group beside a tight oneGMM lets clusters own their spread
Straight bordersthe two interlocking moonsDBSCAN again - it follows density, not lines

Session 5 shows you those two moons live - and the algorithms (DBSCAN especially) that CAN trace curved shapes. Today's point is simpler: when k-means fails, it fails silently and confidently, because the loop always settles and always reports clusters.

Real worldscikit-learn's documentation ships a demo literally titled "Demonstration of k-means assumptions": the library's own authors running their own tool on stretched blobs, unequal sizes, and wrong k, and showing it confidently mis-cluster all three. Official docs that dedicate a page to "here is our tool producing convincing nonsense" are docs worth believing - and session 9 runs that demo's spirit on our own pile.
Everyone steps

Step the loop yourself 🥭 ★ 20 min · one press per round

The lab below runs REAL k-means on the scaled Mango Lane sample - but hands YOU the loop. Every press of ⏭ Step is one round: assignments, then centroid moves, then the new inertia. You will watch a good start settle, then manufacture a bad one and watch it trap itself, live.

Read the opening position. Five centroids have been placed by k-means++ - notice they are spread out across the PCA view of the scaled pile, not clumped. That spread is the whole trick, and it happened before a single round ran.

Press ⏭ Step repeatedly. Each round: customers join their nearest centroid, centroids walk to their members' average, and inertia falls. Watch it fall EVERY round - never rise - until the loop settles around 6 rounds at inertia 92, truth match about 85%. You just ran Lloyd's loop to convergence by hand.

Press ↺ Reset, then 🎲 Unlucky random start, then step about 10 times. The loop still behaves perfectly - inertia falls every round - but it settles PERMANENTLY at inertia 278, truth match 65%. Two centroids are sharing one real segment while two real segments share a centroid. Keep pressing: nothing will ever change. That is a local optimum, live.

Say the moral out loud. The unlucky run did not malfunction - the loop did exactly its job and rolled into the nearest valley. The fix is better STARTS: k-means++ placement or multiple restarts. Not paranoia, and not more looping. The full-run labs in the other sessions all use best-of-5 restarts for exactly this reason.

Compare across sessions. Today's single k-means++ run reached about 85% truth match; the best-of-5-restarts labs elsewhere reach 100% on the same pile. Even good starts vary - and choosing how many starts to pay for is precisely what sklearn's n_init knob is about. The stale-default callout from Part 2 should feel concrete now.

✦ The step button is the lesson Libraries run this loop in microseconds and only show you the answer, which is how k-means stays a black box for most people who use it. Having pressed each round yourself once, you will never again wonder what KMeans.fit() is doing - or why two runs on identical data can disagree.
Before next session

Homework 📝 ◐ 20 minutes

Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · One round of Lloyd's loop is...

Assign, then update - that is the entire algorithm, repeated until assignments stop changing. Both steps can only lower inertia (within-cluster sum of squared distances), which is why the loop always settles.

2 · Why does a bad start produce a PERMANENTLY bad answer instead of a temporarily bad one?

The loop is pure downhill. That guarantees convergence AND guarantees it stays in whatever valley it reaches first. You watched it live: the unlucky start locked at inertia 278 while a better answer at 92 existed. The fix is better starts, not more rounds.

3 · What does k-means++ do differently from plain random initialization?

Spread-out starts make crowded starts (the cause of most local optima) rare. Option A describes the n_init brute-force fix - useful with random starts, which is why sklearn's modern n_init='auto' runs k-means++ just ONCE.

Coverage, honestly

What this session covered from the official universe

✓Andrew Ng ML Specialization course 3, week 1 - k-means algorithm + optimization objectiveLloyd's loop, inertia as the objective, initialization - taught by stepping the loop by hand
✓scikit-learn KMeans documentationk-means++ as default init, local optima, and the n_init='auto' = one k-means++ run correction
✓Google ML Clustering course - the k-means algorithmthe assign/update mechanics and the assumptions fine print, on the Mango Lane pile

Certificates, videos, and graded assessments stay with the official providers - this course teaches the working core with a live in-browser lab.

Session 3 cheat sheet

Lloyd's loopPlace k centroids, then repeat: assign every point to its nearest centroid; move each centroid to its members' average. Stop when assignments stop changing.
InertiaWithin-cluster sum of squared distances - the number k-means minimizes. Lower = tighter clusters around their centroids.
Why it always settlesBoth steps can only lower inertia, so the loop is pure downhill - it must reach a valley and stop. It never oscillates forever.
Local optimumA shallow valley: a bad answer the loop cannot escape, because escaping would require inertia to rise. Lab proof: unlucky start locks at 278 while 92 exists.
k-means++First centroid random; each next centroid picked preferring points far from those already placed. Spread starts, rare local optima. sklearn's default.
n_init='auto'Modern sklearn: ONE k-means++ run. The old n_init=10 default was insurance for RANDOM inits - older tutorials still teach the stale default.
The worldviewk-means assumes round-ish, similar-size blobs with straight (Voronoi) borders. Curved shapes like the two moons are invisible to it - session 5.
The fixBad starts are fixed by better starts: k-means++ or best-of-N restarts (our full-run labs use best-of-5). Never by running the loop longer.