K-means算法详解

K-means聚类算法详解

一、基本概念

1.1 什么是K-means聚类?

K-means聚类(K-means Clustering)是一种经典的无监督学习算法,用于将数据集划分为K个互不重叠的簇(clusters)。其核心目标是:

  • 最大化簇内相似度:同一簇内的数据点尽可能相似
  • 最大化簇间差异度:不同簇之间的数据点尽可能不同

1.2 算法类型

  • 无监督学习:不需要标签数据
  • 基于划分的聚类:将数据划分为预设数量的簇
  • 原型聚类:每个簇由一个质心(centroid)代表

1.3 核心思想

物以类聚,人以群分“——将数据点分配到距离最近的簇中心,通过迭代优化簇中心位置,使簇内误差平方和最小化。


二、数学原理

2.1 目标函数(损失函数)

K-means的目标是最小化簇内误差平方和(Sum of Squared Errors, SSE):

$$ J = \sum_{i=1}^{K} \sum_{x \in C_i} ||x - \mu_i||^2 $$

其中:

  • $K$:簇的数量
  • $C_i$:第$i$个簇
  • $x$:数据点
  • $\mu_i$:第$i$个簇的质心(均值)
  • $||x - \mu_i||^2$:数据点到质心的欧氏距离平方

2.2 距离度量

2.2.1 欧氏距离(Euclidean Distance)

最常用的距离度量:

$$ d(x, \mu) = \sqrt{\sum_{j=1}^{n} (x_j - \mu_j)^2} $$

2.2.2 其他距离度量

  • 曼哈顿距离(Manhattan Distance)
  • 切比雪夫距离(Chebyshev Distance)
  • 余弦相似度(Cosine Similarity)

三、算法流程

3.1 基本步骤

步骤1:初始化(Initialization)

随机选择K个数据点作为初始簇中心(质心)。

问题:随机初始化可能导致:

  • 收敛到局部最优解
  • 收敛速度慢
  • 聚类结果不稳定

解决方案:使用K-means++算法(见第五部分)

步骤2:分配(Assignment)

对于每个数据点,计算其到所有簇中心的距离,将其分配到距离最近的簇

$$ C^{(t+1)}(i) = \arg\min_k ||x^{(i)} - \mu_k^{(t)}||^2 $$

步骤3:更新(Update)

重新计算每个簇的质心(均值):

$$ \mu_k^{(t+1)} = \frac{1}{|C_k|} \sum_{x^{(i)} \in C_k} x^{(i)} $$

步骤4:迭代(Iteration)

重复步骤2和3,直到满足停止条件:

  • 簇中心不再变化(或变化很小)
  • 达到最大迭代次数
  • 目标函数收敛

3.2 算法伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
输入:数据集X = {x₁, x₂, ..., xₘ},簇数量K,最大迭代次数max_iter
输出:簇划分C = {C₁, C₂, ..., Cₖ},簇中心μ = {μ₁, μ₂, ..., μₖ}

1. 初始化:随机选择K个数据点作为初始簇中心 μ₁, μ₂, ..., μₖ
2. for iter = 1 to max_iter do:
3. # 分配阶段
4. for i = 1 to m do:
5. 计算 xᵢ 到每个簇中心的距离
6. 将 xᵢ 分配到距离最近的簇
7. end for
8.
9. # 更新阶段
10. for k = 1 to K do:
11. μₖ = 当前簇Cₖ中所有点的均值
12. end for
13.
14. # 检查收敛
15. if 簇中心变化 < 阈值 then:
16. break
17. end if
18. end for
19. return C, μ

3.3 收敛性证明

K-means算法一定收敛,原因:

  1. 目标函数单调递减

    • 分配阶段:固定簇中心,最小化距离 → 目标函数减小
    • 更新阶段:固定簇划分,计算均值 → 目标函数最小化
  2. 状态空间有限

    • 数据点有限,簇划分的可能组合有限
    • 目标函数有下界(≥ 0)
  3. 收敛到局部最优

    • 由于是贪心算法,只能保证收敛到局部最优解
    • 多次运行取最优结果

