当前位置: 首页 > article >正文

K-means算法在无监督学习中的应用

K-means算法在无监督学习中的应用

K-means算法是一种典型的无监督学习算法,广泛用于聚类分析。在无监督学习中,模型并不依赖于标签数据,而是根据输入数据的特征进行分组。K-means的目标是将数据集分成K个簇,使得同一簇内的数据点相似度较高,而不同簇之间的数据点相似度较低。它通过寻找数据中的潜在结构来自动划分数据。

K-means算法广泛应用于聚类分析图像分割异常检测等领域。本文将详细介绍K-means算法的原理、数学模型、实现步骤,并从这三个应用方向举例讲解K-means在无监督学习中的应用。


1. K-means算法的原理和数学模型

1.1 K-means算法的基本思想

K-means算法是一种基于划分的聚类方法,目标是将数据集分成K个簇,并通过最小化簇内误差平方和(SSE, Sum of Squared Errors)来实现数据的聚类。K-means算法的核心在于通过不断地更新簇的中心(质心)和重新分配数据点,直到算法收敛。

1.2数学模型

假设我们有一个包含n个数据点的数据集,数据点的特征是d维的。K-means算法的目标是将数据集划分为K个簇,最小化每个簇内的误差平方和。具体的数学模型如下:

  1. 初始化: 随机选择K个数据点作为初始簇的中心(质心),记为 C 1 , C 2 , … , C K C_1, C_2, \dots, C_K C1,C2,,CK

  2. 分配步骤: 将每个数据点 x i x_i xi 分配到距离它最近的簇中心。距离度量通常使用欧氏距离:
    d ( x i , C k ) = ∥ x i − C k ∥ 2 d(x_i, C_k) = \| x_i - C_k \|^2 d(xi,Ck)=xiCk2
    数据点 x i x_i xi 被分配到最近的簇中心 C k C_k Ck

  3. 更新步骤: 更新每个簇的质心,新的质心是该簇所有数据点的均值:
    C k = 1 ∣ S k ∣ ∑ x i ∈ S k x i C_k = \frac{1}{|S_k|} \sum_{x_i \in S_k} x_i Ck=Sk1xiSkxi
    其中, S k S_k Sk 是簇 k k k 中的所有数据点, ∣ S k ∣ |S_k| Sk 是簇的大小。

  4. 停止条件: 重复分配和更新步骤,直到簇中心不再发生变化或达到最大迭代次数。

K-means的目标是最小化簇内误差平方和(SSE),公式如下:
S S E = ∑ k = 1 K ∑ x i ∈ S k ∥ x i − C k ∥ 2 SSE = \sum_{k=1}^K \sum_{x_i \in S_k} \| x_i - C_k \|^2 SSE=k=1KxiSkxiCk2
K-means通过迭代更新簇中心来减少SSE,从而实现最优的簇划分。


2. K-means的实现步骤

K-means算法的实现过程可以分为以下几个主要步骤:

  1. 数据预处理
    • 在应用K-means之前,通常需要对数据进行预处理,包括标准化和降维,尤其是当数据特征维度较高时。
  2. 选择K值
    • K值表示簇的数量。在实际应用中,K的选择非常重要,常用的方法有肘部法则(Elbow Method)来确定最佳K值。
  3. 初始化簇中心
    • 选择K个数据点作为初始簇中心,常用的初始化方法有随机选择K-means++
  4. 迭代更新
    • 分配每个数据点到最近的簇中心。
    • 更新簇中心为簇内所有数据点的均值。
    • 重复步骤直到簇中心不再变化。
  5. 评估聚类效果
    • 可以使用SSE、轮廓系数等指标来评估聚类效果。

3. KMeans类的参数解释

from sklearn.cluster import KMeans

