目录
K-means 聚类算法是一种经典的无监督学习算法,主要用于对数据进行聚类分析(clustering)。它的目标是将数据划分为 K 个簇(cluster),使得每个簇内的数据点尽可能相似,而不同簇之间的数据点尽可能不同。
一、K-means 算法的基本原理
K-means 的核心思想是:
-
通过迭代优化,最小化类内平方误差(Within-Cluster Sum of Squares, WCSS)。
-
每个簇有一个中心点(质心,centroid)。
-
每个样本属于与其最近的簇中心。
二、算法流程
假设我们要将数据划分为 K 个簇,数据集为
第一步:初始化
随机选择 K 个样本作为初始簇中心
第二步:分配样本
将每个样本分配到与之欧几里得距离最近的簇中心对应的簇中:
第三步:更新簇中心
重新计算每个簇的中心(所有属于该簇样本的均值):
第四步:重复迭代
重复步骤 2 和 3,直到:
-
簇中心不再变化,或者
-
达到最大迭代次数,或者
-
分配结果不再变化(收敛)
三、目标函数(损失函数)
K-means 的优化目标是最小化总的簇内平方误差:
这个目标函数衡量了所有点到其所属簇中心的距离平方和。
四、优缺点
优点:
-
简单高效,计算速度快
-
对大数据集效果较好
-
易于实现
缺点:
-
需要预先指定 K 值
-
对初始簇中心敏感(可用 K-means++ 改进)
-
只能发现球形簇结构,对非凸簇或不同密度的数据效果差
-
对离群点敏感
五、应用场景
-
图像压缩
-
文本聚类(如新闻自动分类)
-
客户分群(用户行为分析)
-
异常检测
六、仿真
生成模拟数据,300个样本,分成3个中心,利用K-means算法分类离散数据。
仿真代码:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
# 生成模拟数据:300个样本,分成3个中心
X, y_true = make_blobs(n_samples=300, centers=3, cluster_std=0.60, random_state=0)
# 设置中文字体(以Windows为例)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 绘制原始数据
plt.figure(figsize=(6, 6))
plt.scatter(X[:, 0], X[:, 1], s=50)
plt.title('原始数据分布')
plt.xlabel("X1")
plt.ylabel("X2")
plt.grid(True)
plt.show()
# 应用K-means聚类
kmeans = KMeans(n_clusters=3, random_state=0)
kmeans.fit(X)
y_kmeans = kmeans.predict(X)
# 可视化聚类结果
plt.figure(figsize=(6, 6))
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis') # 不同颜色表示不同的簇
centers = kmeans.cluster_centers_ # 提取簇中心
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75, marker='X', label='Centroids')
plt.title("K-means 聚类结果")
plt.xlabel("X1")
plt.ylabel("X2")
plt.legend()
plt.grid(True)
plt.show()
原始数据:
分类结果:
七、K-means的python实现代码
通过阅读理解代码,才能理解算法。
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# 生成测试数据
X, _ = make_blobs(n_samples=300, centers=3, cluster_std=0.6, random_state=42)
# 设置超参数
K = 3 # 簇数
max_iters = 100 # 最大迭代次数
tolerance = 1e-4 # 收敛阈值
# Step 1: 随机初始化簇中心
np.random.seed(42)
initial_indices = np.random.choice(len(X), K, replace=False)
centroids = X[initial_indices]
def compute_distances(X, centroids):
"""计算每个点到每个中心的距离,返回[n_samples, K]矩阵"""
distances = np.zeros((X.shape[0], K))
for k in range(K):
distances[:, k] = np.linalg.norm(X - centroids[k], axis=1)
return distances
# K-means 主循环
for i in range(max_iters):
# Step 2: 分配每个样本到最近的簇
distances = compute_distances(X, centroids)
labels = np.argmin(distances, axis=1)
# Step 3: 更新簇中心
new_centroids = np.array([X[labels == k].mean(axis=0) for k in range(K)])
# Step 4: 检查是否收敛
if np.linalg.norm(new_centroids - centroids) < tolerance:
print(f"Converged at iteration {i}")
break
centroids = new_centroids
# 可视化聚类结果
plt.figure(figsize=(6, 6))
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=50)
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', s=200, marker='X', label='Centroids')
plt.title("K-means 手动实现聚类结果")
plt.legend()
plt.grid(True)
plt.show()