四、K值选择方法

4.1 手肘法(Elbow Method)

4.1.1 原理

绘制K值SSE的关系曲线,选择”肘部”点(SSE下降速度明显变缓的点)。

4.1.2 实现步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

sse = []
k_range = range(1, 11)

for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42)
kmeans.fit(X)
sse.append(kmeans.inertia_) # inertia_ = SSE

# 绘制肘部图
plt.plot(k_range, sse, 'bo-')
plt.xlabel('K值')
plt.ylabel('SSE(簇内误差平方和)')
plt.title('肘部法选择最佳K值')
plt.grid(True)
plt.show()

4.1.3 判读方法

  • 肘部点:曲线斜率明显变化的点
  • 主观性强:需要人工判断

4.2 轮廓系数法(Silhouette Coefficient)

4.2.1 定义

轮廓系数衡量样本与其所在簇的相似度与其他簇的不相似度:

$$ s(i) = \frac{b(i) - a(i)}{\max{a(i), b(i)}} $$

其中:

  • $a(i)$:样本 $i$ 到同簇其他样本的平均距离(簇内凝聚度)
  • $b(i)$:样本 $i$ 到最近其他簇所有样本的平均距离(簇间分离度)

4.2.2 取值范围

  • $s(i) \in [-1, 1]$
  • $s(i) \approx 1$:样本非常适合所在簇
  • $s(i) \approx 0$:样本在两个簇边界
  • $s(i) \approx -1$:样本可能被错误分配

4.2.3 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from sklearn.metrics import silhouette_score

silhouette_scores = []

for k in range(2, 11): # K≥2
kmeans = KMeans(n_clusters=k, random_state=42)
labels = kmeans.fit_predict(X)
score = silhouette_score(X, labels)
silhouette_scores.append(score)

# 绘制轮廓系数图
plt.plot(range(2, 11), silhouette_scores, 'ro-')
plt.xlabel('K值')
plt.ylabel('平均轮廓系数')
plt.title('轮廓系数法选择最佳K值')
plt.grid(True)
plt.show()

# 选择轮廓系数最大的K值
best_k = range(2, 11)[np.argmax(silhouette_scores)]
print(f"最佳K值: {best_k}")

4.3 Gap Statistic方法

4.3.1 原理

比较实际数据的SSE与随机数据的SSE的差距(Gap),选择Gap最大的K值。

4.3.2 优点

  • 更客观,减少主观判断
  • 适用于复杂数据分布

五、改进算法

5.1 K-means++(优化初始化)

5.1.1 问题

标准K-means随机初始化可能导致:

  • 收敛慢
  • 局部最优解
  • 结果不稳定

5.1.2 改进策略

采用概率加权的方式选择初始簇中心:

  1. 随机选择第一个簇中心
  2. 对于每个数据点 $x$,计算其到最近已选簇中心的距离 $D(x)$
  3. 以概率 $\frac{D(x)^2}{\sum D(x)^2}$ 选择下一个簇中心
  4. 重复步骤2-3,直到选择K个簇中心

5.1.3 优点

  • 提高聚类质量
  • 加快收敛速度
  • 减少对初始值的敏感性

5.1.4 实现

1
2
3
# Scikit-learn默认使用K-means++
kmeans = KMeans(n_clusters=3, init='k-means++', random_state=42)
kmeans.fit(X)

5.2 Mini-Batch K-means(优化大数据)

5.2.1 问题

标准K-means需要每次迭代使用全部数据,计算成本高。

5.2.2 改进策略

每次迭代只使用一小批数据(mini-batch)更新簇中心:

  1. 随机选择一个小批量数据
  2. 将这批数据分配到最近的簇
  3. 使用增量更新方式更新簇中心:
    $$ \mu_k^{(t+1)} = (1 - \eta_t) \mu_k^{(t)} + \eta_t \cdot \text{新样本均值} $$

