k-means, with two assumptions relaxed.
Clusters get to be tilted ellipses, not circles. Points get to belong to more than one at once.
Fit by EM — the same soft-assign-then-refit loop, now in 2-D with a full covariance per component.
Those probabilities are the responsibilities. They're how it fits and what it hands back.
300 points, three blobs, sheared into long diagonal overlapping ellipses. No labels. The canonical case k-means gets wrong.
E-step — responsibility of component k for point i:
γik=∑jπjN(xi∣μj,Σj)πkN(xi∣μk,Σk)M-step — re-fit each component, weighted by responsibility. The covariance is what tilts the ellipse:
Σk=Nk1i=1∑Nγik(xi−μk)(xi−μk)⊤The log-likelihood only climbs. When it flattens, stop.
Two contours per component (1σ, 2σ) from the real covariance eigen-decomposition. Points shaded by soft responsibility — blends at the seams. Lower panel: the log-likelihood rising.
Round seeds → diagonal ellipses in three or four frames. Converges at 23 iterations, log-likelihood −1,081 → −515.
Log-likelihood always improves with more components. BIC penalizes the parameter count, so it has a real minimum.
Minimum at k=3. The elbow question with a real objective under it.
Same data. k-means cuts a straight line across the diagonal groups. GMM wraps each in its tilted ellipse.
Adjusted Rand index vs the true blobs: k-means 0.60, GMM 1.0.
From-scratch EM and sklearn.mixture.GaussianMixture(covariance_type="full")
both hit average log-likelihood −1.7162, labels agree at ARI 1.0.