哈喽,我是小白~
今儿和大家聊一个关于聚类算法,最基础的一个算法模型:K-means~
首先,聚类算法是一种无监督学习算法,用来将一组数据点根据它们的特性分成若干组,组内的数据点尽量相似,而组间的数据点尽量不同。通俗地说,聚类就是把看起来“差不多”的东西分成一类。
其中,K-means 是一种非常经典的聚类算法。
如果需要本文PDF版本的同学,文末获取~
另外,文末有总结性的干货~
一起来看下具体细化内容~
K-means 是什么?
K-means 核心思想
1. 目标:把数据分成 个簇(群组)。
2. 方法:
找到每一簇的“中心点”。 把数据点分配到离它最近的中心点所属的簇。 反复调整中心点的位置,直到分组稳定。
通俗案例:把水果分成 类
假设你有一筐水果,它们有两个特性:
1. 重量(克),例如苹果重 200 克,香蕉轻 100 克。
2. 甜度(0-10 分),例如苹果甜度 7,香蕉甜度 5。
你的目标是把这些水果分成两类:重甜的水果 和 轻甜的水果。
数据表如下:
水果 | 重量(克) | 甜度 |
---|---|---|
A | 200 | 8 |
B | 180 | 7 |
C | 120 | 6 |
D | 100 | 5 |
E | 150 | 6 |
我们可以把每个水果看成一个点,用它的“重量”和“甜度”表示在二维空间中的位置。例如,A 的坐标是 (200, 8)。
K-means 计算步骤
1. 初始化中心点:
随机选择两个点作为初始中心点。例如:
初始中心点 (A 的位置)。 初始中心点 (D 的位置)。
2. 计算每个点到中心点的距离:
使用欧几里得距离公式计算:
对每个水果计算它到 和 的距离:
A 到 : A 到 : 类似地计算其他点的距离。
结果: | 水果 | 到 的距离 | 到 的距离 | 分配到哪一类 | | - | - | - | | | A | 0 | 100.04 | C1 | | B | 22.36 | 85.03 | C1 | | C | 80.62 | 20.22 | C2 | | D | 100.04 | 0 | C2 | | E | 50.60 | 50.24 | C2 |
3. 更新中心点:
重新计算每一簇的中心点位置(簇内所有点的平均值):
:(200, 8) 和 (180, 7) 的均值是 。 :(120, 6)、(100, 5)、(150, 6) 的均值是 。
4. 重复步骤 2 和 3:
重新分配点到新中心点,并更新中心点,直到分组不再变化。
结果
经过多次迭代,最终可能得到以下分组:
C1 类(重甜):A、B。 C2 类(轻甜):C、D、E。
中心点稳定在:
。 。
公式解析
1. 问题定义
设有一个数据集 ,其中每个数据点 是 -维空间中的一个向量。我们希望将这些数据点划分为 个簇,使得每个簇内的数据点彼此接近。
目标函数
K-means 的目标是最小化以下函数(簇内平方误差总和):
其中:
是第 个簇的集合。 是第 个簇的质心(中心点),定义为该簇内所有点的均值:
表示点 到其簇中心 的欧几里得距离平方。
2. 算法过程的数学推导
初始化
随机选择 个点作为初始簇中心:。
分配步骤(Assignment Step)
将每个数据点 分配到离它最近的簇中心:
这里, 是一个指示变量(binary indicator),如果数据点 属于第 个簇,则 ;否则 。
更新步骤(Update Step)
更新每个簇的质心 ,使其为该簇内所有点的均值:
其中:
分子是第 个簇中所有点的坐标总和。 分母是第 个簇的点的数量。
迭代
重复分配步骤和更新步骤,直到簇中心不再发生显著变化(收敛)。
3. 数学推导的细节
目标函数的优化
目标函数:
通过两步优化 :
1. 固定簇中心 ,优化簇分配 :
对每个点 ,找到离它最近的簇中心,分配给相应的簇。这是通过最小化 实现的。
2. 固定簇分配 ,优化簇中心 :
对于每个簇 ,重新计算中心点 ,使得所有点到中心点的距离平方和最小。 最优质心是簇内所有点的均值:
收敛性分析
K-means 算法的核心思想是每次迭代都让目标函数 单调递减:
分配步骤保证每个点分配到最近的簇,因此 不增加。 更新步骤通过重新计算质心使簇内距离平方和最小化,因此 也不会增加。
因为 的值是非负的,且每次迭代都单调递减,所以算法一定会收敛。
距离计算公式
在 K-means 中,通常采用欧几里得距离计算两个点之间的距离:
其中 是点 在第 维的坐标。
4. 示例总结
一个二维数据示例:
数据点:。 初始中心点:。 根据上述公式逐步分配点、更新质心,最终得到两个簇 ,。
总结公式
1. 目标函数:
2. 簇分配:
3. 更新质心:
优缺点和适用场景
优点
1. 简单易实现:算法直观,计算简单,容易编码实现。
2. 高效:计算复杂度为 ,适合大规模数据( 为数据点数, 为簇数, 为迭代次数)。
3. 结果可解释性强:簇中心提供了直观的代表性,便于理解。
4. 适用范围广:可用于多种类型的问题,如图像分割、推荐系统等。
缺点
1. 对初始值敏感:初始簇中心的选择会显著影响结果,可能陷入局部最优。
**2. 需要指定 **:需要提前知道簇的数量,但实际中很难确定。
3. 对噪声和离群点敏感:离群点可能显著拉动簇中心,导致结果不稳定。
4. 仅适合球状分布:对形状复杂的簇(如非凸形状)表现较差。
5. 假设均方误差(MSE)最小:假设所有簇的方差相等,不适合簇间方差差异较大的情况。
适用场景
1. 簇之间是球状分布,且簇内方差相近:数据点的分布较为简单,例如分组销售数据。
2. 数据维度较低:高维数据可能需要降维处理(如 PCA),以提高效果。
3. 需要快速实现聚类:对实时性要求高时,K-means 的高效性使其成为首选。
完整案例
这里咱们会模拟客户行为数据(包括消费金额和购买频率),使用 K-means 算法进行聚类,并通过多个鲜艳、炫酷的图形展示聚类效果和分析结果。
会进行以下数据分析的过程:
数据点的分布及聚类结果。
聚类后簇的轮廓系数图,评估聚类质量。
每个簇的客户特征分布(柱状图或雷达图)。
实现代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, silhouette_samples
from matplotlib import cm
# 1. 数据生成:模拟客户消费数据
np.random.seed(42)
n_samples = 500
# 模拟三个簇的客户分布
cluster_1 = np.random.normal(loc=[20, 5], scale=[5, 2], size=(150, 2))
cluster_2 = np.random.normal(loc=[50, 20], scale=[7, 3], size=(200, 2))
cluster_3 = np.random.normal(loc=[30, 40], scale=[5, 5], size=(150, 2))
# 合并数据
data = np.vstack([cluster_1, cluster_2, cluster_3])
data = pd.DataFrame(data, columns=["Spending", "Frequency"])
# 2. 使用 K-means 进行聚类
n_clusters = 3
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
labels = kmeans.fit_predict(data)
# 获取簇中心
centers = kmeans.cluster_centers_
# 计算轮廓分数
silhouette_avg = silhouette_score(data, labels)
silhouette_vals = silhouette_samples(data, labels)
# 3. 图形可视化
## 图1:数据点分布及聚类结果
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
scatter = plt.scatter(data['Spending'], data['Frequency'], c=labels, cmap='viridis', s=50, alpha=0.6)
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, marker='X', label='Cluster Centers')
plt.title("K-means Clustering Results", fontsize=16, fontweight='bold')
plt.xlabel("Spending ($)", fontsize=12)
plt.ylabel("Frequency (times/month)", fontsize=12)
plt.legend(loc='upper right')
plt.grid(color='lightgrey', linestyle='--')
plt.colorbar(scatter, label='Cluster Label')
## 图2:簇的轮廓系数
plt.subplot(1, 2, 2)
y_lower = 10
colors = cm.viridis(np.linspace(0, 1, n_clusters))
for i, color in enumerate(colors):
cluster_silhouette_vals = silhouette_vals[labels == i]
cluster_silhouette_vals.sort()
size_cluster_i = cluster_silhouette_vals.shape[0]
y_upper = y_lower + size_cluster_i
plt.fill_betweenx(np.arange(y_lower, y_upper), 0, cluster_silhouette_vals, facecolor=color, alpha=0.7)
plt.text(-0.05, y_lower + 0.5 * size_cluster_i, f"Cluster {i+1}", fontsize=10)
y_lower = y_upper + 10
plt.axvline(x=silhouette_avg, color="red", linestyle="--", label=f"Average Silhouette ({silhouette_avg:.2f})")
plt.title("Silhouette Analysis for K-means Clustering", fontsize=16, fontweight='bold')
plt.xlabel("Silhouette Coefficient", fontsize=12)
plt.ylabel("Cluster", fontsize=12)
plt.legend(loc='upper right')
plt.grid(color='lightgrey', linestyle='--')
plt.tight_layout()
plt.show()
## 图3:每个簇的特征分布柱状图
cluster_summary = data.copy()
cluster_summary['Cluster'] = labels
cluster_means = cluster_summary.groupby('Cluster').mean()
cluster_means.plot(kind='bar', figsize=(10, 6), color=['#FF6F61', '#6B5B95'], alpha=0.8)
plt.title("Cluster Feature Distribution", fontsize=16, fontweight='bold')
plt.xlabel("Cluster", fontsize=12)
plt.ylabel("Average Value", fontsize=12)
plt.xticks(rotation=0)
plt.grid(color='lightgrey', linestyle='--', axis='y')
plt.legend(["Spending ($)", "Frequency"], loc='upper left')
plt.tight_layout()
plt.show()
# 可选:雷达图
def plot_radar_chart(data, title):
categories = list(data.columns)
num_vars = len(categories)
# 角度
angles = np.linspace(0, 2 * np.pi, num_vars, endpoint=False).tolist()
angles += angles[:1]
# 数据处理
data_values = data.values.flatten().tolist()
data_values += data_values[:1]
# 绘制雷达图
fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True))
ax.fill(angles, data_values, color="#FF6F61", alpha=0.25)
ax.plot(angles, data_values, color="#FF6F61", linewidth=2)
ax.set_yticks([])
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories)
ax.set_title(title, fontsize=14, fontweight='bold')
plt.show()
# 绘制每个簇的雷达图
for i in range(n_clusters):
plot_radar_chart(cluster_means.iloc[[i]], f"Cluster {i+1} Feature Radar")
图1:数据点分布及聚类结果
每个数据点的颜色表示其所属簇。 红色的 X
表示簇的中心位置。
图2:簇的轮廓系数
每个簇的轮廓系数分布用于评估聚类的质量。 红色虚线表示平均轮廓系数,越接近 1 越好。
图3:每个簇的特征分布柱状图
对每个簇的平均消费金额和购买频率进行了柱状图展示,帮助理解簇的特征。
附加:每个簇的雷达图
使用雷达图展示每个簇在各特征上的具体表现,视觉效果更直观。
最后