5.2.3 优点

  • 大幅提升计算速度
  • 适合大规模数据集
  • 内存占用少

5.2.4 实现

1
2
3
4
from sklearn.cluster import MiniBatchKMeans

mbkmeans = MiniBatchKMeans(n_clusters=3, batch_size=100, random_state=42)
mbkmeans.fit(X)

5.3 Canopy聚类(预处理加速)

5.3.1 原理

先使用快速但粗糙的Canopy算法进行预聚类,再在每个Canopy内运行K-means。

5.3.2 步骤

  1. 选择两个距离阈值 $T_1 > T_2$
  2. 随机选择一个点作为Canopy中心
  3. 将距离 $< T_1$ 的点加入Canopy
  4. 将距离 $< T_2$ 的点从候选集中移除
  5. 重复直到所有点都被处理

5.3.3 优点

  • 减少K-means的计算量
  • 适合超大规模数据

六、算法评估指标

6.1 内部指标(无需真实标签)

6.1.1 轮廓系数(Silhouette Coefficient)

已在4.2节详细介绍。

6.1.2 Calinski-Harabasz指数

$$ CH = \frac{\text{簇间离差平方和} / (K-1)}{\text{簇内离差平方和} / (n-K)} $$

  • 值越大,聚类效果越好
  • 计算速度快
1
2
3
4
from sklearn.metrics import calinski_harabasz_score

ch_score = calinski_harabasz_score(X, labels)
print(f"Calinski-Harabasz指数: {ch_score}")

6.1.3 Davies-Bouldin指数

$$ DB = \frac{1}{K} \sum_{i=1}^{K} \max_{j \neq i} \left( \frac{S_i + S_j}{d(\mu_i, \mu_j)} \right) $$

其中 $S_i$ 是簇 $i$ 的平均离散度。

  • 值越小,聚类效果越好
1
2
3
4
from sklearn.metrics import davies_bouldin_score

db_score = davies_bouldin_score(X, labels)
print(f"Davies-Bouldin指数: {db_score}")

6.2 外部指标(需要真实标签)

6.2.1 调整兰德指数(Adjusted Rand Index, ARI)

衡量聚类结果与真实标签的一致性。

1
2
3
4
from sklearn.metrics import adjusted_rand_score

ari = adjusted_rand_score(true_labels, predicted_labels)
print(f"调整兰德指数: {ari}")

6.2.2 归一化互信息(Normalized Mutual Information, NMI)

基于信息论的评估指标。

1
2
3
4
from sklearn.metrics import normalized_mutual_info_score

nmi = normalized_mutual_info_score(true_labels, predicted_labels)
print(f"归一化互信息: {nmi}")

6.2.3 同质性、完整性、V-measure

1
2
3
4
5
6
7
8
9
from sklearn.metrics import homogeneity_score, completeness_score, v_measure_score

homo = homogeneity_score(true_labels, predicted_labels)
comp = completeness_score(true_labels, predicted_labels)
v_measure = v_measure_score(true_labels, predicted_labels)

print(f"同质性: {homo}")
print(f"完整性: {comp}")
print(f"V-measure: {v_measure}")

七、优缺点分析

7.1 优点

简单高效:算法原理简单,易于理解和实现
计算速度快:时间复杂度 $O(n \cdot K \cdot I \cdot d)$,适合大规模数据
可扩展性好:有多种改进算法适应不同场景
结果直观:每个簇由质心代表,易于解释
广泛应用:在多个领域都有成功应用案例

7.2 缺点

需要预设K值:需要事先知道或估计簇的数量
对初始值敏感:不同初始化可能导致不同结果
只能发现凸形簇:无法处理非凸形状的簇(如环形、月牙形)
对异常值敏感:异常值会显著影响簇中心位置
假设簇大小相近:对大小差异很大的簇效果不佳
只能处理数值型数据:需要额外处理分类变量
局部最优问题:可能收敛到局部最优而非全局最优