# n_clusters: 簇的数量,即 K 值
# init: 初始化中心点的方法,默认为 'k-means++',一种启发式方法来选择初始簇中心以加速收敛
# n_init: 运行 k-means 算法的次数,选择具有最小 SSE 的结果,默认为 'auto' 根据数据集大小自动选择
# max_iter: 单次运行的最大迭代次数,达到最大迭代次数或收敛时停止迭代
# tol: 收敛阈值,两次迭代误差小于此值即认为算法已收敛
# verbose: 是否输出详细信息,默认为 0,不输出
# random_state: 随机数生成器的种子,用于初始化中心点,保持结果可重复
# copy_x: 是否复制输入数据,默认为 True,表示复制输入数据
# algorithm: 计算的算法,'auto', 'full', 'elkan','auto'根据数据自动选择算法,'full'使用传统的 EM 算法,'elkan'是一种更有效的 K-means 算法

kmeans = KMeans(
    n_clusters=8,
    init='k-means++',
    n_init='auto',
    max_iter=300,
    tol=0.0001,
    verbose=0,
    random_state=None,
    copy_x=True,
    algorithm='lloyd'
)

kmeans.fit(X)

4. 确定最优K值

4.1 数据预处理(前摇)

from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA

import matplotlib.pyplot as plt
import seaborn as sns

sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})  # 设置主题和字体

# 加载数据集
iris = load_iris()
X = iris.data

# 使用PCA降维到二维,以便可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

4.2 肘部法(Elbow Method)

通过绘制不同K值下的SSE(误差平方和),并寻找SSE快速下降的“肘部”位置。

# 计算不同K值下的SSE
sse = []
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k)
    kmeans.fit(X_pca)  # 这里X_pca是经过PCA降维后的数据
    sse.append(kmeans.inertia_)

# 绘制SSE曲线
plt.figure(figsize=(8, 6))  # 设置图表大小
plt.plot(range(1, 11), sse, marker='o', color='b', linestyle='-', markersize=8)

# 设置中文标题和标签
plt.title('肘部法则选择最佳K值', fontsize=14)
plt.xlabel('簇的数量 (K)', fontsize=12)
plt.ylabel('SSE', fontsize=12)

# 显示图表
plt.show()

download

4.3 轮廓系数(Silhouette Score)

衡量聚类质量的指标,轮廓系数越大,表示聚类效果越好。

# 计算不同K值下的轮廓系数
silhouette_scores = []
for k in range(2, 11):  # 由于轮廓系数要求至少有两个簇
    kmeans = KMeans(n_clusters=k)
    cluster_labels = kmeans.fit_predict(X_pca)
    silhouette_avg = silhouette_score(X_pca, cluster_labels)
    silhouette_scores.append(silhouette_avg)

# 绘制轮廓系数曲线
plt.figure(figsize=(8, 6))  # 设置图表大小
plt.plot(range(2, 11), silhouette_scores, marker='o', color='b', linestyle='-', markersize=8)

# 设置中文标题和标签
plt.title('轮廓系数选择最佳K值', fontsize=14)
plt.xlabel('簇的数量 (K)', fontsize=12)
plt.ylabel('轮廓系数', fontsize=12)

# 显示图表
plt.show()

download

4.4 遍历K值(肉眼比较法)

通过遍历不同K值,并比较它们的可视化效果。
(当使用 肘部法轮廓系数 无法确定K值时候,可遍历K值根据可视化结果比较作为参考)

# 设置图表的整体大小
fig, axes = plt.subplots(2, 4, figsize=(18, 8), sharex=True, sharey=True)  # 2行5列的子图排布

# 遍历聚类数量1到8
for k in range(1, 9):
    kmeans = KMeans(n_clusters=k)
    kmeans.fit(X_pca)  # 用降维后的数据进行聚类
    labels = kmeans.labels_  # 聚类标签

    # 选择子图位置
    ax = axes[(k - 1) // 4, (k - 1) % 4]

    # 绘制每个子图
    ax.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap='viridis', s=50, alpha=0.6)
    ax.set_title(f'k = {k}', fontsize=12)
    ax.set_xlabel('特征1', fontsize=10)
    ax.set_ylabel('特征2', fontsize=10)
    ax.grid(True)

# 调整布局,防止标签重叠
plt.tight_layout()
plt.show()

download


5. K-means算法的应用

