解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界
K-Means是一种常用的无监督学习算法,广泛应用于数据聚类分析。本文将详细讲解K-Means的数学原理,包括目标函数和算法的迭代过程,阐述算法如何通过迭代优化簇的质心位置达到分类目的。同时,文章将使用Python从零实现一个完整的K-Means聚类算法,包括手动初始化、距离计算、簇的更新等步骤。通过详细的代码和中文注释,本文帮助读者深刻理解K-Means算法的本质和实现过程,最终展示如何使用该算法进行数据聚类和分析。
目录
- 引言
- K-Means算法原理
- 2.1 算法概述
- 2.2 目标函数定义
- 2.3 算法的迭代过程
- Python手动实现K-Means算法
- 3.1 数据准备
- 3.2 初始化质心
- 3.3 分配样本到最近的质心
- 3.4 更新质心位置
- 3.5 完整K-Means算法的实现
- 应用案例:使用K-Means进行数据聚类
- 结论
1. 引言
K-Means是一种无监督的聚类算法,其目的在于将数据分成K个簇,使得簇内样本间的距离尽可能小,而簇间距离尽可能大。尽管许多库中已经实现了K-Means算法,但手动实现算法有助于我们理解其迭代优化的过程。本文将从K-Means的数学原理出发,逐步实现K-Means聚类算法,并应用于实际数据的聚类分析中。
2. K-Means算法原理
2.1 算法概述
K-Means算法的核心是通过不断迭代调整簇的质心位置来最小化簇内的样本距离。算法的主要步骤如下:
- 随机选择K个点作为初始质心。
- 将每个样本分配到距离最近的质心,从而形成K个簇。
- 重新计算每个簇的质心(簇内样本的平均位置)。
- 重复步骤2和3,直到质心位置不再发生变化(或变化小于设定的阈值)。
2.2 目标函数定义
K-Means的目标是最小化所有样本到其所属簇质心的欧氏距离之和。给定数据集 X = { x 1 , x 2 , … , x n } \mathbf{X} = \{x_1, x_2, \dots, x_n\} X={x1,x2,…,xn},其中每个样本点 x i ∈ R d x_i \in \mathbb{R}^d xi∈Rd,算法通过选择K个质心 C = { c 1 , c 2 , … , c K } \mathbf{C} = \{c_1, c_2, \dots, c_K\} C={c1,c2,…,cK} 来最小化以下目标函数:
J ( X , C ) = ∑ i = 1 n ∑ j = 1 K 1 ( x i ∈ C j ) ∥ x i − c j ∥ 2 J(\mathbf{X}, \mathbf{C}) = \sum_{i=1}^{n} \sum_{j=1}^{K} \mathbf{1}(x_i \in C_j) \|x_i - c_j\|^2 J(X,C)=i=1∑nj=1∑K1(xi∈Cj)∥xi−cj∥2
其中:
- ∥ x i − c j ∥ 2 \|x_i - c_j\|^2 ∥xi−cj∥2 表示样本 x i x_i xi 到质心 c j c_j cj 的欧氏距离。
- 1 ( x i ∈ C j ) \mathbf{1}(x_i \in C_j) 1(xi∈Cj) 是指示函数,表示 x i x_i xi 是否属于第 j j j 个簇。
2.3 算法的迭代过程
K-Means通过以下两个步骤交替进行来优化目标函数:
-
簇分配步骤:将每个样本点分配到最近的质心。
对于每个样本点 x i x_i xi,找到与其距离最近的质心 c j c_j cj,并将 x i x_i xi 分配给簇 C j C_j Cj。计算距离通常使用欧氏距离:
∥ x i − c j ∥ = ∑ k = 1 d ( x i k − c j k ) 2 \|x_i - c_j\| = \sqrt{\sum_{k=1}^{d} (x_{ik} - c_{jk})^2} ∥xi−cj∥=k=1∑d(xik−cjk)2
-
质心更新步骤:重新计算每个簇的质心,即簇内所有样本的平均位置:
c j = 1 ∣ C j ∣ ∑ x i ∈ C j x i c_j = \frac{1}{|C_j|} \sum_{x_i \in C_j} x_i cj=∣Cj∣1xi∈Cj∑xi
迭代结束条件通常为质心位置不再变化,或达到设定的最大迭代次数。
3. Python手动实现K-Means算法
3.1 数据准备
我们先创建一个数据集以便后续测试K-Means算法。为简化演示,我们使用二维数据。
import numpy as np
import matplotlib.pyplot as plt
# 生成样本数据
np.random.seed(42)
num_samples_per_cluster = 50
centers = [[2, 2], [8, 3], [3, 6]]
cluster_std = [0.8, 0.5, 1.0]
# 创建三个不同簇的数据点
X = []
for i, center in enumerate(centers):
X.append(np.random.normal(loc=center, scale=cluster_std[i], size=(num_samples_per_cluster, 2)))
X = np.vstack(X)
3.2 初始化质心
为了实现K-Means,首先需要随机初始化K个质心。这些质心可以从数据集中随机选择。
def initialize_centroids(X, K):
"""从数据集中随机选择K个点作为初始质心"""
indices = np.random.choice(X.shape[0], K, replace=False)
centroids = X[indices]
return centroids
# 测试初始化
K = 3 # 假设分为3个簇
initial_centroids = initialize_centroids(X, K)
print("初始质心:\n", initial_centroids)
3.3 分配样本到最近的质心
接下来,我们实现样本分配函数,即计算每个样本到所有质心的距离,并分配给最近的质心。
def assign_clusters(X, centroids):
"""将每个样本分配到最近的质心"""
clusters = []
for x in X:
distances = [np.linalg.norm(x - centroid) for centroid in centroids]
closest_index = np.argmin(distances)
clusters.append(closest_index)
return np.array(clusters)
# 测试分配函数
clusters = assign_clusters(X, initial_centroids)
print("分配的簇索引:\n", clusters)
3.4 更新质心位置
根据分配好的簇,我们可以计算每个簇内所有样本的均值,更新质心位置。
def update_centroids(X, clusters, K):
"""更新质心的位置,计算每个簇的均值"""
new_centroids = []
for k in range(K):
cluster_points = X[clusters == k]
new_centroid = cluster_points.mean(axis=0)
new_centroids.append(new_centroid)
return np.array(new_centroids)
# 测试更新质心
updated_centroids = update_centroids(X, clusters, K)
print("更新后的质心:\n", updated_centroids)
3.5 完整K-Means算法的实现
我们可以将上述步骤合并到一个完整的K-Means算法中,实现迭代优化,直到质心不再发生明显变化。
def kmeans(X, K, max_iters=100, tol=1e-4):
"""K-Means算法实现"""
# 随机初始化质心
centroids = initialize_centroids(X, K)
for i in range(max_iters):
# 分配样本到最近的质心
clusters = assign_clusters(X, centroids)
# 更新质心
new_centroids = update_centroids(X, clusters, K)
# 计算质心移动的距离
centroid_shifts = np.linalg.norm(new_centroids - centroids, axis=1)
# 检查是否满足停止条件
if np.all(centroid_shifts < tol):
print(f"算法在第 {i} 次迭代后收敛。")
break
centroids = new_centroids
return centroids, clusters
# 运行K-Means算法
final_centroids, final_clusters = kmeans(X, K)
print("最终质心:\n", final_centroids)
4. 应用案例:使用K-Means进行数据聚类
使用我们实现的K-Means算法对数据进行聚类,并可视化结果。
# 绘制聚类结果
def plot_clusters(X, clusters, centroids):
plt.scatter(X[:, 0], X[:, 1], c=clusters, cmap='viridis', marker
='o', s=50)
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', marker='x', s=200, label='Centroids')
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.legend()
plt.title("K-Means Clustering")
plt.show()
# 绘制结果
plot_clusters(X, final_clusters, final_centroids)
通过此可视化,我们可以清楚地看到每个簇的分布情况,以及质心在数据分布中的位置。
5. 结论
本文从数学原理和代码实现两个方面详细介绍了K-Means聚类算法。通过手动实现K-Means,我们可以更清楚地理解其聚类过程:从随机初始化质心到迭代更新,以及目标函数的优化。K-Means是机器学习和数据分析中非常重要的无监督学习算法之一,理解其基本原理和实现过程能够帮助我们在数据聚类和探索中更好地应用它。
通过这篇文章,读者不仅能够掌握K-Means的理论,还可以在Python中实现该算法,并将其应用于真实数据的聚类分析中。