八、应用场景

8.1 客户细分(Customer Segmentation)

8.1.1 应用场景

  • 市场营销:将客户分为不同群体,制定针对性策略
  • 产品推荐:基于用户群体特征推荐产品
  • 客户价值分析:识别高价值客户群体

8.1.2 特征选择

  • 消费金额
  • 购买频率
  • 最近购买时间
  • 产品偏好

8.2 图像分割(Image Segmentation)

8.2.1 应用场景

  • 图像压缩:将相似颜色像素归为一类
  • 目标检测:分割图像中的不同物体
  • 医学影像:分割器官、肿瘤等区域

8.2.2 实现方法

将每个像素的RGB值作为特征,进行聚类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
from sklearn.cluster import KMeans
from PIL import Image

# 读取图像
img = Image.open('image.jpg')
img_array = np.array(img)
h, w, c = img_array.shape

# 重塑为二维数组(每个像素一行)
X = img_array.reshape(-1, 3)

# K-means聚类
kmeans = KMeans(n_clusters=5, random_state=42)
labels = kmeans.fit_predict(X)

# 用簇中心颜色替换原像素
segmented_img = kmeans.cluster_centers_[labels]
segmented_img = segmented_img.reshape(h, w, 3).astype(np.uint8)

# 保存结果
result = Image.fromarray(segmented_img)
result.save('segmented_image.jpg')

8.3 文档聚类(Document Clustering)

8.3.1 应用场景

  • 新闻分类:将相似新闻归为一类
  • 主题建模:发现文档集合中的主题
  • 搜索引擎:相似文档推荐

8.3.2 特征提取

使用TF-IDF、词袋模型等将文档转换为向量。

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.feature_extraction.text import TfidfVectorizer

# 文档集合
documents = ["文本1", "文本2", "文本3", ...]

# TF-IDF向量化
vectorizer = TfidfVectorizer(max_features=1000)
X = vectorizer.fit_transform(documents)

# K-means聚类
kmeans = KMeans(n_clusters=5, random_state=42)
labels = kmeans.fit_predict(X)

8.4 异常检测(Anomaly Detection)

8.3.1 原理

异常点通常距离簇中心很远,可以通过距离判断。

1
2
3
4
5
6
7
8
9
10
11
# 计算每个点到其簇中心的距离
distances = []
for i in range(len(X)):
cluster = labels[i]
centroid = kmeans.cluster_centers_[cluster]
distance = np.linalg.norm(X[i] - centroid)
distances.append(distance)

# 设定阈值,识别异常点
threshold = np.percentile(distances, 95) # 95%分位数
anomalies = np.array(distances) > threshold

8.5 其他应用

  • 生物信息学:基因表达数据分析
  • 社交网络:社区发现
  • 金融风控:欺诈检测
  • 推荐系统:用户/物品聚类

九、实现示例

9.1 基础实现(Scikit-learn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler

# 1. 生成示例数据
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=42)

# 2. 特征标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 3. K-means聚类
kmeans = KMeans(n_clusters=4, init='k-means++', n_init=10, max_iter=300, random_state=42)
labels = kmeans.fit_predict(X_scaled)

# 4. 可视化结果
plt.figure(figsize=(12, 5))

# 原始数据
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c='gray', alpha=0.6)
plt.title('原始数据')
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.grid(True)

# 聚类结果
plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.6)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
c='red', marker='X', s=200, label='簇中心')
plt.title('K-means聚类结果 (K=4)')
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

# 5. 评估指标
from sklearn.metrics import silhouette_score, calinski_harabasz_score

silhouette = silhouette_score(X_scaled, labels)
ch_score = calinski_harabasz_score(X_scaled, labels)

print(f"轮廓系数: {silhouette:.4f}")
print(f"Calinski-Harabasz指数: {ch_score:.2f}")
print(f"簇中心:\n{kmeans.cluster_centers_}")

