最强聚类算法,K-means!!

文摘   2024-11-22 11:16   北京  

哈喽,我是小白~

今儿和大家聊一个关于聚类算法,最基础的一个算法模型:K-means~

首先,聚类算法是一种无监督学习算法,用来将一组数据点根据它们的特性分成若干组,组内的数据点尽量相似,而组间的数据点尽量不同。通俗地说,聚类就是把看起来“差不多”的东西分成一类。

其中,K-means 是一种非常经典的聚类算法。

如果需要本文PDF版本的同学,文末获取~

另外,文末有总结性的干货~

一起来看下具体细化内容~

K-means 是什么?

K-means 核心思想

1. 目标:把数据分成  个簇(群组)。

2. 方法

  • 找到每一簇的“中心点”。
  • 把数据点分配到离它最近的中心点所属的簇。
  • 反复调整中心点的位置,直到分组稳定。

通俗案例:把水果分成  类

假设你有一筐水果,它们有两个特性:

1. 重量(克),例如苹果重 200 克,香蕉轻 100 克。

2. 甜度(0-10 分),例如苹果甜度 7,香蕉甜度 5。

你的目标是把这些水果分成两类:重甜的水果 和 轻甜的水果

数据表如下:

水果重量(克)甜度
A2008
B1807
C1206
D1005
E1506

我们可以把每个水果看成一个点,用它的“重量”和“甜度”表示在二维空间中的位置。例如,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 算法的核心思想是每次迭代都让目标函数  单调递减:

  1. 分配步骤保证每个点分配到最近的簇,因此  不增加。
  2. 更新步骤通过重新计算质心使簇内距离平方和最小化,因此  也不会增加。

因为  的值是非负的,且每次迭代都单调递减,所以算法一定会收敛。

距离计算公式

在 K-means 中,通常采用欧几里得距离计算两个点之间的距离:

其中  是点  在第  维的坐标。

4. 示例总结

一个二维数据示例:

  • 数据点:
  • 初始中心点:
  • 根据上述公式逐步分配点、更新质心,最终得到两个簇 

总结公式

1. 目标函数

2. 簇分配

3. 更新质心

优缺点和适用场景

优点

1. 简单易实现:算法直观,计算简单,容易编码实现。

2. 高效:计算复杂度为 ,适合大规模数据( 为数据点数, 为簇数, 为迭代次数)。

3. 结果可解释性强:簇中心提供了直观的代表性,便于理解。

4. 适用范围广:可用于多种类型的问题,如图像分割、推荐系统等。

缺点

1. 对初始值敏感:初始簇中心的选择会显著影响结果,可能陷入局部最优。

**2. 需要指定 **:需要提前知道簇的数量,但实际中很难确定。

3. 对噪声和离群点敏感:离群点可能显著拉动簇中心,导致结果不稳定。

4. 仅适合球状分布:对形状复杂的簇(如非凸形状)表现较差。

5. 假设均方误差(MSE)最小:假设所有簇的方差相等,不适合簇间方差差异较大的情况。

适用场景

1. 簇之间是球状分布,且簇内方差相近:数据点的分布较为简单,例如分组销售数据。

2. 数据维度较低:高维数据可能需要降维处理(如 PCA),以提高效果。

3. 需要快速实现聚类:对实时性要求高时,K-means 的高效性使其成为首选。

完整案例

这里咱们会模拟客户行为数据(包括消费金额和购买频率),使用 K-means 算法进行聚类,并通过多个鲜艳、炫酷的图形展示聚类效果和分析结果。

会进行以下数据分析的过程:

  1. 数据点的分布及聚类结果。

  2. 聚类后簇的轮廓系数图,评估聚类质量。

  3. 每个簇的客户特征分布(柱状图或雷达图)。

实现代码

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=[205], scale=[52], size=(1502))
cluster_2 = np.random.normal(loc=[5020], scale=[73], size=(2002))
cluster_3 = np.random.normal(loc=[3040], scale=[55], size=(1502))

# 合并数据
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=(146))
plt.subplot(121)
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(122)
y_lower = 10
colors = cm.viridis(np.linspace(01, 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=(106), 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(02 * 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=(66), 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:每个簇的特征分布柱状图

  • 对每个簇的平均消费金额和购买频率进行了柱状图展示,帮助理解簇的特征。

附加:每个簇的雷达图

使用雷达图展示每个簇在各特征上的具体表现,视觉效果更直观。

最后

以上就是今天所有的内容了。
获取本文PDF,点击名片回复「基础算法模型」即可~
 
另外, 我们整理了机器学习算法册子,总共16大块的内容,124个问题的总结点击领取!
如果对你来说比较有用,记得点赞、转发,收藏起来慢慢学习~
下期会有更多干货等着你!~

Python和机器学习初学者
Python和机器学习分享,只写干货,一起学习~
 最新文章