K-means算法广泛应用于多个领域,以下文中三个常见的应用方向进行举例:聚类分析图像分割异常检测

5.1 聚类分析

聚类分析旨在将数据集划分为多个簇,使得簇内的数据点相似度高,簇间的数据点相似度低。

5.1.1 数据加载、降维并聚类

from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA

import matplotlib.pyplot as plt
import seaborn as sns

sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})  # 设置主题和字体

# 加载数据集
iris = load_iris()
X = iris.data

# 使用PCA降维到二维,以便可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

# 聚类
kmeans = KMeans(n_clusters=3, random_state=42)
clusters = kmeans.fit_predict(X_pca)

5.1.2 绘制聚类散点图

# 可视化聚类结果
plt.figure(figsize=(10, 8))  # 调整画布大小
sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})  # 设置主题和字体

# 绘制散点图
scatter = sns.scatterplot(
    x=X_pca[:, 0],
    y=X_pca[:, 1],
    hue=clusters,
    palette='bright',
    style=clusters,
    markers=["o", "s", "D"],
    s=100,  # 调整点的大小
    edgecolor='black',  # 添加边框颜色
    linewidth=0.8,  # 边框线宽
    alpha=0.8,  # 透明度
)

# 设置标题和轴标签
plt.title("K-Means聚类结果", fontsize=18, fontweight="bold", pad=10)
plt.xlabel("主成分1", fontsize=14, labelpad=10)
plt.ylabel("主成分2", fontsize=14, labelpad=10)

# 设置轴刻度字体大小
plt.tick_params(axis='x', labelsize=12)
plt.tick_params(axis='y', labelsize=12)

# 设置图例样式
legend = plt.legend(
    title="聚类类别",
    title_fontsize=13,
    fontsize=12,
    loc='lower right',  # 调整图例位置
    bbox_to_anchor=(0.975, 0.025),  # 向左上偏移0.1
    frameon=True,  # 添加图例边框
    shadow=True,  # 添加阴影
    fancybox=True,  # 圆角边框
    borderpad=1  # 内边距
)
legend.get_frame().set_linewidth(1.2)  # 设置图例边框线宽

# 添加网格线样式
plt.grid(color='gray', linestyle='--', linewidth=0.8, alpha=0.3)

# 调整布局
plt.tight_layout()

# 显示图表
plt.show()

download

5.1.3 添加轮廓线

# 绘制轮廓线
sns.kdeplot(
    x=X_pca[:, 0],
    y=X_pca[:, 1],
    hue=clusters,
    palette='bright',
    levels=5,
    alpha=0.5,
    linewidths=1.5,
    ax=scatter,
)

download

5.1.4 添加簇中心点

# 将中心点转换到PCA空间
centers = kmeans.cluster_centers_
plt.scatter(
    centers[:, 0], centers[:, 1],
    c='red', marker='X', s=200, label='聚类中心', edgecolor='black', linewidth=2
)  # 绘制中心点

download


5.2 图像分割

图像分割是指将图像划分为多个区域,每个区域的像素在颜色、纹理等特征上具有相似性。K-means算法可以用于图像分割,将图像的每个像素聚类到不同的簇中,从而实现对图像的分割。

5.2.1 导入图片

from sklearn.datasets import load_sample_image
import matplotlib.pyplot as plt

# 加载“chinajpg”样本图像
flower = load_sample_image('china.jpg')

# 显示图片
plt.figure(figsize=(6, 6))
plt.imshow(flower)
plt.axis('off')  # 禁用轴
plt.show()

download

5.2.2 分割过程

from sklearn.cluster import KMeans
import numpy as np

# 加载图像并转换为numpy数组
china = load_sample_image("china.jpg")
china = np.array(china)

# 将图像转为二维数组(每个像素为一个数据点)
X = china.reshape(-1, 3)  # 3是RGB通道

# K-means聚类,设定聚类数为4
kmeans = KMeans(n_clusters=3, random_state=1)
kmeans.fit(X)

# 获取聚类标签
labels = kmeans.labels_