9.2 K值选择(肘部法 + 轮廓系数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# 生成数据
X, _ = make_blobs(n_samples=500, centers=4, cluster_std=0.8, random_state=42)

# 肘部法
sse = []
silhouette_scores = []
k_range = range(1, 11)

for k in k_range:
kmeans = KMeans(n_clusters=k, init='k-means++', n_init=10, random_state=42)
kmeans.fit(X)
sse.append(kmeans.inertia_)

if k > 1: # 轮廓系数需要至少2个簇
labels = kmeans.labels_
silhouette_scores.append(silhouette_score(X, labels))
else:
silhouette_scores.append(0)

# 绘制图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))

# 肘部图
ax1.plot(k_range, sse, 'bo-', linewidth=2, markersize=8)
ax1.set_xlabel('K值', fontsize=12)
ax1.set_ylabel('SSE', fontsize=12)
ax1.set_title('肘部法', fontsize=14)
ax1.grid(True, alpha=0.3)

# 轮廓系数图
ax2.plot(k_range, silhouette_scores, 'ro-', linewidth=2, markersize=8)
ax2.set_xlabel('K值', fontsize=12)
ax2.set_ylabel('轮廓系数', fontsize=12)
ax2.set_title('轮廓系数法', fontsize=14)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 推荐K值
best_k_silhouette = np.argmax(silhouette_scores[1:]) + 2
print(f"基于轮廓系数的最佳K值: {best_k_silhouette}")
print(f"最大轮廓系数: {max(silhouette_scores[1:]):.4f}")

9.3 图像压缩应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from PIL import Image
import urllib.request
from io import BytesIO

# 下载示例图片
url = "https://images.unsplash.com/photo-1501854140801-50d01698950b"
response = urllib.request.urlopen(url)
img = Image.open(BytesIO(response.read()))
img = img.resize((400, 300)) # 缩小图片

# 转换为numpy数组
img_array = np.array(img)
h, w, c = img_array.shape

# 重塑为二维数组(每个像素一行)
X = img_array.reshape(-1, 3)

# 不同K值的压缩效果对比
k_values = [2, 4, 8, 16, 32, 64]
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

for i, k in enumerate(k_values):
# K-means聚类
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)

# 用簇中心颜色替换
compressed_img = kmeans.cluster_centers_[labels]
compressed_img = compressed_img.reshape(h, w, 3).astype(np.uint8)

