首页>>人工智能->详解K均值聚类算法(K

详解K均值聚类算法(K

时间:2023-11-29 本站 点击:1

完整代码:https://github.com/Honour-Van/RaspberryPILabPKU/blob/master/Lab%203/kmeans.py

实例说明

假定某共用办公室有两个人使用,他们使用办公室的时候都会用空调遥控器设置房间的温度。但空调遥控器显示面板坏掉了,只能通过加减温度的方式盲调整。

但已知他们习惯的温度不同。办公室的温度计有记录功能,每天记录12 小时的温度,每15 分钟记录一次,根据一个月所记录的数据,通过聚类算法,算出这两个人的喜好温度,并估算他们各使用办公室多少时间。

可以认为这两个人使用办公室的时候室内温度是不同方差和不同均值的正态分布随机数,生成模拟数据,并用matplotlib 画出数据的直方图。 使用K 均值聚类算法将模拟数据分成两个簇,解答前面的问题。 使用温度传感器读入温度,根据温度值,判断是谁在使用办公室。

聚类算法概述

聚类算法的步骤可以抽象地分为如下几步:

获取原始数据

随机选取K个位置作为初始的聚类中心

计算每个样本与各个聚类中心之间的距离,把每个样本分配给距离它最近的聚类中心,聚类中心以及分配给它们的样本就代表一个聚类

每分配完成后,聚类的聚类中心会根据聚类中现有的样本被重新计算,获得新的聚类中心

这个过程将不断重复直到满足某个终止条件。

终止条件可以是没有(或最小数目)样本被重新分配给不同的聚类,或者是聚类中心不再发生变化。

接下来我们分步骤利用Python语言将这几步实例化。

聚类算法实例分析

(a)数据生成

这个任务首先需要我们人为构造有两条正态曲线的数据集构成的全数据集。随后我们再利用聚类算法将其分开。

作数据集之前首先导入必要的模块:

import numpy as np # numpy的ndarray数据类型极有利于数据处理;import matplotlib.pyplot as plt # matplotlib用于作图;import scipy.stats as stats # scipy辅助numpy进行统计分析。在这个实验中用于对直方图数据进行正态拟合。

以下用于生成数据集:

half_total_times = 360 # 数据规模# 将两条正态曲线的数据合并为总的数据集。log = np.append(np.random.normal(33, 2, half_total_times),                np.random.normal(27, 2, half_total_times))# 也可以如下实现:# log = np.random.normal(33, 2, half_total_times)# log = np.append(log, np.random.normal(27, 2, half_total_times))# 以下绘图plt.hist(log, 80, histtype='bar', facecolor='yellowgreen', alpha=0.75)plt.ylabel('Frequentness')plt.xlabel('Temperature')plt.xticks(np.arange(20, 42, 1))plt.title("Raw Temperature Stats.")plt.show()

作出数据集图样如下:

(b)确定聚类中心

首先将打乱的的数据分成两类,将它们的均值确定为两个聚类中心。 要说明的是在这个一维问题当中,聚类中心就是一个float型的数字。聚类过程当中,这个数据通过均值反复更新,直至退出。

np.random.shuffle(log)t1, t2 = log[:half_total_times], log[half_total_times:]k1, k2 = np.mean(t1), np.mean(t2)

(c)初步聚类

利用先前确定的聚类中心对两类各自进行是否偏离中心的判断,完成初步聚类。

i, j = 0, 0while i < len(t1):    tmp = t1[i] # 取出当前值,为判断作准备    if abs(tmp-k1) > abs(tmp-k2): # 如果偏离中心,换到另一类        t2 = np.append(t2, tmp) # 注意增删要用原来ndarray名去承接删除后的引用        t1 = np.delete(t1, i)        # print(tmp, "to 2")        continue # 防止跳过    i = i + 1while j < len(t2):    tmp = t2[j]    if abs(tmp-k1) < abs(tmp-k2):        t1 = np.append(t1, tmp)        t2 = np.delete(t2, j)        # print(tmp, "to 1")        continue    j = j + 1

(d)重新确定聚类中心

在先前聚类的基础上再次有目标地确定聚类中心。

k1 = np.mean(t1)k2 = np.mean(t2)print(k1, k2)

(e)(f)循环操作直至算法结束

重复上述操作直至两类之间不发生任何交换。通过循环将(c)(d)的代码重新实现如下。若发生交换则继续循环。

while True:    flag = True # 默认不发生交换,已经完成标记为True    i, j = 0, 0    while i < len(t1):        tmp = t1[i]        if abs(tmp-k1) > abs(tmp-k2):            t2 = np.append(t2, tmp)            t1 = np.delete(t1, i)            flag = False  # 发生一次交换那么循环就会继续进行            print(tmp, "has been moved to 2")        i = i + 1    while j < len(t2):        tmp = t2[j]        if abs(tmp-k1) < abs(tmp-k2):            t1 = np.append(t1, tmp)            t2 = np.delete(t2, j)            flag = False            print(tmp, "has been moved to 1")        j = j + 1    if flag:        break    k1 = np.mean(t1)    k2 = np.mean(t2)

聚类结果如下:

plt.hist(t1, 80, histtype='bar', density=1, facecolor='blue', alpha=0.75)plt.hist(t2, 80, histtype='bar', density=1, facecolor='green', alpha=0.75)plt.ylabel('Proportion')plt.xlabel('Temperature')plt.xticks(np.arange(20, 42, 1))plt.title("2-Means Clustering")plt.show()

为了更加清楚地表现这个聚类的结果,我们可以利用scipy的stats组件进行正态拟合,重新作图:

# 接受绘图的返回值n1, bins1, patches1 = plt.hist(    t1, 80, histtype='bar', density=1, facecolor='blue', alpha=0.75)  n2, bins2, patches2 = plt.hist(    t2, 80, histtype='bar', density=1, facecolor='green', alpha=0.75)# 利用返回的横坐标与重新拟合y1 = stats.norm.pdf(bins1, np.mean(t1), np.std(t1))y2 = stats.norm.pdf(bins2, np.mean(t2), np.std(t2))plt.plot(bins1, y1, 'r--')plt.plot(bins2, y2, 'r--')plt.ylabel('Proportion')plt.xlabel('Temperature')plt.xticks(np.arange(20, 42, 1))plt.title("2-Means Clustering")plt.show()

如下:

注意这幅图和上面没有带拟合的是次实验的结果。

输出的值为:

final k-means: 27.00089944939223 33.127071306372585 # htt = 720

思考:K均值聚类算法的一个缺陷

我们可以看到,这个聚类中心距离最初设定的27和33有一定的距离。

当数据量较小时,这个差距是由数据集本身的特性决定的。由于数据集过小,随机性使得平均值并不能较完美地趋近中心值。事实上,选定half_total_times为一个360时,每一次的结果都会有较大的差异。

于是我们猜测当数据量较大时,这个值将会排除随机因素,趋于一个稳定的值。依次将数据量扩大,作如下的试验:

数据顺序经过调整,将高温区和低温区对齐。实际聚类结果中,33.*的数据可能在后

# htt : half_total_timesfinal k-means: 33.12662153907564 26.9236373559599 # htt = 5000final k-means: 33.12400746682427 26.867736591964416 # htt = 20000final k-means: 33.13533873227883 26.89201367317691 # htt = 50000final k-means: 33.13308044620618 26.87871264214395 # htt = 100000final k-means: 33.113832074382046 26.873664094790183 # htt = 150000final k-means: 33.104500152812 26.87391229842333 # htt = 200000final k-means: 33.11234652005665 26.885121276955825 # htt = 250000final k-means: 33.11720891682733 26.876981068264236 # htt = 300000final k-means: 33.112480044788235 26.89036390407023 # htt = 350000final k-means: 33.121317671847606 26.88380554470867 # htt = 400000final k-means: 33.120665211354314 26.886840666914292# htt = 450000final k-means: 33.118645760649365 26.87833700669375 # htt = 500000final k-means: 33.11972276310472 26.883882190093534 # htt = 550000final k-means: 33.11476481580636 26.882009336811468 # htt = 600000final k-means: 33.11899334301187 26.885940661245822 # htt = 650000final k-means: 33.122877982248134 26.884929683501547 # htt = 700000     final k-means: 33.11483313992061 26.882989502619292 # htt = 750000      final k-means: 33.112846759715936 26.880544838547046 # htt = 800000

我们不难看到,k均值也并没有收敛到正态的中心设定值。高温集合的均值偏大,低温集合的均值偏小。

从原始数据其实是不难理解的。两个正态曲线有一部分交错,由于聚类算法过程只按距离分配,低温的正态的波浪线部分分配给高温类,高温的正态的斜线部分分配给低温类,如图 所以两类当中各自并不是一条完整的正态曲线的数据集,当数据量大时这一点是容易看出的,如图直方图和红色的正态曲线有较大的差异。 聚类算法使得交错的部分不能有效地归入实际上的两类,而是数学上的两类。从而导致了最终的系统性的稳定偏离,收敛到33.112(3)和26.882(4),而不是33和27。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/AI/1108.html