# 将标签重新变为图像的形状
segmented_image = labels.reshape(china.shape[0], china.shape[1])

5.2.3 可视化

import seaborn as sns
from mpl_toolkits.mplot3d import Axes3D  # 可视化RGB三维散点图

sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})  # 设置主题和字体

# 提取RGB通道数据
R = X[:, 0]
G = X[:, 1]
B = X[:, 2]

# 创建3D散点图
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

# 使用透明度(alpha)调整点的透明度
scatter = ax.scatter(R, G, B, c=labels, cmap='viridis', s=5, alpha=0.5)  # alpha为透明度

# 设置坐标轴标签和标题
ax.set_xlabel('R', fontsize=12, labelpad=10)
ax.set_ylabel('G', fontsize=12, labelpad=10)
ax.set_zlabel('B', fontsize=12, labelpad=10)
ax.set_title('K-means 聚类 RGB 三维散点图', fontsize=14)

# 设置更精致的坐标轴刻度
ax.tick_params(axis='both', which='major', labelsize=10)

# 显示图形
plt.show()

download

# 可视化图像分割结果
plt.figure(figsize=(8, 6))
plt.imshow(segmented_image, cmap='viridis')
plt.title('图像分割:K-means聚类', fontsize=14)
# plt.axis('off')  # 禁用坐标轴
plt.show()

download

5.2.4 拆分结果

# 将标签重新变为图像的形状
segmented_image_list = []

# 创建每个簇的分割图像
for cluster in range(3):  # 假设我们有3个簇
    # 创建一个全为0的图像(与原图尺寸一致)
    segmented_image = np.zeros_like(china)
    
    # 将属于该簇的像素赋值为原图像素,其他像素设置为黑色
    # 获取属于该簇的像素的索引
    cluster_pixels = labels == cluster
    # 将符合条件的像素赋值
    segmented_image.reshape(-1, 3)[cluster_pixels] = china.reshape(-1, 3)[cluster_pixels]
    
    # 添加到分割图像列表中
    segmented_image_list.append(segmented_image)

# 创建子图布局
fig, axes = plt.subplots(1, 3, figsize=(12, 10))

# 绘制每个聚类的分割图像
for i, ax in enumerate(axes.flat):
    ax.imshow(segmented_image_list[i])  # 显示分割后的图像
    ax.set_title(f'Cluster {i+1}', fontsize=14)  # 设置标题为聚类编号
    ax.axis('off')  # 禁用坐标轴

# 调整布局,防止重叠
plt.tight_layout()
plt.show()

download


5.3 异常检测

K-means算法也可用于异常检测,尤其是在数据集中存在噪声或异常点时。通过计算数据点到簇中心的距离,可以识别那些远离簇中心的点作为异常点。这些异常点可能代表了异常行为或错误数据。

5.3.1 生成原始数据集并可视化

from sklearn.cluster import KMeans
import numpy as np
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
import seaborn as sns

# 设置主题和字体
sns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})

# 生成模拟数据:两个明显簇和少量离群点
np.random.seed(42)
cluster_1 = np.random.normal(loc=[5, 5], scale=1.5, size=(100, 2))
cluster_2 = np.random.normal(loc=[15, 15], scale=1.5, size=(100, 2))
outliers = np.random.uniform(low=[0, 0], high=[20, 20], size=(10, 2))
X = np.vstack([cluster_1, cluster_2, outliers])

# 可视化样本
plt.figure(figsize=(8, 6))
x = X[:, 0]
y = X[:, 1]
plt.scatter(x, y, s=20, c='gray', alpha=0.7, label='数据点')
plt.title("数据点分布图", fontsize=16)
plt.xlabel("X轴", fontsize=12)
plt.ylabel("Y轴", fontsize=12)
plt.grid(color='gray', linestyle='--', linewidth=0.8, alpha=0.3)  # 添加网格线样式
plt.legend()
plt.show()

download

5.3.2 聚类并进行异常值比较

# KMeans算法拟合
kmeans_model = KMeans(n_clusters=2, random_state=42).fit(X)

# 聚类中心
centroids = kmeans_model.cluster_centers_