# 显示
ax = axes[i//3, i%3]
ax.imshow(compressed_img)
ax.set_title(f'K={k}(压缩比: {24/(3*np.log2(k)):.1f}x)', fontsize=12)
ax.axis('off')

plt.tight_layout()
plt.show()

# 原图对比
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
ax1.imshow(img)
ax1.set_title('原始图像', fontsize=14)
ax1.axis('off')

# 使用K=16的压缩图像
kmeans = KMeans(n_clusters=16, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
compressed_img = kmeans.cluster_centers_[labels]
compressed_img = compressed_img.reshape(h, w, 3).astype(np.uint8)

ax2.imshow(compressed_img)
ax2.set_title('压缩图像 (K=16)', fontsize=14)
ax2.axis('off')

plt.tight_layout()
plt.show()

9.4 客户细分实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# 1. 创建模拟客户数据
np.random.seed(42)
n_customers = 1000

data = {
'年龄': np.random.normal(35, 10, n_customers),
'年收入(万)': np.random.normal(50, 20, n_customers),
'消费金额(年)': np.random.normal(20, 10, n_customers),
'购买频率(月)': np.random.poisson(2, n_customers),
'会员时长(年)': np.random.uniform(0, 10, n_customers)
}

df = pd.DataFrame(data)

# 2. 数据预处理
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df)

# 3. 选择最佳K值
sse = []
for k in range(1, 11):
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
kmeans.fit(X_scaled)
sse.append(kmeans.inertia_)

plt.figure(figsize=(10, 5))
plt.plot(range(1, 11), sse, 'bo-')
plt.xlabel('K值')
plt.ylabel('SSE')
plt.title('肘部法选择最佳K值')
plt.grid(True)
plt.show()

# 4. 应用K-means (K=4)
kmeans = KMeans(n_clusters=4, random_state=42, n_init=10)
df['客户群体'] = kmeans.fit_predict(X_scaled)

# 5. 分析各群体特征
group_stats = df.groupby('客户群体').mean()
print("各客户群体特征均值:")
print(group_stats)

# 6. 可视化(使用PCA降维)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=df['客户群体'],
cmap='viridis', alpha=0.6, s=50)
plt.colorbar(scatter, label='客户群体')
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
c='red', marker='X', s=200, label='簇中心')
plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.2%}方差)')
plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.2%}方差)')
plt.title('客户群体可视化(PCA降维)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

# 7. 群体画像
group_names = {
0: '高价值忠诚客户',
1: '年轻潜力客户',
2: '普通大众客户',
3: '低频消费客户'
}

for i in range(4):
print(f"\n【{group_names[i]}】(群体{i})")
print(f" 样本数量: {sum(df['客户群体'] == i)}")
print(f" 平均年龄: {group_stats.loc[i, '年龄']:.1f}岁")
print(f" 平均年收入: {group_stats.loc[i, '年收入(万)']:.1f}万")
print(f" 平均年消费: {group_stats.loc[i, '消费金额(年)']:.1f}万")
print(f" 平均购买频率: {group_stats.loc[i, '购买频率(月)']:.1f}次/月")
print(f" 平均会员时长: {group_stats.loc[i, '会员时长(年)']:.1f}年")

十、工程实践建议

10.1 数据预处理

  1. 缺失值处理:删除或填充
  2. 异常值处理:识别并处理
  3. 特征标准化:必须进行标准化/归一化
  4. 特征选择:选择与聚类目标相关的特征
  5. 降维处理:高维数据可使用PCA降维

10.2 参数调优

  1. K值选择:结合肘部法、轮廓系数等方法
  2. 初始化方法:优先使用K-means++
  3. 运行次数:设置n_init参数多次运行取最优
  4. 最大迭代次数:根据数据规模调整

10.3 结果验证

  1. 多次运行:检查结果稳定性
  2. 可视化分析:使用PCA/t-SNE降维可视化
  3. 业务验证:结合业务知识判断合理性
  4. 对比实验:与其他聚类算法对比

10.4 常见陷阱

  1. 忽略特征缩放:不同量纲的特征会影响结果
  2. 盲目选择K值:需要多种方法综合判断
  3. 忽视数据分布:非凸形数据不适合K-means
  4. 过度解读结果:聚类结果需要业务验证

十一、与其他聚类算法对比

特性 K-means 层次聚类 DBSCAN 高斯混合模型
是否需要预设K
处理非凸簇
对异常值敏感
计算复杂度 $O(n)$ $O(n^2)$ $O(n \log n)$ $O(n)$
可扩展性 ✓✓✓ ✓✓ ✓✓
概率输出

总结

K-means聚类作为最经典、最常用的聚类算法,具有以下特点:

核心优势

  • 简单高效:易于理解和实现
  • 计算快速:适合大规模数据
  • 应用广泛:在多个领域都有成功案例

使用要点

  1. 必须进行特征标准化
  2. 合理选择K值(肘部法 + 轮廓系数)
  3. 使用K-means++初始化
  4. 多次运行取最优结果

适用场景

  • 数据分布近似球形
  • 簇大小相近
  • 簇间分离度高
  • 需要快速聚类结果

局限性

  • 无法处理非凸形簇
  • 对异常值敏感
  • 需要预设K值
  • 可能收敛到局部最优

掌握K-means不仅是学习聚类算法的基础,更是理解更复杂无监督学习方法的起点。在实际应用中,应结合数据特点和业务需求,灵活选择和调整算法参数。