研究背景
在论文《A Method of Clustering for Large Sets of Multivariate Data》中,MacQueen首次提出了K-means算法,并介绍了基本的算法流程和应用场景。
在文献《Some Methods for Classification and Analysis of Multivariate Observations》中,Hartigan和Wong提出了一种改进的K-means算法,称为K-means++算法,能够更好地避免陷入局部最优解。
在论文《Robust and Fast Clustering Algorithm for High Dimensional Data Using K-Means with a Modified Distance Metric》中,Huang等人提出了一种基于K-means算法的改进方法,称为K-means++D算法,能够更好地处理高维数据集。
在文献《A Survey of Clustering Data Mining Techniques》中,Jain等人介绍了K-means算法的一些变体和扩展方法,如基于密度的DBSCAN算法、层次聚类算法等,并对其进行了比较和分析。
在论文《The K-means Clustering Algorithm: A Survey》中,Xu等人对K-means算法的发展历史、算法优缺点、应用领域等方面进行了全面的综述和分析。
在文献《K-means Clustering Based on Genetic Algorithm and Particle Swarm Optimization》中,Mohamed等人提出了一种结合遗传算法和粒子群优化的K-means算法,能够更好地克服局部最优解问题。
原理说明
Kmeans是一种常见的聚类算法,用于将相似的数据点归类到不同的群组中。Kmeans的原理如下:
初始化:Kmeans算法首先需要初始化一个用户指定数量的聚类中心点,通常是随机选取K个数据点作为聚类中心点。
分配:对于每个数据点,计算其到每个聚类中心点的距离,并将其分配到距离最近的聚类中心点所代表的聚类中。
更新:在所有数据点都被分配到对应的聚类中之后,重新计算每个聚类中心点的位置,即将每个聚类中的所有数据点的坐标平均值作为新的聚类中心点位置。
重复:重复步骤2和3,直到所有聚类中心点的位置不再改变,或达到预设的最大迭代次数。
输出:输出聚类结果,即每个数据点所属的聚类编号。
Kmeans算法的核心是通过最小化每个数据点到其所属聚类中心点的距离平方和来确定最优的聚类中心点位置。在实际应用中,Kmeans算法通常需要多次运行并比较结果,以获得最优的聚类结果。
原理推导
随机选择K个中心点作为簇的初始中心;
将每个数据点分配到离它最近的簇中;
计算每个簇的中心点,更新簇中心;
重复步骤2和3,直到簇中心不再发生变化或达到最大迭代次数。
下面对K-means算法进行数学推导:
设数据集为X={x1, x2, …, xn},其中每个数据点xi是一个d维向量。假设将数据点分为K个簇,第k个簇的中心点为μk,则第i个数据点与第k个簇的中心点的距离为:
dist(xi, μk) = ||xi - μk||2
其中||.||2表示欧几里得范数。
K-means算法的目标是最小化所有数据点与其所属簇中心点的距离之和,即:
J(μ1, μ2, …, μK) = ∑i=1 to n min_k{dist(xi, μk)}^2
其中min_k{.}表示求解所有K个簇中与xi距离最近的中心点μk,并将xi分配到第k个簇中。
为了求解上述目标函数J,需要对μ1, μ2, …, μK进行优化。具体而言,需要先固定簇分配,对簇中心进行优化,然后再固定簇中心,对簇分配进行优化。
对于固定簇分配,目标函数J是关于μ1, μ2, …, μK的凸函数,因此可以使用梯度下降法求解其最小值。具体而言,需要将目标函数对μk求导,即:
∂J(μ1, μ2, …, μK) / ∂μk = ∑i=1 to n 2xi(μk - xi)^T*[μk - xi = 0
其中^T表示向量的转置,即矩阵的行列互换。令上述导数等于0,得到μk的最优解:
μk = 1/Nk * ∑i∈Ck xi
其中Ck表示第k个簇中的数据点,Nk表示第k个簇中的数据点个数。
对于固定簇中心,目标函数J是关于数据点分配的离散优化问题,可以使用交替最小化法(alternating optimization)求解。具体而言,可以先随机分配数据点到簇中
具体而言,可以先随机分配数据点到簇中,然后依次更新每个簇的中心点,直到簇中心点不再发生变化或达到最大迭代次数。更新簇分配时,可以根据当前簇中心点,将每个数据点分配到距离其最近的簇中。
具体而言,假设第i个数据点当前被分配到第k个簇中,其所属簇中心为μk,则将该数据点分配到其他簇中的中心点为μl时,目标函数的变化量为:
ΔJ = ||xi - μl||2 - ||xi - μk||2
将ΔJ展开,得到:
ΔJ = ||xi||2 + ||μl||2 - 2xi^Tμl - ||xi||2 - ||μk||2 + 2xi^Tμk
ΔJ = 2(xi^Tμk - xi^Tμl + μl^Tμl - μk^Tμk)
由于将xi分配到距离其最近的簇中时,ΔJ应当小于等于0,因此可以通过比较ΔJ的大小,将xi分配到距离其最近的簇中。
综上所述,K-means算法的具体步骤如下:
随机选择K个中心点作为簇的初始中心;
将每个数据点分配到离它最近的簇中;
计算每个簇的中心点,更新簇中心;
重复步骤2和3,直到簇中心不再发生变化或达到最大迭代次数。
其中,簇分配可以使用上述交替最小化法求解,簇中心可以使用梯度下降法求解。最终的目标函数是所有数据点与其所属簇中心点的距离之和的平方,即:
J(μ1, μ2, …, μK) = ∑i=1 to n min_k{dist(xi, μk)}^2
其中dist(xi, μk) = ||xi - μk||2表示数据点xi与簇中心点μk之间的距离。
代码示意
import numpy as np
import matplotlib.pyplot as plt
# 设置随机种子,确保每次运行结果相同
np.random.seed(42)
# 生成数据
data = np.vstack([
np.random.normal(loc=(2, 2), scale=0.5, size=(100, 2)),
np.random.normal(loc=(-2, -2), scale=0.5, size=(100, 2)),
np.random.normal(loc=(2, -2), scale=0.5, size=(100, 2)),
np.random.normal(loc=(-2, 2), scale=0.5, size=(100, 2)),
])
# 定义 K-means 算法
def kmeans(data, k, max_iter=100):
# 随机初始化聚类中心
centroids = data[np.random.choice(data.shape[0], k, replace=False)]
for i in range(max_iter):
# 计算每个样本到聚类中心的距离
distances = np.linalg.norm(data[:, np.newaxis, :] - centroids, axis=2)
# 分配每个样本到最近的聚类中心
labels = np.argmin(distances, axis=1)
# 更新聚类中心
new_centroids = np.array([data[labels == j].mean(axis=0) for j in range(k)])
# 如果聚类中心不再发生变化,则结束算法
if np.allclose(centroids, new_centroids):
break
centroids = new_centroids
return labels, centroids
# 运行 K-means 算法
labels, centroids = kmeans(data, k=4)
# 绘制聚类结果
fig, ax = plt.subplots()
colors = ["red", "green", "blue", "purple"]
for i in range(4):
ax.scatter(data[labels==i, 0], data[labels==i, 1], c=colors[i], label=f"Cluster {i}")
ax.scatter(centroids[:, 0], centroids[:, 1], marker="*", s=200, c="black", label="Centroids")
ax.legend()
plt.show()