# 每个样本到聚类中心的欧式距离
D = cdist(X, centroids, 'euclidean')

# 每个样本的最近聚类中心索引
cluster_labels = D.argmin(axis=1)

# 每个样本到所属聚类中心的距离
distances_to_cluster = np.min(D, axis=1)

# 设置异常点的距离阈值
threshold = 4.0

# 异常点判定:仅比较点与其所属簇中心的距离
outliers = X[distances_to_cluster > threshold]

array([[15.77257153, 20.77909724],
[18.47198785, 12.19910221],
[10.13809899, 13.46341854],
[18.19955006, 12.0718683 ],
[ 1.12750993, 17.29444753],
[16.25802018, 19.99435347],
[19.93273674, 11.10863411],
[16.99294781, 4.94696203],
[ 9.01088271, 2.5831883 ],
[19.08102055, 12.12349269],
[ 4.57285611, 13.43401369],
[12.36256481, 7.16325436]])

5.3.3 可视化异常值检测结果

# 可视化
plt.figure(figsize=(10, 8))

# 绘制簇的点
palette = sns.color_palette("husl", 2)
for i in range(2):  # 遍历每个簇
    plt.scatter(X[cluster_labels == i, 0], X[cluster_labels == i, 1], 
                s=50, color=palette[i], alpha=0.7, label=f"簇 {i+1}", edgecolor='k', marker='o')

# 绘制聚类中心
plt.scatter(centroids[:, 0], centroids[:, 1], 
            s=200, c='black', marker='X', label='聚类中心', zorder=5)

# 绘制离群点
if len(outliers) > 0:
    plt.scatter(outliers[:, 0], outliers[:, 1], 
                s=100, c='red', label='离群点', edgecolor='k', zorder=6)

# 绘制分界线
for center in centroids:
    circle = plt.Circle(center, threshold, color='gray', alpha=0.3, fill=True, linestyle='--')
    plt.gca().add_artist(circle)

# 图表设置
plt.title("KMeans 聚类与优化后的异常值检测", fontsize=16)
plt.xlabel("X轴", fontsize=12)
plt.ylabel("Y轴", fontsize=12)
plt.grid(color='gray', linestyle='--', linewidth=0.8, alpha=0.3)  # 添加网格线样式
plt.legend(loc='upper left', fontsize=10)
plt.tight_layout()
plt.show()

download


http://www.kler.cn/a/473305.html

相关文章:

  • 怎样修改el-table主题样式
  • 无网络时自动切换备用网络环境
  • 第3章:Go语言复合数据类型
  • jenkins 使用 ssh-agent向windows进行部署
  • 25上软考中级【软件设计师】易混淆知识点
  • 【年前假期学SHU分享】:计算机生物学、智能计算、通信、大数据、电子信息工程
  • 网络安全-XSS跨站脚本攻击(基础篇)
  • C++虚函数(八股总结)
  • 深入理解 JSON 数据传递方式:数组格式与对象包装格式的对比与选择
  • 力扣第141题:环形链表 C语言解法
  • CentOS: RPM安装、YUM安装、编译安装(详细解释+实例分析!!!)
  • ExcelDataReader:一个.Net高性能Excel开源读取器
  • 游戏引擎学习第76天
  • 将txt转成excel正则化公式的调整
  • 超完整Docker学习记录,Docker常用命令详解
  • 前后端实现防抖节流实现
  • HTB:Bank[WriteUP]
  • Java虚拟机(Java Virtual Machine,JVM)
  • STM32 : GPIO_TypeDef
  • 【深度学习】深度(Deep Learning)学习基础
  • 历代iPhone运行内存大小和电池容量信息
  • vulhub-deathnote靶场实战
  • 锂电池SOC估计 | Matlab基于CNN神经网络的锂电池锂电池SOC估计,附锂电池最新文章汇集
  • sys.dm_exec_connections:查询与 SQL Server 实例建立的连接有关的信息以及每个连接的详细信息(客户端ip)
  • buuctf漏洞复现之log4j
  • 盛最多水的容器