文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python实现聚类K-means算法

2023-09-20 13:24

关注

本文内容、数据参考周志华《机器学习》,代码部分为个人实现,如有错误还请指出。
K-means(K均值)算法是最简单的一种聚类算法,它期望最小化平方误差
E = ∑ i = 1 k ∑ x ∈ C i ∣ ∣ x − μi ∣ ∣22 E=\sum\limits_{i=1}^k\sum\limits_{x\in C_i}||\pmb x-\pmb\mu_i||_2^2 E=i=1kxCixxxμμμi22
其中 μi = 1 ∣ C i ∣ ∑ x ∈ C i x \pmb\mu_i=\frac{1}{|C_i|}\sum_{x \in C_i}\pmb x μμμi=Ci1xCixxx是簇(cluster) Ci C_i Ci的均值向量, ∣ ∣ x − μi ∣ ∣2 ||\pmb x-\pmb\mu_i||_2 xxxμμμi2即一般意义下向量的模长。
K-means算法的一般步骤如下:

输入:样本集 D = { x1 , x2 , . . . , xm } ; D=\{\pmb x_1,\pmb x_2,...,\pmb x_m\}; D={xxx1,xxx2,...,xxxm};聚类簇数 k . k. k.
过程
   1 : 从 D 中 随 机 选 择 k 个 样 本 作 为 初 始 均 值 向 量 { μ1 , μ2 , . . . , μk } \ \ 1:从D中随机选择k个样本作为初始均值向量\{\pmb \mu_1, \pmb \mu_2,...,\pmb \mu_k\}   1:Dk{μμμ1,μμμ2,...,μμμk}
   2 : repeat \ \ 2:\texttt{repeat}   2:repeat
   3 : 令 Ci = ∅ ( 1 ≤ i ≤ k ) \ \ 3:\hspace{0.5cm}令C_i=\varnothing(1\le i \le k)   3:Ci=(1ik)
   4 : for j = 1 , 2 , . . . , m do \ \ 4:\hspace{0.5cm}\texttt{for}\hspace{0.2cm} j=1,2,...,m \hspace{0.2cm}\texttt{do}   4:forj=1,2,...,mdo
   5 : 计 算 样 本 xj 与 各 均 值 向 量 μi ( 1 ≤ i ≤ k ) 的 距 离 : d j i = ∣ ∣ x − μi ∣ ∣2 ; \ \ 5:\hspace{1.0cm}计算样本\pmb x_j与各均值向量\pmb \mu_i(1\le i \le k)的距离:d_{ji}=||\pmb x-\pmb\mu_i||_2;   5:xxxjμμμi(1ik)dji=xxxμμμi2;
   6 : 根 据 距 离 最 近 的 均 值 向 量 确 定 xj 的 簇 标 记 : λj = argmin i ∈ { 1 , 2 , . . . , k } d j i ; \ \ 6:\hspace{1.0cm}根据距离最近的均值向量确定x_j的簇标记:\lambda_j=\texttt{argmin}_{i\in \{1,2,...,k\}}d_{ji};   6:xj:λj=argmini{1,2,...,k}dji;
   7 : 将 样 本 xj 划 入 相 应 的 簇 : C λ j = C λ j ∪ { xj } ; \ \ 7:\hspace{1.0cm}将样本\pmb x_j划入相应的簇:C_{\lambda_j}=C_{\lambda_j}\cup\{\pmb x_j\};   7:xxxj:Cλj=Cλj{xxxj};
   8 : end for \ \ 8:\hspace{0.5cm}\texttt{end for}   8:end for
   9 : for i = 1 , 2 , . . . , k do \ \ 9:\hspace{0.5cm}\texttt{for}\hspace{0.2cm}i=1,2,...,k\hspace{0.2cm}\texttt{do}   9:fori=1,2,...,kdo
