2 Unsupervised Learning
K-Means Clustering & its Variants
Objective Function & Lloyd's Algorithm
Partition points into clusters to minimize within-cluster sum of squares (Inertia):
Two-step coordinate descent:
- Assignment step: assign each instance to its closest centroid:
- Update step: recompute centroids as mean of assigned points:
def calculate_distance(point1, point2):
return sum((x-y)**2 for x,y in zip(point1,point2))
def k_means_clustering(points: list[tuple[float, ...]], k: int, initial_centroids: list[tuple[float, ...]], max_iterations: int) -> list[tuple[float, ...]]:
final_centroids = list(initial_centroids)
for i in range(max_iterations):
# 1. Assignment step
labels = [min(range(k), key= lambda x: calculate_distance(point, initial_centroids[x])) for point in points]
# 2. Update step
for i in range(k):
group = [ point for point,label in zip(points, labels) if label == i]
if group:
final_centroids[i] = tuple(sum(item)/len(group) for item in zip(*group))
return final_centroids
Limitations:
- Sensitive to centroid initialization (can get trapped in local minima).
- Assumes spherical, equally-sized clusters with similar density.
- Sensitive to outliers (mean is heavily affected).
- must be specified in advance.
How to Choose K
- Elbow Method: plot Inertia (SSE) vs . Pick the "elbow" point where marginal loss reduction slows down significantly.
- Silhouette Score: evaluates cohesion vs separation for each sample:where is mean intra-cluster distance, is mean nearest-cluster distance. Higher is better.
Variants
1. K-Means++
Addresses initialization sensitivity:
- Choose first centroid uniformly at random from dataset.
- For each point , compute , the shortest distance to existing centroids.
- Choose next centroid from points with probability:
- Repeat steps 2-3 until centroids are chosen.
Theoretical guarantee: -competitive with optimal solution.
2. Mini-Batch K-Means
Optimized for large-scale datasets:
- Samples a mini-batch of size at each iteration.
- Centroids updated using online running average: .
- Drastically reduces computation and memory per step with minimal quality drop.
3. K-Medoids (PAM - Partitioning Around Medoids)
- Centroids must be actual data points (medoids) instead of means.
- Can minimize arbitrary dissimilarity (e.g. Manhattan L1 distance) instead of squared Euclidean:
- Much more robust to outliers and noise than K-Means.
Principal Component Analysis (PCA)
Linear unsupervised dimensionality reduction: find orthogonal projection directions that maximize preserved variance (equivalently, minimize reconstruction error).
Mathematical Derivation (Maximum Variance)
Let dataset be zero-centered: .
Sample covariance matrix:
Project data onto unit direction vector ():
Maximize variance under constraint :
Take derivative with respect to :
- The optimal projection direction is an eigenvector of covariance matrix .
- The variance along is the corresponding eigenvalue .
Algorithm Steps
- Center the data: .
- Compute covariance matrix: .
- Eigenvalue decomposition: solve .
- Sort eigenvalues descending: , select top eigenvectors:
- Project data:
Explained Variance Ratio
Proportion of total variance preserved by top principal components:
Connection to SVD
For zero-centered , compute Singular Value Decomposition: .
- The right singular vectors are the principal direction eigenvectors of .
- Singular values relate to eigenvalues by: .
- Direct SVD on avoids calculating , offering better numerical stability and speed.
import numpy as np
def pca(X: np.ndarray, k: int) -> tuple[np.ndarray, np.ndarray]:
# 1. Zero-center
X_centered = X - np.mean(X, axis=0)
# 2. SVD: X = U S V^T
U, S, Vt = np.linalg.svd(X_centered, full_matrices=False)
# 3. Top-k components (V^T rows)
components = Vt[:k]
# 4. Project
Z = np.dot(X_centered, components.T)
return Z, components