10 : 计 算 新 均 值 向 量 : μi ′ = 1 ∣ C i ∣ ∑ x ∈ C i x ; 10:\hspace{1.0cm}计算新均值向量:\pmb \mu_i^{'}=\frac{1}{|C_i|}\sum_{x \in C_i}\pmb x; 10::μμμi=Ci1xCixxx;
11 : if μi ′ ≠ μi then 11:\hspace{1.0cm}\texttt{if} \hspace{0.2cm}\pmb \mu_i^{'}\neq\pmb \mu_i\hspace{0.2cm}\texttt{then} 11:ifμμμi=μμμithen
12 : 将 当 前 均 值 向 量 μi 更 新 为 μi ′ 12:\hspace{1.5cm}将当前均值向量\pmb \mu_i更新为\pmb \mu_i^{'} 12:μμμiμμμi
13 : else 13:\hspace{1.0cm}\texttt{else} 13:else
14 : 保 持 当 前 均 值 向 量 不 变 14:\hspace{1.5cm}保持当前均值向量不变 14:
15 : end if 15:\hspace{1.0cm}\texttt{end if} 15:end if
16 : end for 16:\hspace{0.5cm}\texttt{end for} 16:end for
17 : until 当 前 均 值 向 量 均 未 更 新 17:\texttt{until}\hspace{0.2cm}当前均值向量均未更新 17:until
输出:簇划分 C = { C1 , C2 , . . . , Ck } \mathcal{C}=\{C_1,C_2,...,C_k\} C={C1,C2,...,Ck}

:为避免运行时间过长,通常设置一个最大运行轮数或最小调整幅度阈值,若到达最大轮数或调整幅度小于阈值,则停止运行。

下面我们用python来实现一下K-means算法:我们先尝试手动实现这个算法,再用sklearn库中的KMeans类来实现。数据我们采用《机器学习》的西瓜数据(P202表9.1):

# 下面的内容保存在 melons.txt 中# 第一列为西瓜的密度;第二列为西瓜的含糖率。我们要把这30个西瓜分为3类0.697 0.4600.774 0.3760.634 0.2640.608 0.3180.556 0.2150.403 0.2370.481 0.1490.437 0.2110.666 0.0910.243 0.2670.245 0.0570.343 0.0990.639 0.1610.657 0.1980.360 0.3700.593 0.0420.719 0.1030.359 0.1880.339 0.2410.282 0.2570.748 0.2320.714 0.3460.483 0.3120.478 0.4370.525 0.3690.751 0.4890.532 0.4720.473 0.3760.725 0.4450.446 0.459

手动实现

我们用到的库有matplotlibnumpy,如果没有需要先用pip安装一下。

import randomimport numpy as npimport matplotlib.pyplot as plt

下面定义一些数据:

k = 3 # 要分的簇数rnd = 0 # 轮次,用于控制迭代次数(见上文)ROUND_LIMIT = 100 # 轮次的上限THRESHOLD = 1e-10 # 单轮改变距离的阈值,若改变幅度小于该阈值,算法终止melons = [] # 西瓜的列表clusters = [] # 簇的列表,clusters[i]表示第i簇包含的西瓜

melons.txt读取数据,保存在列表中:

f = open('melons.txt', 'r')for line in f:# 把字符串转化为numpy中的float64类型    melons.append(np.array(line.split(' '), dtype = np.string_).astype(np.float64))

m m m个数据中随机挑选出 k k k个,对应上面算法的第 1 1 1行:

# random的sample函数从列表中随机挑选出k个样本(不重复)。我们在这里把这些样本作为均值向量mean_vectors = random.sample(melons, k)

下面是算法的主要部分。

# 这个while对应上面算法的2-17行while True:    rnd += 1 # 轮次增加    change = 0 # 把改变幅度重置为0    # 清空对簇的划分,对应上面算法的第3行    clusters = []    for i in range(k):        clusters.append([])    # 这个for对应上面算法的4-8行    for melon in melons:    '''    argmin 函数找出容器中最小的下标,在这里这个目标容器是    list(map(lambda vec: np.linalg.norm(melon - vec, ord = 2), mean_vectors)),    它表示melon与mean_vectors中所有向量的距离列表。    (numpy.linalg.norm计算向量的范数,ord = 2即欧几里得范数,或模长)    '''        c = np.argmin(            list(map( lambda vec: np.linalg.norm(melon - vec, ord = 2), mean_vectors))        )        clusters[c].append(melon)# 这个for对应上面算法的9-16行    for i in range(k):    # 求每个簇的新均值向量        new_vector = np.zeros((1,2))        for melon in clusters[i]:            new_vector += melon        new_vector /= len(clusters[i])                # 累加改变幅度并更新均值向量        change += np.linalg.norm(mean_vectors[i] - new_vector, ord = 2)        mean_vectors[i] = new_vector# 若超过设定的轮次或者变化幅度<预先设定的阈值,结束算法    if rnd > ROUND_LIMIT or change < THRESHOLD:        breakprint('最终迭代%d轮'%rnd)

最后我们绘图来观察一下划分的结果:

colors = ['red', 'green', 'blue']# 每个簇换一下颜色,同时迭代簇和颜色两个列表for i, col in zip(range(k), colors):    for melon in clusters[i]:    # 绘制散点图        plt.scatter(melon[0], melon[1], color = col)plt.show()

划分结果(由于最开始的 k k k个均值向量随机选取,每次划分的结果可能会不同):
在这里插入图片描述
完整代码:

import randomimport numpy as npimport matplotlib.pyplot as pltk = 3rnd = 0ROUND_LIMIT = 10THRESHOLD = 1e-10melons = []clusters = []f = open('melons.txt', 'r')for line in f:    melons.append(np.array(line.split(' '), dtype = np.string_).astype(np.float64))mean_vectors = random.sample(melons, k)while True:    rnd += 1    change = 0    clusters = []    for i in range(k):        clusters.append([])    for melon in melons:        c = np.argmin(            list(map( lambda vec: np.linalg.norm(melon - vec, ord = 2), mean_vectors))        )        clusters[c].append(melon)    for i in range(k):        new_vector = np.zeros((1,2))        for melon in clusters[i]:            new_vector += melon        new_vector /= len(clusters[i])        change += np.linalg.norm(mean_vectors[i] - new_vector, ord = 2)        mean_vectors[i] = new_vector    if rnd > ROUND_LIMIT or change < THRESHOLD:        breakprint('最终迭代%d轮'%rnd)colors = ['red', 'green', 'blue']for i, col in zip(range(k), colors):    for melon in clusters[i]:        plt.scatter(melon[0], melon[1], color = col)plt.show()

sklearn库中的KMeans

这种经典算法显然不需要我们反复地造轮子,被广泛使用的python机器学习库sklearn已经提供了该算法的实现。sklearn的官方文档中给了我们一个示例:

>>> from sklearn.cluster import KMeans>>> import numpy as np>>> X = np.array([[1, 2], [1, 4], [1, 0],...               [10, 2], [10, 4], [10, 0]])>>> kmeans = KMeans(n_clusters=2, random_state=0).fit(X)>>> kmeans.labels_array([1, 1, 1, 0, 0, 0], dtype=int32)>>> kmeans.predict([[0, 0], [12, 3]])array([1, 0], dtype=int32)>>> kmeans.cluster_centers_array([[10.,  2.],       [ 1.,  2.]])

可以看出,X即要聚类的数据(1,2),(1,4),(1,0)等。
KMeans类的初始化参数n_clusters即簇数 k k k;
random_state是用于初始化选取 k k k个向量的随机数种子;
kmeans.labels_即每个点所属的簇;
kmeans.predict方法预测新的数据属于哪个簇;
kmeans.cluster_centers_返回每个簇的中心。
我们就改造一下这个简单的示例,完成对上面西瓜的聚类。

import numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeansX = []f = open('melons.txt', 'r')for line in f:    X.append(np.array(line.split(' '), dtype = np.string_).astype(np.float64))kmeans = KMeans(n_clusters = 3, random_state = 0).fit(X)colors = ['red', 'green', 'blue']for i, cluster in enumerate(kmeans.labels_):    plt.scatter(X[i][0], X[i][1], color = colors[cluster])plt.show()

运行结果如下,可以看到和我们手写的聚类结果基本一致。
在这里插入图片描述

来源地址:https://blog.csdn.net/wyn1564464568/article/details/125782286

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